Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

37
sound/mips/Kconfig Normal file
View file

@ -0,0 +1,37 @@
# ALSA MIPS drivers
menuconfig SND_MIPS
bool "MIPS sound devices"
depends on MIPS
default y
help
Support for sound devices of MIPS architectures.
if SND_MIPS
config SND_SGI_O2
tristate "SGI O2 Audio"
depends on SGI_IP32
help
Sound support for the SGI O2 Workstation.
config SND_SGI_HAL2
tristate "SGI HAL2 Audio"
depends on SGI_HAS_HAL2
help
Sound support for the SGI Indy and Indigo2 Workstation.
config SND_AU1X00
tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
depends on MIPS_ALCHEMY
select SND_PCM
select SND_AC97_CODEC
help
ALSA Sound driver for the Au1x00's AC97 port.
Newer drivers for ASoC are available, please do not use
this driver as it will be removed in the future.
endif # SND_MIPS

12
sound/mips/Makefile Normal file
View file

@ -0,0 +1,12 @@
#
# Makefile for ALSA
#
snd-au1x00-objs := au1x00.o
snd-sgi-o2-objs := sgio2audio.o ad1843.o
snd-sgi-hal2-objs := hal2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o

561
sound/mips/ad1843.c Normal file
View file

@ -0,0 +1,561 @@
/*
* AD1843 low level driver
*
* Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
* Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
*
* 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/init.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ad1843.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.
*/
struct ad1843_bitfield {
char reg;
char lo_bit;
char nbits;
};
static const struct ad1843_bitfield
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_RD2M = { 3, 0, 5 }, /* Right DAC 2 Mix Gain/Atten */
ad1843_RD2MM = { 3, 7, 1 }, /* Right DAC 2 Mix Mute */
ad1843_LD2M = { 3, 8, 5 }, /* Left DAC 2 Mix Gain/Atten */
ad1843_LD2MM = { 3, 15, 1 }, /* Left DAC 2 Mix Mute */
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 DAC2 Digital Mute */
ad1843_LDA2AM = { 12, 15, 1 }, /* Left DAC2 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_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */
ad1843_C2C = { 20, 0, 16 }, /* Clock 2 Sample Rate Select */
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_DAMIX = { 25, 14, 1 }, /* DAC Digital Mix Enable */
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_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_DDMEN = { 27, 12, 1 }, /* DAC2 to DAC1 Mix 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_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.
*/
struct ad1843_gain {
int negative; /* nonzero if gain is negative. */
const struct ad1843_bitfield *lfield;
const struct ad1843_bitfield *rfield;
const struct ad1843_bitfield *lmute;
const struct ad1843_bitfield *rmute;
};
static const struct ad1843_gain ad1843_gain_RECLEV = {
.negative = 0,
.lfield = &ad1843_LIG,
.rfield = &ad1843_RIG
};
static const struct ad1843_gain ad1843_gain_LINE = {
.negative = 1,
.lfield = &ad1843_LX1M,
.rfield = &ad1843_RX1M,
.lmute = &ad1843_LX1MM,
.rmute = &ad1843_RX1MM
};
static const struct ad1843_gain ad1843_gain_LINE_2 = {
.negative = 1,
.lfield = &ad1843_LDA2G,
.rfield = &ad1843_RDA2G,
.lmute = &ad1843_LDA2GM,
.rmute = &ad1843_RDA2GM
};
static const struct ad1843_gain ad1843_gain_MIC = {
.negative = 1,
.lfield = &ad1843_LMCM,
.rfield = &ad1843_RMCM,
.lmute = &ad1843_LMCMM,
.rmute = &ad1843_RMCMM
};
static const struct ad1843_gain ad1843_gain_PCM_0 = {
.negative = 1,
.lfield = &ad1843_LDA1G,
.rfield = &ad1843_RDA1G,
.lmute = &ad1843_LDA1GM,
.rmute = &ad1843_RDA1GM
};
static const struct ad1843_gain ad1843_gain_PCM_1 = {
.negative = 1,
.lfield = &ad1843_LD2M,
.rfield = &ad1843_RD2M,
.lmute = &ad1843_LD2MM,
.rmute = &ad1843_RD2MM
};
static const struct ad1843_gain *ad1843_gain[AD1843_GAIN_SIZE] =
{
&ad1843_gain_RECLEV,
&ad1843_gain_LINE,
&ad1843_gain_LINE_2,
&ad1843_gain_MIC,
&ad1843_gain_PCM_0,
&ad1843_gain_PCM_1,
};
/* read the current value of an AD1843 bitfield. */
static int ad1843_read_bits(struct snd_ad1843 *ad1843,
const struct ad1843_bitfield *field)
{
int w;
w = ad1843->read(ad1843->chip, field->reg);
return w >> field->lo_bit & ((1 << field->nbits) - 1);
}
/*
* write a new value to an AD1843 bitfield and return the old value.
*/
static int ad1843_write_bits(struct snd_ad1843 *ad1843,
const struct ad1843_bitfield *field,
int newval)
{
int w, mask, oldval, newbits;
w = ad1843->read(ad1843->chip, field->reg);
mask = ((1 << field->nbits) - 1) << field->lo_bit;
oldval = (w & mask) >> field->lo_bit;
newbits = (newval << field->lo_bit) & mask;
w = (w & ~mask) | newbits;
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(struct snd_ad1843 *ad1843, int argcount, ...)
{
va_list ap;
const struct ad1843_bitfield *fp;
int w = 0, mask, *value, reg = -1;
va_start(ap, argcount);
while (--argcount >= 0) {
fp = va_arg(ap, const struct ad1843_bitfield *);
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(struct snd_ad1843 *ad1843, int argcount, ...)
{
va_list ap;
int reg;
const struct ad1843_bitfield *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 struct ad1843_bitfield *);
value = va_arg(ap, int);
if (reg == -1)
reg = fp->reg;
else
WARN_ON(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;
ad1843->write(ad1843->chip, reg, w);
}
int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id)
{
const struct ad1843_gain *gp = ad1843_gain[id];
int ret;
ret = (1 << gp->lfield->nbits);
if (!gp->lmute)
ret -= 1;
return ret;
}
/*
* ad1843_get_gain reads the specified register and extracts the gain value
* using the supplied gain type.
*/
int ad1843_get_gain(struct snd_ad1843 *ad1843, int id)
{
int lg, rg, lm, rm;
const struct ad1843_gain *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;
}
if (gp->lmute) {
ad1843_read_multi(ad1843, 2, gp->lmute, &lm, gp->rmute, &rm);
if (lm)
lg = 0;
if (rm)
rg = 0;
}
return lg << 0 | rg << 8;
}
/*
* Set an audio channel's gain.
*
* Returns the new gain, which may be lower than the old gain.
*/
int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval)
{
const struct ad1843_gain *gp = ad1843_gain[id];
unsigned short mask = (1 << gp->lfield->nbits) - 1;
int lg = (newval >> 0) & mask;
int rg = (newval >> 8) & mask;
int lm = (lg == 0) ? 1 : 0;
int rm = (rg == 0) ? 1 : 0;
if (gp->negative) {
lg = mask - lg;
rg = mask - rg;
}
if (gp->lmute)
ad1843_write_multi(ad1843, 2, gp->lmute, lm, gp->rmute, rm);
ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg);
return ad1843_get_gain(ad1843, id);
}
/* Returns the current recording source */
int ad1843_get_recsrc(struct snd_ad1843 *ad1843)
{
int val = ad1843_read_bits(ad1843, &ad1843_LSS);
if (val < 0 || val > 2) {
val = 2;
ad1843_write_multi(ad1843, 2,
&ad1843_LSS, val, &ad1843_RSS, val);
}
return val;
}
/*
* Set recording source.
*
* Returns newsrc on success, -errno on failure.
*/
int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc)
{
if (newsrc < 0 || newsrc > 2)
return -EINVAL;
ad1843_write_multi(ad1843, 2, &ad1843_LSS, newsrc, &ad1843_RSS, newsrc);
return newsrc;
}
/* Setup ad1843 for D/A conversion. */
void ad1843_setup_dac(struct snd_ad1843 *ad1843,
unsigned int id,
unsigned int framerate,
snd_pcm_format_t fmt,
unsigned int channels)
{
int ad_fmt = 0, 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_LE:
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:
break;
}
switch (channels) {
case 2:
ad_mode = 0;
break;
case 1:
ad_mode = 1;
break;
default:
break;
}
if (id) {
ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
ad1843_write_multi(ad1843, 2,
&ad1843_DA2SM, ad_mode,
&ad1843_DA2F, ad_fmt);
} else {
ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
ad1843_write_multi(ad1843, 2,
&ad1843_DA1SM, ad_mode,
&ad1843_DA1F, ad_fmt);
}
}
void ad1843_shutdown_dac(struct snd_ad1843 *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(struct snd_ad1843 *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_LE: 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: 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(struct snd_ad1843 *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(struct snd_ad1843 *ad1843)
{
unsigned long later;
if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) {
printk(KERN_ERR "ad1843: AD1843 won't initialize\n");
return -EIO;
}
ad1843_write_bits(ad1843, &ad1843_SCF, 1);
/* 4. Put the conversion resources into standby. */
ad1843_write_bits(ad1843, &ad1843_PDNI, 0);
later = jiffies + msecs_to_jiffies(500);
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_timeout_interruptible(5);
}
/* 5. Power up the clock generators and enable clock output pins. */
ad1843_write_multi(ad1843, 3,
&ad1843_C1EN, 1,
&ad1843_C2EN, 1,
&ad1843_C3EN, 1);
/* 6. Configure conversion resources while they are in standby. */
/* DAC1/2 use clock 1/2 as source, ADC uses clock 3. Always. */
ad1843_write_multi(ad1843, 4,
&ad1843_DA1C, 1,
&ad1843_DA2C, 2,
&ad1843_ADLC, 3,
&ad1843_ADRC, 3);
/* 7. Enable conversion resources. */
ad1843_write_bits(ad1843, &ad1843_ADTLK, 1);
ad1843_write_multi(ad1843, 7,
&ad1843_ANAEN, 1,
&ad1843_AAMEN, 1,
&ad1843_DA1EN, 1,
&ad1843_DA2EN, 1,
&ad1843_DDMEN, 1,
&ad1843_ADLEN, 1,
&ad1843_ADREN, 1);
/* 8. Configure conversion resources while they are enabled. */
/* set gain to 0 for all channels */
ad1843_set_gain(ad1843, AD1843_GAIN_RECLEV, 0);
ad1843_set_gain(ad1843, AD1843_GAIN_LINE, 0);
ad1843_set_gain(ad1843, AD1843_GAIN_LINE_2, 0);
ad1843_set_gain(ad1843, AD1843_GAIN_MIC, 0);
ad1843_set_gain(ad1843, AD1843_GAIN_PCM_0, 0);
ad1843_set_gain(ad1843, AD1843_GAIN_PCM_1, 0);
/* Unmute all channels. */
/* DAC1 */
ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0);
/* DAC2 */
ad1843_write_multi(ad1843, 2, &ad1843_LDA2GM, 0, &ad1843_RDA2GM, 0);
/* Set default recording source to Line In and set
* mic gain to +20 dB.
*/
ad1843_set_recsrc(ad1843, 2);
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;
}

728
sound/mips/au1x00.c Normal file
View file

@ -0,0 +1,728 @@
/*
* BRIEF MODULE DESCRIPTION
* Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
*
* Copyright 2004 Cooper Street Innovations Inc.
* Author: Charles Eidsness <charles@cooper-street.com>
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.
*
* History:
*
* 2004-09-09 Charles Eidsness -- Original verion -- based on
* sa11xx-uda1341.c ALSA driver and the
* au1000.c OSS driver.
* 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6
*
*/
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/ac97_codec.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1000_dma.h>
MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
#define PLAYBACK 0
#define CAPTURE 1
#define AC97_SLOT_3 0x01
#define AC97_SLOT_4 0x02
#define AC97_SLOT_6 0x08
#define AC97_CMD_IRQ 31
#define READ 0
#define WRITE 1
#define READ_WAIT 2
#define RW_DONE 3
struct au1000_period
{
u32 start;
u32 relative_end; /*realtive to start of buffer*/
struct au1000_period * next;
};
/*Au1000 AC97 Port Control Reisters*/
struct au1000_ac97_reg {
u32 volatile config;
u32 volatile status;
u32 volatile data;
u32 volatile cmd;
u32 volatile cntrl;
};
struct audio_stream {
struct snd_pcm_substream *substream;
int dma;
spinlock_t dma_lock;
struct au1000_period * buffer;
unsigned int period_size;
unsigned int periods;
};
struct snd_au1000 {
struct snd_card *card;
struct au1000_ac97_reg volatile *ac97_ioport;
struct resource *ac97_res_port;
spinlock_t ac97_lock;
struct snd_ac97 *ac97;
struct snd_pcm *pcm;
struct audio_stream *stream[2]; /* playback & capture */
int dmaid[2]; /* tx(0)/rx(1) DMA ids */
};
/*--------------------------- Local Functions --------------------------------*/
static void
au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
{
u32 volatile ac97_config;
spin_lock(&au1000->ac97_lock);
ac97_config = au1000->ac97_ioport->config;
ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
au1000->ac97_ioport->config = ac97_config;
spin_unlock(&au1000->ac97_lock);
}
static void
au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
{
u32 volatile ac97_config;
spin_lock(&au1000->ac97_lock);
ac97_config = au1000->ac97_ioport->config;
ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
au1000->ac97_ioport->config = ac97_config;
spin_unlock(&au1000->ac97_lock);
}
static void
au1000_release_dma_link(struct audio_stream *stream)
{
struct au1000_period * pointer;
struct au1000_period * pointer_next;
stream->period_size = 0;
stream->periods = 0;
pointer = stream->buffer;
if (! pointer)
return;
do {
pointer_next = pointer->next;
kfree(pointer);
pointer = pointer_next;
} while (pointer != stream->buffer);
stream->buffer = NULL;
}
static int
au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
unsigned int periods)
{
struct snd_pcm_substream *substream = stream->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct au1000_period *pointer;
unsigned long dma_start;
int i;
dma_start = virt_to_phys(runtime->dma_area);
if (stream->period_size == period_bytes &&
stream->periods == periods)
return 0; /* not changed */
au1000_release_dma_link(stream);
stream->period_size = period_bytes;
stream->periods = periods;
stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
if (! stream->buffer)
return -ENOMEM;
pointer = stream->buffer;
for (i = 0; i < periods; i++) {
pointer->start = (u32)(dma_start + (i * period_bytes));
pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
if (i < periods - 1) {
pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
if (! pointer->next) {
au1000_release_dma_link(stream);
return -ENOMEM;
}
pointer = pointer->next;
}
}
pointer->next = stream->buffer;
return 0;
}
static void
au1000_dma_stop(struct audio_stream *stream)
{
if (snd_BUG_ON(!stream->buffer))
return;
disable_dma(stream->dma);
}
static void
au1000_dma_start(struct audio_stream *stream)
{
if (snd_BUG_ON(!stream->buffer))
return;
init_dma(stream->dma);
if (get_dma_active_buffer(stream->dma) == 0) {
clear_dma_done0(stream->dma);
set_dma_addr0(stream->dma, stream->buffer->start);
set_dma_count0(stream->dma, stream->period_size >> 1);
set_dma_addr1(stream->dma, stream->buffer->next->start);
set_dma_count1(stream->dma, stream->period_size >> 1);
} else {
clear_dma_done1(stream->dma);
set_dma_addr1(stream->dma, stream->buffer->start);
set_dma_count1(stream->dma, stream->period_size >> 1);
set_dma_addr0(stream->dma, stream->buffer->next->start);
set_dma_count0(stream->dma, stream->period_size >> 1);
}
enable_dma_buffers(stream->dma);
start_dma(stream->dma);
}
static irqreturn_t
au1000_dma_interrupt(int irq, void *dev_id)
{
struct audio_stream *stream = (struct audio_stream *) dev_id;
struct snd_pcm_substream *substream = stream->substream;
spin_lock(&stream->dma_lock);
switch (get_dma_buffer_done(stream->dma)) {
case DMA_D0:
stream->buffer = stream->buffer->next;
clear_dma_done0(stream->dma);
set_dma_addr0(stream->dma, stream->buffer->next->start);
set_dma_count0(stream->dma, stream->period_size >> 1);
enable_dma_buffer0(stream->dma);
break;
case DMA_D1:
stream->buffer = stream->buffer->next;
clear_dma_done1(stream->dma);
set_dma_addr1(stream->dma, stream->buffer->next->start);
set_dma_count1(stream->dma, stream->period_size >> 1);
enable_dma_buffer1(stream->dma);
break;
case (DMA_D0 | DMA_D1):
printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
au1000_dma_stop(stream);
au1000_dma_start(stream);
break;
case (~DMA_D0 & ~DMA_D1):
printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
}
spin_unlock(&stream->dma_lock);
snd_pcm_period_elapsed(substream);
return IRQ_HANDLED;
}
/*-------------------------- PCM Audio Streams -------------------------------*/
static unsigned int rates[] = {8000, 11025, 16000, 22050};
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static struct snd_pcm_hardware snd_au1000_hw =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED | \
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
.rate_min = 8000,
.rate_max = 22050,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 128*1024,
.period_bytes_min = 32,
.period_bytes_max = 16*1024,
.periods_min = 8,
.periods_max = 255,
.fifo_size = 16,
};
static int
snd_au1000_playback_open(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[PLAYBACK]->substream = substream;
au1000->stream[PLAYBACK]->buffer = NULL;
substream->private_data = au1000->stream[PLAYBACK];
substream->runtime->hw = snd_au1000_hw;
return (snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
}
static int
snd_au1000_capture_open(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[CAPTURE]->substream = substream;
au1000->stream[CAPTURE]->buffer = NULL;
substream->private_data = au1000->stream[CAPTURE];
substream->runtime->hw = snd_au1000_hw;
return (snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
}
static int
snd_au1000_playback_close(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[PLAYBACK]->substream = NULL;
return 0;
}
static int
snd_au1000_capture_close(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[CAPTURE]->substream = NULL;
return 0;
}
static int
snd_au1000_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct audio_stream *stream = substream->private_data;
int err;
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
return au1000_setup_dma_link(stream,
params_period_bytes(hw_params),
params_periods(hw_params));
}
static int
snd_au1000_hw_free(struct snd_pcm_substream *substream)
{
struct audio_stream *stream = substream->private_data;
au1000_release_dma_link(stream);
return snd_pcm_lib_free_pages(substream);
}
static int
snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->channels == 1)
au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
else
au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
return 0;
}
static int
snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->channels == 1)
au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
else
au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
return 0;
}
static int
snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct audio_stream *stream = substream->private_data;
int err = 0;
spin_lock(&stream->dma_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
au1000_dma_start(stream);
break;
case SNDRV_PCM_TRIGGER_STOP:
au1000_dma_stop(stream);
break;
default:
err = -EINVAL;
break;
}
spin_unlock(&stream->dma_lock);
return err;
}
static snd_pcm_uframes_t
snd_au1000_pointer(struct snd_pcm_substream *substream)
{
struct audio_stream *stream = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
long location;
spin_lock(&stream->dma_lock);
location = get_dma_residue(stream->dma);
spin_unlock(&stream->dma_lock);
location = stream->buffer->relative_end - location;
if (location == -1)
location = 0;
return bytes_to_frames(runtime,location);
}
static struct snd_pcm_ops snd_card_au1000_playback_ops = {
.open = snd_au1000_playback_open,
.close = snd_au1000_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_au1000_hw_params,
.hw_free = snd_au1000_hw_free,
.prepare = snd_au1000_playback_prepare,
.trigger = snd_au1000_trigger,
.pointer = snd_au1000_pointer,
};
static struct snd_pcm_ops snd_card_au1000_capture_ops = {
.open = snd_au1000_capture_open,
.close = snd_au1000_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_au1000_hw_params,
.hw_free = snd_au1000_hw_free,
.prepare = snd_au1000_capture_prepare,
.trigger = snd_au1000_trigger,
.pointer = snd_au1000_pointer,
};
static int
snd_au1000_pcm_new(struct snd_au1000 *au1000)
{
struct snd_pcm *pcm;
int err;
unsigned long flags;
if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
return err;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_au1000_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_au1000_capture_ops);
pcm->private_data = au1000;
pcm->info_flags = 0;
strcpy(pcm->name, "Au1000 AC97 PCM");
spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
flags = claim_dma_lock();
au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0],
"AC97 TX", au1000_dma_interrupt, 0,
au1000->stream[PLAYBACK]);
if (au1000->stream[PLAYBACK]->dma < 0) {
release_dma_lock(flags);
return -EBUSY;
}
au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1],
"AC97 RX", au1000_dma_interrupt, 0,
au1000->stream[CAPTURE]);
if (au1000->stream[CAPTURE]->dma < 0){
release_dma_lock(flags);
return -EBUSY;
}
/* enable DMA coherency in read/write DMA channels */
set_dma_mode(au1000->stream[PLAYBACK]->dma,
get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
set_dma_mode(au1000->stream[CAPTURE]->dma,
get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
release_dma_lock(flags);
au1000->pcm = pcm;
return 0;
}
/*-------------------------- AC97 CODEC Control ------------------------------*/
static unsigned short
snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_au1000 *au1000 = ac97->private_data;
u32 volatile cmd;
u16 volatile data;
int i;
spin_lock(&au1000->ac97_lock);
/* would rather use the interrupt than this polling but it works and I can't
get the interrupt driven case to work efficiently */
for (i = 0; i < 0x5000; i++)
if (!(au1000->ac97_ioport->status & AC97C_CP))
break;
if (i == 0x5000)
printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
cmd = (u32) reg & AC97C_INDEX_MASK;
cmd |= AC97C_READ;
au1000->ac97_ioport->cmd = cmd;
/* now wait for the data */
for (i = 0; i < 0x5000; i++)
if (!(au1000->ac97_ioport->status & AC97C_CP))
break;
if (i == 0x5000) {
printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
spin_unlock(&au1000->ac97_lock);
return 0;
}
data = au1000->ac97_ioport->cmd & 0xffff;
spin_unlock(&au1000->ac97_lock);
return data;
}
static void
snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct snd_au1000 *au1000 = ac97->private_data;
u32 cmd;
int i;
spin_lock(&au1000->ac97_lock);
/* would rather use the interrupt than this polling but it works and I can't
get the interrupt driven case to work efficiently */
for (i = 0; i < 0x5000; i++)
if (!(au1000->ac97_ioport->status & AC97C_CP))
break;
if (i == 0x5000)
printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
cmd = (u32) reg & AC97C_INDEX_MASK;
cmd &= ~AC97C_READ;
cmd |= ((u32) val << AC97C_WD_BIT);
au1000->ac97_ioport->cmd = cmd;
spin_unlock(&au1000->ac97_lock);
}
/*------------------------------ Setup / Destroy ----------------------------*/
static void snd_au1000_free(struct snd_card *card)
{
struct snd_au1000 *au1000 = card->private_data;
if (au1000->stream[PLAYBACK]) {
if (au1000->stream[PLAYBACK]->dma >= 0)
free_au1000_dma(au1000->stream[PLAYBACK]->dma);
kfree(au1000->stream[PLAYBACK]);
}
if (au1000->stream[CAPTURE]) {
if (au1000->stream[CAPTURE]->dma >= 0)
free_au1000_dma(au1000->stream[CAPTURE]->dma);
kfree(au1000->stream[CAPTURE]);
}
if (au1000->ac97_res_port) {
/* put internal AC97 block into reset */
if (au1000->ac97_ioport) {
au1000->ac97_ioport->cntrl = AC97C_RS;
iounmap(au1000->ac97_ioport);
au1000->ac97_ioport = NULL;
}
release_and_free_resource(au1000->ac97_res_port);
au1000->ac97_res_port = NULL;
}
}
static struct snd_ac97_bus_ops ops = {
.write = snd_au1000_ac97_write,
.read = snd_au1000_ac97_read,
};
static int au1000_ac97_probe(struct platform_device *pdev)
{
int err;
void __iomem *io;
struct resource *r;
struct snd_card *card;
struct snd_au1000 *au1000;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE,
sizeof(struct snd_au1000), &card);
if (err < 0)
return err;
au1000 = card->private_data;
au1000->card = card;
spin_lock_init(&au1000->ac97_lock);
/* from here on let ALSA call the special freeing function */
card->private_free = snd_au1000_free;
/* TX DMA ID */
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!r) {
err = -ENODEV;
snd_printk(KERN_INFO "no TX DMA platform resource!\n");
goto out;
}
au1000->dmaid[0] = r->start;
/* RX DMA ID */
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!r) {
err = -ENODEV;
snd_printk(KERN_INFO "no RX DMA platform resource!\n");
goto out;
}
au1000->dmaid[1] = r->start;
au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
GFP_KERNEL);
if (!au1000->stream[PLAYBACK])
goto out;
au1000->stream[PLAYBACK]->dma = -1;
au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
GFP_KERNEL);
if (!au1000->stream[CAPTURE])
goto out;
au1000->stream[CAPTURE]->dma = -1;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
goto out;
err = -EBUSY;
au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
pdev->name);
if (!au1000->ac97_res_port) {
snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
goto out;
}
io = ioremap(r->start, resource_size(r));
if (!io)
goto out;
au1000->ac97_ioport = (struct au1000_ac97_reg *)io;
/* configure pins for AC'97
TODO: move to board_setup.c */
au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
/* Initialise Au1000's AC'97 Control Block */
au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
udelay(10);
au1000->ac97_ioport->cntrl = AC97C_CE;
udelay(10);
/* Initialise External CODEC -- cold reset */
au1000->ac97_ioport->config = AC97C_RESET;
udelay(10);
au1000->ac97_ioport->config = 0x0;
mdelay(5);
/* Initialise AC97 middle-layer */
err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus);
if (err < 0)
goto out;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = au1000;
err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97);
if (err < 0)
goto out;
err = snd_au1000_pcm_new(au1000);
if (err < 0)
goto out;
strcpy(card->driver, "Au1000-AC97");
strcpy(card->shortname, "AMD Au1000-AC97");
sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
err = snd_card_register(card);
if (err < 0)
goto out;
printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
platform_set_drvdata(pdev, card);
return 0;
out:
snd_card_free(card);
return err;
}
static int au1000_ac97_remove(struct platform_device *pdev)
{
return snd_card_free(platform_get_drvdata(pdev));
}
struct platform_driver au1000_ac97c_driver = {
.driver = {
.name = "au1000-ac97c",
.owner = THIS_MODULE,
},
.probe = au1000_ac97_probe,
.remove = au1000_ac97_remove,
};
module_platform_driver(au1000_ac97c_driver);

936
sound/mips/hal2.c Normal file
View file

@ -0,0 +1,936 @@
/*
* Driver for A2 audio system used in SGI machines
* Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
*
* Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
* was based on code from Ulf Carlsson
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm-indirect.h>
#include <sound/initval.h>
#include "hal2.h"
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for SGI HAL2 soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for SGI HAL2 soundcard.");
MODULE_DESCRIPTION("ALSA driver for SGI HAL2 audio");
MODULE_AUTHOR("Thomas Bogendoerfer");
MODULE_LICENSE("GPL");
#define H2_BLOCK_SIZE 1024
#define H2_BUF_SIZE 16384
struct hal2_pbus {
struct hpc3_pbus_dmacregs *pbus;
int pbusnr;
unsigned int ctrl; /* Current state of pbus->pbdma_ctrl */
};
struct hal2_desc {
struct hpc_dma_desc desc;
u32 pad; /* padding */
};
struct hal2_codec {
struct snd_pcm_indirect pcm_indirect;
struct snd_pcm_substream *substream;
unsigned char *buffer;
dma_addr_t buffer_dma;
struct hal2_desc *desc;
dma_addr_t desc_dma;
int desc_count;
struct hal2_pbus pbus;
int voices; /* mono/stereo */
unsigned int sample_rate;
unsigned int master; /* Master frequency */
unsigned short mod; /* MOD value */
unsigned short inc; /* INC value */
};
#define H2_MIX_OUTPUT_ATT 0
#define H2_MIX_INPUT_GAIN 1
struct snd_hal2 {
struct snd_card *card;
struct hal2_ctl_regs *ctl_regs; /* HAL2 ctl registers */
struct hal2_aes_regs *aes_regs; /* HAL2 aes registers */
struct hal2_vol_regs *vol_regs; /* HAL2 vol registers */
struct hal2_syn_regs *syn_regs; /* HAL2 syn registers */
struct hal2_codec dac;
struct hal2_codec adc;
};
#define H2_INDIRECT_WAIT(regs) while (hal2_read(&regs->isr) & H2_ISR_TSTATUS);
#define H2_READ_ADDR(addr) (addr | (1<<7))
#define H2_WRITE_ADDR(addr) (addr)
static inline u32 hal2_read(u32 *reg)
{
return __raw_readl(reg);
}
static inline void hal2_write(u32 val, u32 *reg)
{
__raw_writel(val, reg);
}
static u32 hal2_i_read32(struct snd_hal2 *hal2, u16 addr)
{
u32 ret;
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(H2_READ_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
ret = hal2_read(&regs->idr0) & 0xffff;
hal2_write(H2_READ_ADDR(addr) | 0x1, &regs->iar);
H2_INDIRECT_WAIT(regs);
ret |= (hal2_read(&regs->idr0) & 0xffff) << 16;
return ret;
}
static void hal2_i_write16(struct snd_hal2 *hal2, u16 addr, u16 val)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(val, &regs->idr0);
hal2_write(0, &regs->idr1);
hal2_write(0, &regs->idr2);
hal2_write(0, &regs->idr3);
hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
}
static void hal2_i_write32(struct snd_hal2 *hal2, u16 addr, u32 val)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(val & 0xffff, &regs->idr0);
hal2_write(val >> 16, &regs->idr1);
hal2_write(0, &regs->idr2);
hal2_write(0, &regs->idr3);
hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
}
static void hal2_i_setbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(H2_READ_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
hal2_write((hal2_read(&regs->idr0) & 0xffff) | bit, &regs->idr0);
hal2_write(0, &regs->idr1);
hal2_write(0, &regs->idr2);
hal2_write(0, &regs->idr3);
hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
}
static void hal2_i_clearbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(H2_READ_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
hal2_write((hal2_read(&regs->idr0) & 0xffff) & ~bit, &regs->idr0);
hal2_write(0, &regs->idr1);
hal2_write(0, &regs->idr2);
hal2_write(0, &regs->idr3);
hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
H2_INDIRECT_WAIT(regs);
}
static int hal2_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;
switch ((int)kcontrol->private_value) {
case H2_MIX_OUTPUT_ATT:
uinfo->value.integer.max = 31;
break;
case H2_MIX_INPUT_GAIN:
uinfo->value.integer.max = 15;
break;
}
return 0;
}
static int hal2_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
u32 tmp;
int l, r;
switch ((int)kcontrol->private_value) {
case H2_MIX_OUTPUT_ATT:
tmp = hal2_i_read32(hal2, H2I_DAC_C2);
if (tmp & H2I_C2_MUTE) {
l = 0;
r = 0;
} else {
l = 31 - ((tmp >> H2I_C2_L_ATT_SHIFT) & 31);
r = 31 - ((tmp >> H2I_C2_R_ATT_SHIFT) & 31);
}
break;
case H2_MIX_INPUT_GAIN:
tmp = hal2_i_read32(hal2, H2I_ADC_C2);
l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
break;
}
ucontrol->value.integer.value[0] = l;
ucontrol->value.integer.value[1] = r;
return 0;
}
static int hal2_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
u32 old, new;
int l, r;
l = ucontrol->value.integer.value[0];
r = ucontrol->value.integer.value[1];
switch ((int)kcontrol->private_value) {
case H2_MIX_OUTPUT_ATT:
old = hal2_i_read32(hal2, H2I_DAC_C2);
new = old & ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
if (l | r) {
l = 31 - l;
r = 31 - r;
new |= (l << H2I_C2_L_ATT_SHIFT);
new |= (r << H2I_C2_R_ATT_SHIFT);
} else
new |= H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE;
hal2_i_write32(hal2, H2I_DAC_C2, new);
break;
case H2_MIX_INPUT_GAIN:
old = hal2_i_read32(hal2, H2I_ADC_C2);
new = old & ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
new |= (l << H2I_C2_L_GAIN_SHIFT);
new |= (r << H2I_C2_R_GAIN_SHIFT);
hal2_i_write32(hal2, H2I_ADC_C2, new);
break;
}
return old != new;
}
static struct snd_kcontrol_new hal2_ctrl_headphone = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = H2_MIX_OUTPUT_ATT,
.info = hal2_gain_info,
.get = hal2_gain_get,
.put = hal2_gain_put,
};
static struct snd_kcontrol_new hal2_ctrl_mic = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = H2_MIX_INPUT_GAIN,
.info = hal2_gain_info,
.get = hal2_gain_get,
.put = hal2_gain_put,
};
static int hal2_mixer_create(struct snd_hal2 *hal2)
{
int err;
/* mute DAC */
hal2_i_write32(hal2, H2I_DAC_C2,
H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
/* mute ADC */
hal2_i_write32(hal2, H2I_ADC_C2, 0);
err = snd_ctl_add(hal2->card,
snd_ctl_new1(&hal2_ctrl_headphone, hal2));
if (err < 0)
return err;
err = snd_ctl_add(hal2->card,
snd_ctl_new1(&hal2_ctrl_mic, hal2));
if (err < 0)
return err;
return 0;
}
static irqreturn_t hal2_interrupt(int irq, void *dev_id)
{
struct snd_hal2 *hal2 = dev_id;
irqreturn_t ret = IRQ_NONE;
/* decide what caused this interrupt */
if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
snd_pcm_period_elapsed(hal2->dac.substream);
ret = IRQ_HANDLED;
}
if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
snd_pcm_period_elapsed(hal2->adc.substream);
ret = IRQ_HANDLED;
}
return ret;
}
static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
{
unsigned short mod;
if (44100 % rate < 48000 % rate) {
mod = 4 * 44100 / rate;
codec->master = 44100;
} else {
mod = 4 * 48000 / rate;
codec->master = 48000;
}
codec->inc = 4;
codec->mod = mod;
rate = 4 * codec->master / mod;
return rate;
}
static void hal2_set_dac_rate(struct snd_hal2 *hal2)
{
unsigned int master = hal2->dac.master;
int inc = hal2->dac.inc;
int mod = hal2->dac.mod;
hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
hal2_i_write32(hal2, H2I_BRES1_C2,
((0xffff & (inc - mod - 1)) << 16) | inc);
}
static void hal2_set_adc_rate(struct snd_hal2 *hal2)
{
unsigned int master = hal2->adc.master;
int inc = hal2->adc.inc;
int mod = hal2->adc.mod;
hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0);
hal2_i_write32(hal2, H2I_BRES2_C2,
((0xffff & (inc - mod - 1)) << 16) | inc);
}
static void hal2_setup_dac(struct snd_hal2 *hal2)
{
unsigned int fifobeg, fifoend, highwater, sample_size;
struct hal2_pbus *pbus = &hal2->dac.pbus;
/* Now we set up some PBUS information. The PBUS needs information about
* what portion of the fifo it will use. If it's receiving or
* transmitting, and finally whether the stream is little endian or big
* endian. The information is written later, on the start call.
*/
sample_size = 2 * hal2->dac.voices;
/* Fifo should be set to hold exactly four samples. Highwater mark
* should be set to two samples. */
highwater = (sample_size * 2) >> 1; /* halfwords */
fifobeg = 0; /* playback is first */
fifoend = (sample_size * 4) >> 3; /* doublewords */
pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD |
(highwater << 8) | (fifobeg << 16) | (fifoend << 24);
/* We disable everything before we do anything at all */
pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
/* Setup the HAL2 for playback */
hal2_set_dac_rate(hal2);
/* Set endianess */
hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
/* Set DMA bus */
hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
/* We are using 1st Bresenham clock generator for playback */
hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
| (1 << H2I_C1_CLKID_SHIFT)
| (hal2->dac.voices << H2I_C1_DATAT_SHIFT));
}
static void hal2_setup_adc(struct snd_hal2 *hal2)
{
unsigned int fifobeg, fifoend, highwater, sample_size;
struct hal2_pbus *pbus = &hal2->adc.pbus;
sample_size = 2 * hal2->adc.voices;
highwater = (sample_size * 2) >> 1; /* halfwords */
fifobeg = (4 * 4) >> 3; /* record is second */
fifoend = (4 * 4 + sample_size * 4) >> 3; /* doublewords */
pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD |
(highwater << 8) | (fifobeg << 16) | (fifoend << 24);
pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
/* Setup the HAL2 for record */
hal2_set_adc_rate(hal2);
/* Set endianess */
hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
/* Set DMA bus */
hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
/* We are using 2nd Bresenham clock generator for record */
hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
| (2 << H2I_C1_CLKID_SHIFT)
| (hal2->adc.voices << H2I_C1_DATAT_SHIFT));
}
static void hal2_start_dac(struct snd_hal2 *hal2)
{
struct hal2_pbus *pbus = &hal2->dac.pbus;
pbus->pbus->pbdma_dptr = hal2->dac.desc_dma;
pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
/* enable DAC */
hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
}
static void hal2_start_adc(struct snd_hal2 *hal2)
{
struct hal2_pbus *pbus = &hal2->adc.pbus;
pbus->pbus->pbdma_dptr = hal2->adc.desc_dma;
pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
/* enable ADC */
hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
}
static inline void hal2_stop_dac(struct snd_hal2 *hal2)
{
hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
/* The HAL2 itself may remain enabled safely */
}
static inline void hal2_stop_adc(struct snd_hal2 *hal2)
{
hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
}
static int hal2_alloc_dmabuf(struct hal2_codec *codec)
{
struct hal2_desc *desc;
dma_addr_t desc_dma, buffer_dma;
int count = H2_BUF_SIZE / H2_BLOCK_SIZE;
int i;
codec->buffer = dma_alloc_noncoherent(NULL, H2_BUF_SIZE,
&buffer_dma, GFP_KERNEL);
if (!codec->buffer)
return -ENOMEM;
desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc),
&desc_dma, GFP_KERNEL);
if (!desc) {
dma_free_noncoherent(NULL, H2_BUF_SIZE,
codec->buffer, buffer_dma);
return -ENOMEM;
}
codec->buffer_dma = buffer_dma;
codec->desc_dma = desc_dma;
codec->desc = desc;
for (i = 0; i < count; i++) {
desc->desc.pbuf = buffer_dma + i * H2_BLOCK_SIZE;
desc->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
desc->desc.pnext = (i == count - 1) ?
desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc);
desc++;
}
dma_cache_sync(NULL, codec->desc, count * sizeof(struct hal2_desc),
DMA_TO_DEVICE);
codec->desc_count = count;
return 0;
}
static void hal2_free_dmabuf(struct hal2_codec *codec)
{
dma_free_noncoherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
codec->desc, codec->desc_dma);
dma_free_noncoherent(NULL, H2_BUF_SIZE, codec->buffer,
codec->buffer_dma);
}
static struct snd_pcm_hardware hal2_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
.formats = SNDRV_PCM_FMTBIT_S16_BE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 65536,
.period_bytes_min = 1024,
.period_bytes_max = 65536,
.periods_min = 2,
.periods_max = 1024,
};
static int hal2_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int err;
err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
if (err < 0)
return err;
return 0;
}
static int hal2_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static int hal2_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
int err;
runtime->hw = hal2_pcm_hw;
err = hal2_alloc_dmabuf(&hal2->dac);
if (err)
return err;
return 0;
}
static int hal2_playback_close(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
hal2_free_dmabuf(&hal2->dac);
return 0;
}
static int hal2_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct hal2_codec *dac = &hal2->dac;
dac->voices = runtime->channels;
dac->sample_rate = hal2_compute_rate(dac, runtime->rate);
memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect));
dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
dac->substream = substream;
hal2_setup_dac(hal2);
return 0;
}
static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
hal2->dac.pcm_indirect.hw_io = hal2->dac.buffer_dma;
hal2->dac.pcm_indirect.hw_data = 0;
substream->ops->ack(substream);
hal2_start_dac(hal2);
break;
case SNDRV_PCM_TRIGGER_STOP:
hal2_stop_dac(hal2);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t
hal2_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *dac = &hal2->dac;
return snd_pcm_indirect_playback_pointer(substream, &dac->pcm_indirect,
dac->pbus.pbus->pbdma_bptr);
}
static void hal2_playback_transfer(struct snd_pcm_substream *substream,
struct snd_pcm_indirect *rec, size_t bytes)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
unsigned char *buf = hal2->dac.buffer + rec->hw_data;
memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes);
dma_cache_sync(NULL, buf, bytes, DMA_TO_DEVICE);
}
static int hal2_playback_ack(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *dac = &hal2->dac;
dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
snd_pcm_indirect_playback_transfer(substream,
&dac->pcm_indirect,
hal2_playback_transfer);
return 0;
}
static int hal2_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *adc = &hal2->adc;
int err;
runtime->hw = hal2_pcm_hw;
err = hal2_alloc_dmabuf(adc);
if (err)
return err;
return 0;
}
static int hal2_capture_close(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
hal2_free_dmabuf(&hal2->adc);
return 0;
}
static int hal2_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct hal2_codec *adc = &hal2->adc;
adc->voices = runtime->channels;
adc->sample_rate = hal2_compute_rate(adc, runtime->rate);
memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect));
adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
adc->substream = substream;
hal2_setup_adc(hal2);
return 0;
}
static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
hal2->adc.pcm_indirect.hw_io = hal2->adc.buffer_dma;
hal2->adc.pcm_indirect.hw_data = 0;
printk(KERN_DEBUG "buffer_dma %x\n", hal2->adc.buffer_dma);
hal2_start_adc(hal2);
break;
case SNDRV_PCM_TRIGGER_STOP:
hal2_stop_adc(hal2);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t
hal2_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *adc = &hal2->adc;
return snd_pcm_indirect_capture_pointer(substream, &adc->pcm_indirect,
adc->pbus.pbus->pbdma_bptr);
}
static void hal2_capture_transfer(struct snd_pcm_substream *substream,
struct snd_pcm_indirect *rec, size_t bytes)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
unsigned char *buf = hal2->adc.buffer + rec->hw_data;
dma_cache_sync(NULL, buf, bytes, DMA_FROM_DEVICE);
memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
}
static int hal2_capture_ack(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *adc = &hal2->adc;
snd_pcm_indirect_capture_transfer(substream,
&adc->pcm_indirect,
hal2_capture_transfer);
return 0;
}
static struct snd_pcm_ops hal2_playback_ops = {
.open = hal2_playback_open,
.close = hal2_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = hal2_pcm_hw_params,
.hw_free = hal2_pcm_hw_free,
.prepare = hal2_playback_prepare,
.trigger = hal2_playback_trigger,
.pointer = hal2_playback_pointer,
.ack = hal2_playback_ack,
};
static struct snd_pcm_ops hal2_capture_ops = {
.open = hal2_capture_open,
.close = hal2_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = hal2_pcm_hw_params,
.hw_free = hal2_pcm_hw_free,
.prepare = hal2_capture_prepare,
.trigger = hal2_capture_trigger,
.pointer = hal2_capture_pointer,
.ack = hal2_capture_ack,
};
static int hal2_pcm_create(struct snd_hal2 *hal2)
{
struct snd_pcm *pcm;
int err;
/* create first pcm device with one outputs and one input */
err = snd_pcm_new(hal2->card, "SGI HAL2 Audio", 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = hal2;
strcpy(pcm->name, "SGI HAL2");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&hal2_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&hal2_capture_ops);
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
0, 1024 * 1024);
return 0;
}
static int hal2_dev_free(struct snd_device *device)
{
struct snd_hal2 *hal2 = device->device_data;
free_irq(SGI_HPCDMA_IRQ, hal2);
kfree(hal2);
return 0;
}
static struct snd_device_ops hal2_ops = {
.dev_free = hal2_dev_free,
};
static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3,
int index)
{
codec->pbus.pbusnr = index;
codec->pbus.pbus = &hpc3->pbdma[index];
}
static int hal2_detect(struct snd_hal2 *hal2)
{
unsigned short board, major, minor;
unsigned short rev;
/* reset HAL2 */
hal2_write(0, &hal2->ctl_regs->isr);
/* release reset */
hal2_write(H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N,
&hal2->ctl_regs->isr);
hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);
rev = hal2_read(&hal2->ctl_regs->rev);
if (rev & H2_REV_AUDIO_PRESENT)
return -ENODEV;
board = (rev & H2_REV_BOARD_M) >> 12;
major = (rev & H2_REV_MAJOR_CHIP_M) >> 4;
minor = (rev & H2_REV_MINOR_CHIP_M);
printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n",
board, major, minor);
return 0;
}
static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
{
struct snd_hal2 *hal2;
struct hpc3_regs *hpc3 = hpc3c0;
int err;
hal2 = kzalloc(sizeof(struct snd_hal2), GFP_KERNEL);
if (!hal2)
return -ENOMEM;
hal2->card = card;
if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
"SGI HAL2", hal2)) {
printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
kfree(hal2);
return -EAGAIN;
}
hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0];
hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1];
hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2];
hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3];
if (hal2_detect(hal2) < 0) {
kfree(hal2);
return -ENODEV;
}
hal2_init_codec(&hal2->dac, hpc3, 0);
hal2_init_codec(&hal2->adc, hpc3, 1);
/*
* All DMA channel interfaces in HAL2 are designed to operate with
* PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles
* in D5. HAL2 is a 16-bit device which can accept both big and little
* endian format. It assumes that even address bytes are on high
* portion of PBUS (15:8) and assumes that HPC3 is programmed to
* accept a live (unsynchronized) version of P_DREQ_N from HAL2.
*/
#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \
(2 << HPC3_DMACFG_D4R_SHIFT) | \
(2 << HPC3_DMACFG_D5R_SHIFT) | \
(0 << HPC3_DMACFG_D3W_SHIFT) | \
(2 << HPC3_DMACFG_D4W_SHIFT) | \
(2 << HPC3_DMACFG_D5W_SHIFT) | \
HPC3_DMACFG_DS16 | \
HPC3_DMACFG_EVENHI | \
HPC3_DMACFG_RTIME | \
(8 << HPC3_DMACFG_BURST_SHIFT) | \
HPC3_DMACFG_DRQLIVE)
/*
* Ignore what's mentioned in the specification and write value which
* works in The Real World (TM)
*/
hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hal2, &hal2_ops);
if (err < 0) {
free_irq(SGI_HPCDMA_IRQ, hal2);
kfree(hal2);
return err;
}
*rchip = hal2;
return 0;
}
static int hal2_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_hal2 *chip;
int err;
err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
err = hal2_create(card, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
err = hal2_pcm_create(chip);
if (err < 0) {
snd_card_free(card);
return err;
}
err = hal2_mixer_create(chip);
if (err < 0) {
snd_card_free(card);
return err;
}
strcpy(card->driver, "SGI HAL2 Audio");
strcpy(card->shortname, "SGI HAL2 Audio");
sprintf(card->longname, "%s irq %i",
card->shortname,
SGI_HPCDMA_IRQ);
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
platform_set_drvdata(pdev, card);
return 0;
}
static int hal2_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
snd_card_free(card);
return 0;
}
static struct platform_driver hal2_driver = {
.probe = hal2_probe,
.remove = hal2_remove,
.driver = {
.name = "sgihal2",
.owner = THIS_MODULE,
}
};
module_platform_driver(hal2_driver);

245
sound/mips/hal2.h Normal file
View file

@ -0,0 +1,245 @@
#ifndef __HAL2_H
#define __HAL2_H
/*
* Driver for HAL2 sound processors
* Copyright (c) 1999 Ulf Carlsson <ulfc@bun.falkenberg.se>
* Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
*/
#include <linux/types.h>
/* Indirect status register */
#define H2_ISR_TSTATUS 0x01 /* RO: transaction status 1=busy */
#define H2_ISR_USTATUS 0x02 /* RO: utime status bit 1=armed */
#define H2_ISR_QUAD_MODE 0x04 /* codec mode 0=indigo 1=quad */
#define H2_ISR_GLOBAL_RESET_N 0x08 /* chip global reset 0=reset */
#define H2_ISR_CODEC_RESET_N 0x10 /* codec/synth reset 0=reset */
/* Revision register */
#define H2_REV_AUDIO_PRESENT 0x8000 /* RO: audio present 0=present */
#define H2_REV_BOARD_M 0x7000 /* RO: bits 14:12, board revision */
#define H2_REV_MAJOR_CHIP_M 0x00F0 /* RO: bits 7:4, major chip revision */
#define H2_REV_MINOR_CHIP_M 0x000F /* RO: bits 3:0, minor chip revision */
/* Indirect address register */
/*
* Address of indirect internal register to be accessed. A write to this
* register initiates read or write access to the indirect registers in the
* HAL2. Note that there af four indirect data registers for write access to
* registers larger than 16 byte.
*/
#define H2_IAR_TYPE_M 0xF000 /* bits 15:12, type of functional */
/* block the register resides in */
/* 1=DMA Port */
/* 9=Global DMA Control */
/* 2=Bresenham */
/* 3=Unix Timer */
#define H2_IAR_NUM_M 0x0F00 /* bits 11:8 instance of the */
/* blockin which the indirect */
/* register resides */
/* If IAR_TYPE_M=DMA Port: */
/* 1=Synth In */
/* 2=AES In */
/* 3=AES Out */
/* 4=DAC Out */
/* 5=ADC Out */
/* 6=Synth Control */
/* If IAR_TYPE_M=Global DMA Control: */
/* 1=Control */
/* If IAR_TYPE_M=Bresenham: */
/* 1=Bresenham Clock Gen 1 */
/* 2=Bresenham Clock Gen 2 */
/* 3=Bresenham Clock Gen 3 */
/* If IAR_TYPE_M=Unix Timer: */
/* 1=Unix Timer */
#define H2_IAR_ACCESS_SELECT 0x0080 /* 1=read 0=write */
#define H2_IAR_PARAM 0x000C /* Parameter Select */
#define H2_IAR_RB_INDEX_M 0x0003 /* Read Back Index */
/* 00:word0 */
/* 01:word1 */
/* 10:word2 */
/* 11:word3 */
/*
* HAL2 internal addressing
*
* The HAL2 has "indirect registers" (idr) which are accessed by writing to the
* Indirect Data registers. Write the address to the Indirect Address register
* to transfer the data.
*
* We define the H2IR_* to the read address and H2IW_* to the write address and
* H2I_* to be fields in whatever register is referred to.
*
* When we write to indirect registers which are larger than one word (16 bit)
* we have to fill more than one indirect register before writing. When we read
* back however we have to read several times, each time with different Read
* Back Indexes (there are defs for doing this easily).
*/
/*
* Relay Control
*/
#define H2I_RELAY_C 0x9100
#define H2I_RELAY_C_STATE 0x01 /* state of RELAY pin signal */
/* DMA port enable */
#define H2I_DMA_PORT_EN 0x9104
#define H2I_DMA_PORT_EN_SY_IN 0x01 /* Synth_in DMA port */
#define H2I_DMA_PORT_EN_AESRX 0x02 /* AES receiver DMA port */
#define H2I_DMA_PORT_EN_AESTX 0x04 /* AES transmitter DMA port */
#define H2I_DMA_PORT_EN_CODECTX 0x08 /* CODEC transmit DMA port */
#define H2I_DMA_PORT_EN_CODECR 0x10 /* CODEC receive DMA port */
#define H2I_DMA_END 0x9108 /* global dma endian select */
#define H2I_DMA_END_SY_IN 0x01 /* Synth_in DMA port */
#define H2I_DMA_END_AESRX 0x02 /* AES receiver DMA port */
#define H2I_DMA_END_AESTX 0x04 /* AES transmitter DMA port */
#define H2I_DMA_END_CODECTX 0x08 /* CODEC transmit DMA port */
#define H2I_DMA_END_CODECR 0x10 /* CODEC receive DMA port */
/* 0=b_end 1=l_end */
#define H2I_DMA_DRV 0x910C /* global PBUS DMA enable */
#define H2I_SYNTH_C 0x1104 /* Synth DMA control */
#define H2I_AESRX_C 0x1204 /* AES RX dma control */
#define H2I_C_TS_EN 0x20 /* Timestamp enable */
#define H2I_C_TS_FRMT 0x40 /* Timestamp format */
#define H2I_C_NAUDIO 0x80 /* Sign extend */
/* AESRX CTL, 16 bit */
#define H2I_AESTX_C 0x1304 /* AES TX DMA control */
#define H2I_AESTX_C_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */
#define H2I_AESTX_C_CLKID_M 0x18
#define H2I_AESTX_C_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */
#define H2I_AESTX_C_DATAT_M 0x300
/* CODEC registers */
#define H2I_DAC_C1 0x1404 /* DAC DMA control, 16 bit */
#define H2I_DAC_C2 0x1408 /* DAC DMA control, 32 bit */
#define H2I_ADC_C1 0x1504 /* ADC DMA control, 16 bit */
#define H2I_ADC_C2 0x1508 /* ADC DMA control, 32 bit */
/* Bits in CTL1 register */
#define H2I_C1_DMA_SHIFT 0 /* DMA channel */
#define H2I_C1_DMA_M 0x7
#define H2I_C1_CLKID_SHIFT 3 /* Bresenham Clock Gen 1-3 */
#define H2I_C1_CLKID_M 0x18
#define H2I_C1_DATAT_SHIFT 8 /* 1=mono 2=stereo (3=quad) */
#define H2I_C1_DATAT_M 0x300
/* Bits in CTL2 register */
#define H2I_C2_R_GAIN_SHIFT 0 /* right a/d input gain */
#define H2I_C2_R_GAIN_M 0xf
#define H2I_C2_L_GAIN_SHIFT 4 /* left a/d input gain */
#define H2I_C2_L_GAIN_M 0xf0
#define H2I_C2_R_SEL 0x100 /* right input select */
#define H2I_C2_L_SEL 0x200 /* left input select */
#define H2I_C2_MUTE 0x400 /* mute */
#define H2I_C2_DO1 0x00010000 /* digital output port bit 0 */
#define H2I_C2_DO2 0x00020000 /* digital output port bit 1 */
#define H2I_C2_R_ATT_SHIFT 18 /* right d/a output - */
#define H2I_C2_R_ATT_M 0x007c0000 /* attenuation */
#define H2I_C2_L_ATT_SHIFT 23 /* left d/a output - */
#define H2I_C2_L_ATT_M 0x0f800000 /* attenuation */
#define H2I_SYNTH_MAP_C 0x1104 /* synth dma handshake ctrl */
/* Clock generator CTL 1, 16 bit */
#define H2I_BRES1_C1 0x2104
#define H2I_BRES2_C1 0x2204
#define H2I_BRES3_C1 0x2304
#define H2I_BRES_C1_SHIFT 0 /* 0=48.0 1=44.1 2=aes_rx */
#define H2I_BRES_C1_M 0x03
/* Clock generator CTL 2, 32 bit */
#define H2I_BRES1_C2 0x2108
#define H2I_BRES2_C2 0x2208
#define H2I_BRES3_C2 0x2308
#define H2I_BRES_C2_INC_SHIFT 0 /* increment value */
#define H2I_BRES_C2_INC_M 0xffff
#define H2I_BRES_C2_MOD_SHIFT 16 /* modcontrol value */
#define H2I_BRES_C2_MOD_M 0xffff0000 /* modctrl=0xffff&(modinc-1) */
/* Unix timer, 64 bit */
#define H2I_UTIME 0x3104
#define H2I_UTIME_0_LD 0xffff /* microseconds, LSB's */
#define H2I_UTIME_1_LD0 0x0f /* microseconds, MSB's */
#define H2I_UTIME_1_LD1 0xf0 /* tenths of microseconds */
#define H2I_UTIME_2_LD 0xffff /* seconds, LSB's */
#define H2I_UTIME_3_LD 0xffff /* seconds, MSB's */
struct hal2_ctl_regs {
u32 _unused0[4];
u32 isr; /* 0x10 Status Register */
u32 _unused1[3];
u32 rev; /* 0x20 Revision Register */
u32 _unused2[3];
u32 iar; /* 0x30 Indirect Address Register */
u32 _unused3[3];
u32 idr0; /* 0x40 Indirect Data Register 0 */
u32 _unused4[3];
u32 idr1; /* 0x50 Indirect Data Register 1 */
u32 _unused5[3];
u32 idr2; /* 0x60 Indirect Data Register 2 */
u32 _unused6[3];
u32 idr3; /* 0x70 Indirect Data Register 3 */
};
struct hal2_aes_regs {
u32 rx_stat[2]; /* Status registers */
u32 rx_cr[2]; /* Control registers */
u32 rx_ud[4]; /* User data window */
u32 rx_st[24]; /* Channel status data */
u32 tx_stat[1]; /* Status register */
u32 tx_cr[3]; /* Control registers */
u32 tx_ud[4]; /* User data window */
u32 tx_st[24]; /* Channel status data */
};
struct hal2_vol_regs {
u32 right; /* Right volume */
u32 left; /* Left volume */
};
struct hal2_syn_regs {
u32 _unused0[2];
u32 page; /* DOC Page register */
u32 regsel; /* DOC Register selection */
u32 dlow; /* DOC Data low */
u32 dhigh; /* DOC Data high */
u32 irq; /* IRQ Status */
u32 dram; /* DRAM Access */
};
#endif /* __HAL2_H */

977
sound/mips/sgio2audio.c Normal file
View file

@ -0,0 +1,977 @@
/*
* Sound driver for Silicon Graphics O2 Workstations A/V board audio.
*
* Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org>
* Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
* Mxier part taken from mace_audio.c:
* Copyright 2007 Thorben Jändling <tj.trevelyan@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/ip32/ip32_ints.h>
#include <asm/ip32/mace.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#define SNDRV_GET_ID
#include <sound/initval.h>
#include <sound/ad1843.h>
MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>");
MODULE_DESCRIPTION("SGI O2 Audio");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for SGI O2 soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard.");
#define AUDIO_CONTROL_RESET BIT(0) /* 1: reset audio interface */
#define AUDIO_CONTROL_CODEC_PRESENT BIT(1) /* 1: codec detected */
#define CODEC_CONTROL_WORD_SHIFT 0
#define CODEC_CONTROL_READ BIT(16)
#define CODEC_CONTROL_ADDRESS_SHIFT 17
#define CHANNEL_CONTROL_RESET BIT(10) /* 1: reset channel */
#define CHANNEL_DMA_ENABLE BIT(9) /* 1: enable DMA transfer */
#define CHANNEL_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */
#define CHANNEL_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */
#define CHANNEL_INT_THRESHOLD_50 (2 << 5) /* int on buffer >50% full */
#define CHANNEL_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */
#define CHANNEL_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */
#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
#define CHANNEL_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */
#define CHANNEL_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */
#define CHANNEL_RING_SHIFT 12
#define CHANNEL_RING_SIZE (1 << CHANNEL_RING_SHIFT)
#define CHANNEL_RING_MASK (CHANNEL_RING_SIZE - 1)
#define CHANNEL_LEFT_SHIFT 40
#define CHANNEL_RIGHT_SHIFT 8
struct snd_sgio2audio_chan {
int idx;
struct snd_pcm_substream *substream;
int pos;
snd_pcm_uframes_t size;
spinlock_t lock;
};
/* definition of the chip-specific record */
struct snd_sgio2audio {
struct snd_card *card;
/* codec */
struct snd_ad1843 ad1843;
spinlock_t ad1843_lock;
/* channels */
struct snd_sgio2audio_chan channel[3];
/* resources */
void *ring_base;
dma_addr_t ring_base_dma;
};
/* AD1843 access */
/*
* read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
*
* Returns unsigned register value on success, -errno on failure.
*/
static int read_ad1843_reg(void *priv, int reg)
{
struct snd_sgio2audio *chip = priv;
int val;
unsigned long flags;
spin_lock_irqsave(&chip->ad1843_lock, flags);
writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
wmb();
val = readq(&mace->perif.audio.codec_control); /* flush bus */
udelay(200);
val = readq(&mace->perif.audio.codec_read);
spin_unlock_irqrestore(&chip->ad1843_lock, flags);
return val;
}
/*
* write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
*/
static int write_ad1843_reg(void *priv, int reg, int word)
{
struct snd_sgio2audio *chip = priv;
int val;
unsigned long flags;
spin_lock_irqsave(&chip->ad1843_lock, flags);
writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
(word << CODEC_CONTROL_WORD_SHIFT),
&mace->perif.audio.codec_control);
wmb();
val = readq(&mace->perif.audio.codec_control); /* flush bus */
udelay(200);
spin_unlock_irqrestore(&chip->ad1843_lock, flags);
return 0;
}
static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843,
(int)kcontrol->private_value);
return 0;
}
static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
int vol;
vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value);
ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF;
ucontrol->value.integer.value[1] = vol & 0xFF;
return 0;
}
static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
int newvol, oldvol;
oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value);
newvol = (ucontrol->value.integer.value[0] << 8) |
ucontrol->value.integer.value[1];
newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value,
newvol);
return newvol != oldvol;
}
static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char *texts[3] = {
"Cam Mic", "Mic", "Line"
};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
if (uinfo->value.enumerated.item >= 3)
uinfo->value.enumerated.item = 1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843);
return 0;
}
static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
int newsrc, oldsrc;
oldsrc = ad1843_get_recsrc(&chip->ad1843);
newsrc = ad1843_set_recsrc(&chip->ad1843,
ucontrol->value.enumerated.item[0]);
return newsrc != oldsrc;
}
/* dac1/pcm0 mixer control */
static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = AD1843_GAIN_PCM_0,
.info = sgio2audio_gain_info,
.get = sgio2audio_gain_get,
.put = sgio2audio_gain_put,
};
/* dac2/pcm1 mixer control */
static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 1,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = AD1843_GAIN_PCM_1,
.info = sgio2audio_gain_info,
.get = sgio2audio_gain_get,
.put = sgio2audio_gain_put,
};
/* record level mixer control */
static struct snd_kcontrol_new sgio2audio_ctrl_reclevel = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = AD1843_GAIN_RECLEV,
.info = sgio2audio_gain_info,
.get = sgio2audio_gain_get,
.put = sgio2audio_gain_put,
};
/* record level source control */
static struct snd_kcontrol_new sgio2audio_ctrl_recsource = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = sgio2audio_source_info,
.get = sgio2audio_source_get,
.put = sgio2audio_source_put,
};
/* line mixer control */
static struct snd_kcontrol_new sgio2audio_ctrl_line = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = AD1843_GAIN_LINE,
.info = sgio2audio_gain_info,
.get = sgio2audio_gain_get,
.put = sgio2audio_gain_put,
};
/* cd mixer control */
static struct snd_kcontrol_new sgio2audio_ctrl_cd = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Playback Volume",
.index = 1,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = AD1843_GAIN_LINE_2,
.info = sgio2audio_gain_info,
.get = sgio2audio_gain_get,
.put = sgio2audio_gain_put,
};
/* mic mixer control */
static struct snd_kcontrol_new sgio2audio_ctrl_mic = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.private_value = AD1843_GAIN_MIC,
.info = sgio2audio_gain_info,
.get = sgio2audio_gain_get,
.put = sgio2audio_gain_put,
};
static int snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
{
int err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_recsource, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_line, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_cd, chip));
if (err < 0)
return err;
err = snd_ctl_add(chip->card,
snd_ctl_new1(&sgio2audio_ctrl_mic, chip));
if (err < 0)
return err;
return 0;
}
/* low-level audio interface DMA */
/* get data out of bounce buffer, count must be a multiple of 32 */
/* returns 1 if a period has elapsed */
static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
unsigned int ch, unsigned int count)
{
int ret;
unsigned long src_base, src_pos, dst_mask;
unsigned char *dst_base;
int dst_pos;
u64 *src;
s16 *dst;
u64 x;
unsigned long flags;
struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
spin_lock_irqsave(&chip->channel[ch].lock, flags);
src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
dst_base = runtime->dma_area;
dst_pos = chip->channel[ch].pos;
dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
/* check if a period has elapsed */
chip->channel[ch].size += (count >> 3); /* in frames */
ret = chip->channel[ch].size >= runtime->period_size;
chip->channel[ch].size %= runtime->period_size;
while (count) {
src = (u64 *)(src_base + src_pos);
dst = (s16 *)(dst_base + dst_pos);
x = *src;
dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff;
dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff;
src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK;
dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask;
count -= sizeof(u64);
}
writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
chip->channel[ch].pos = dst_pos;
spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return ret;
}
/* put some DMA data in bounce buffer, count must be a multiple of 32 */
/* returns 1 if a period has elapsed */
static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
unsigned int ch, unsigned int count)
{
int ret;
s64 l, r;
unsigned long dst_base, dst_pos, src_mask;
unsigned char *src_base;
int src_pos;
u64 *dst;
s16 *src;
unsigned long flags;
struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
spin_lock_irqsave(&chip->channel[ch].lock, flags);
dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
src_base = runtime->dma_area;
src_pos = chip->channel[ch].pos;
src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
/* check if a period has elapsed */
chip->channel[ch].size += (count >> 3); /* in frames */
ret = chip->channel[ch].size >= runtime->period_size;
chip->channel[ch].size %= runtime->period_size;
while (count) {
src = (s16 *)(src_base + src_pos);
dst = (u64 *)(dst_base + dst_pos);
l = src[0]; /* sign extend */
r = src[1]; /* sign extend */
*dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) |
((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT);
dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK;
src_pos = (src_pos + 2 * sizeof(s16)) & src_mask;
count -= sizeof(u64);
}
writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
chip->channel[ch].pos = src_pos;
spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return ret;
}
static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
int ch = chan->idx;
/* reset DMA channel */
writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control);
udelay(10);
writeq(0, &mace->perif.audio.chan[ch].control);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* push a full buffer */
snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32);
}
/* set DMA to wake on 50% empty and enable interrupt */
writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50,
&mace->perif.audio.chan[ch].control);
return 0;
}
static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
writeq(0, &mace->perif.audio.chan[chan->idx].control);
return 0;
}
static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id)
{
struct snd_sgio2audio_chan *chan = dev_id;
struct snd_pcm_substream *substream;
struct snd_sgio2audio *chip;
int count, ch;
substream = chan->substream;
chip = snd_pcm_substream_chip(substream);
ch = chan->idx;
/* empty the ring */
count = CHANNEL_RING_SIZE -
readq(&mace->perif.audio.chan[ch].depth) - 32;
if (snd_sgio2audio_dma_pull_frag(chip, ch, count))
snd_pcm_period_elapsed(substream);
return IRQ_HANDLED;
}
static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id)
{
struct snd_sgio2audio_chan *chan = dev_id;
struct snd_pcm_substream *substream;
struct snd_sgio2audio *chip;
int count, ch;
substream = chan->substream;
chip = snd_pcm_substream_chip(substream);
ch = chan->idx;
/* fill the ring */
count = CHANNEL_RING_SIZE -
readq(&mace->perif.audio.chan[ch].depth) - 32;
if (snd_sgio2audio_dma_push_frag(chip, ch, count))
snd_pcm_period_elapsed(substream);
return IRQ_HANDLED;
}
static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
{
struct snd_sgio2audio_chan *chan = dev_id;
struct snd_pcm_substream *substream;
substream = chan->substream;
snd_sgio2audio_dma_stop(substream);
snd_sgio2audio_dma_start(substream);
return IRQ_HANDLED;
}
/* PCM part */
/* PCM hardware definition */
static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
.formats = SNDRV_PCM_FMTBIT_S16_BE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 65536,
.period_bytes_min = 32768,
.period_bytes_max = 65536,
.periods_min = 1,
.periods_max = 1024,
};
/* PCM playback open callback */
static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_sgio2audio_pcm_hw;
runtime->private_data = &chip->channel[1];
return 0;
}
static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_sgio2audio_pcm_hw;
runtime->private_data = &chip->channel[2];
return 0;
}
/* PCM capture open callback */
static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_sgio2audio_pcm_hw;
runtime->private_data = &chip->channel[0];
return 0;
}
/* PCM close callback */
static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->private_data = NULL;
return 0;
}
/* hw_params callback */
static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
}
/* hw_free callback */
static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
/* prepare callback */
static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
int ch = chan->idx;
unsigned long flags;
spin_lock_irqsave(&chip->channel[ch].lock, flags);
/* Setup the pseudo-dma transfer pointers. */
chip->channel[ch].pos = 0;
chip->channel[ch].size = 0;
chip->channel[ch].substream = substream;
/* set AD1843 format */
/* hardware format is always S16_LE */
switch (substream->stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
ad1843_setup_dac(&chip->ad1843,
ch - 1,
runtime->rate,
SNDRV_PCM_FORMAT_S16_LE,
runtime->channels);
break;
case SNDRV_PCM_STREAM_CAPTURE:
ad1843_setup_adc(&chip->ad1843,
runtime->rate,
SNDRV_PCM_FORMAT_S16_LE,
runtime->channels);
break;
}
spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return 0;
}
/* trigger callback */
static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* start the PCM engine */
snd_sgio2audio_dma_start(substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
/* stop the PCM engine */
snd_sgio2audio_dma_stop(substream);
break;
default:
return -EINVAL;
}
return 0;
}
/* pointer callback */
static snd_pcm_uframes_t
snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
/* get the current hardware pointer */
return bytes_to_frames(substream->runtime,
chip->channel[chan->idx].pos);
}
/* operators */
static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
.open = snd_sgio2audio_playback1_open,
.close = snd_sgio2audio_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_sgio2audio_pcm_hw_params,
.hw_free = snd_sgio2audio_pcm_hw_free,
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
.open = snd_sgio2audio_playback2_open,
.close = snd_sgio2audio_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_sgio2audio_pcm_hw_params,
.hw_free = snd_sgio2audio_pcm_hw_free,
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
.open = snd_sgio2audio_capture_open,
.close = snd_sgio2audio_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_sgio2audio_pcm_hw_params,
.hw_free = snd_sgio2audio_pcm_hw_free,
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
/*
* definitions of capture are omitted here...
*/
/* create a pcm device */
static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
{
struct snd_pcm *pcm;
int err;
/* create first pcm device with one outputs and one input */
err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, "SGI O2 DAC1");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback1_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_sgio2audio_capture_ops);
/* create second pcm device with one outputs and no input */
err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, "SGI O2 DAC2");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback2_ops);
return 0;
}
static struct {
int idx;
int irq;
irqreturn_t (*isr)(int, void *);
const char *desc;
} snd_sgio2_isr_table[] = {
{
.idx = 0,
.irq = MACEISA_AUDIO1_DMAT_IRQ,
.isr = snd_sgio2audio_dma_in_isr,
.desc = "Capture DMA Channel 0"
}, {
.idx = 0,
.irq = MACEISA_AUDIO1_OF_IRQ,
.isr = snd_sgio2audio_error_isr,
.desc = "Capture Overflow"
}, {
.idx = 1,
.irq = MACEISA_AUDIO2_DMAT_IRQ,
.isr = snd_sgio2audio_dma_out_isr,
.desc = "Playback DMA Channel 1"
}, {
.idx = 1,
.irq = MACEISA_AUDIO2_MERR_IRQ,
.isr = snd_sgio2audio_error_isr,
.desc = "Memory Error Channel 1"
}, {
.idx = 2,
.irq = MACEISA_AUDIO3_DMAT_IRQ,
.isr = snd_sgio2audio_dma_out_isr,
.desc = "Playback DMA Channel 2"
}, {
.idx = 2,
.irq = MACEISA_AUDIO3_MERR_IRQ,
.isr = snd_sgio2audio_error_isr,
.desc = "Memory Error Channel 2"
}
};
/* ALSA driver */
static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
{
int i;
/* reset interface */
writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
udelay(1);
writeq(0, &mace->perif.audio.control);
/* release IRQ's */
for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++)
free_irq(snd_sgio2_isr_table[i].irq,
&chip->channel[snd_sgio2_isr_table[i].idx]);
dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
chip->ring_base, chip->ring_base_dma);
/* release card data */
kfree(chip);
return 0;
}
static int snd_sgio2audio_dev_free(struct snd_device *device)
{
struct snd_sgio2audio *chip = device->device_data;
return snd_sgio2audio_free(chip);
}
static struct snd_device_ops ops = {
.dev_free = snd_sgio2audio_dev_free,
};
static int snd_sgio2audio_create(struct snd_card *card,
struct snd_sgio2audio **rchip)
{
struct snd_sgio2audio *chip;
int i, err;
*rchip = NULL;
/* check if a codec is attached to the interface */
/* (Audio or Audio/Video board present) */
if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
return -ENOENT;
chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->card = card;
chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
&chip->ring_base_dma, GFP_USER);
if (chip->ring_base == NULL) {
printk(KERN_ERR
"sgio2audio: could not allocate ring buffers\n");
kfree(chip);
return -ENOMEM;
}
spin_lock_init(&chip->ad1843_lock);
/* initialize channels */
for (i = 0; i < 3; i++) {
spin_lock_init(&chip->channel[i].lock);
chip->channel[i].idx = i;
}
/* allocate IRQs */
for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) {
if (request_irq(snd_sgio2_isr_table[i].irq,
snd_sgio2_isr_table[i].isr,
0,
snd_sgio2_isr_table[i].desc,
&chip->channel[snd_sgio2_isr_table[i].idx])) {
snd_sgio2audio_free(chip);
printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n",
snd_sgio2_isr_table[i].irq);
return -EBUSY;
}
}
/* reset the interface */
writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
udelay(1);
writeq(0, &mace->perif.audio.control);
msleep_interruptible(1); /* give time to recover */
/* set ring base */
writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase);
/* attach the AD1843 codec */
chip->ad1843.read = read_ad1843_reg;
chip->ad1843.write = write_ad1843_reg;
chip->ad1843.chip = chip;
/* initialize the AD1843 codec */
err = ad1843_init(&chip->ad1843);
if (err < 0) {
snd_sgio2audio_free(chip);
return err;
}
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
snd_sgio2audio_free(chip);
return err;
}
*rchip = chip;
return 0;
}
static int snd_sgio2audio_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_sgio2audio *chip;
int err;
err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
err = snd_sgio2audio_create(card, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
err = snd_sgio2audio_new_pcm(chip);
if (err < 0) {
snd_card_free(card);
return err;
}
err = snd_sgio2audio_new_mixer(chip);
if (err < 0) {
snd_card_free(card);
return err;
}
strcpy(card->driver, "SGI O2 Audio");
strcpy(card->shortname, "SGI O2 Audio");
sprintf(card->longname, "%s irq %i-%i",
card->shortname,
MACEISA_AUDIO1_DMAT_IRQ,
MACEISA_AUDIO3_MERR_IRQ);
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
platform_set_drvdata(pdev, card);
return 0;
}
static int snd_sgio2audio_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
snd_card_free(card);
return 0;
}
static struct platform_driver sgio2audio_driver = {
.probe = snd_sgio2audio_probe,
.remove = snd_sgio2audio_remove,
.driver = {
.name = "sgio2audio",
.owner = THIS_MODULE,
}
};
module_platform_driver(sgio2audio_driver);