Commit fc37449f authored by Adrian Bunk's avatar Adrian Bunk Committed by Linus Torvalds

The next round of scheduled OSS code removal

This patch contains the next round of scheduled OSS code removal.
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5b4db0c2
......@@ -219,13 +219,6 @@ Who: Jean Delvare <khali@linux-fr.org>,
---------------------------
What: drivers depending on OBSOLETE_OSS
When: options in 2.6.22, code in 2.6.24
Why: OSS drivers with ALSA replacements
Who: Adrian Bunk <bunk@stusta.de>
---------------------------
What: ACPI procfs interface
When: July 2008
Why: ACPI sysfs conversion should be finished by January 2008.
......
/proc/sound, /dev/sndstat
-------------------------
/proc/sound and /dev/sndstat is not supported by the
driver. To find out whether the driver succeeded loading,
check the kernel log (dmesg).
ALaw/uLaw sample formats
------------------------
This driver does not support the ALaw/uLaw sample formats.
ALaw is the default mode when opening a sound device
using OSS/Free. The reason for the lack of support is
that the hardware does not support these formats, and adding
conversion routines to the kernel would lead to very ugly
code in the presence of the mmap interface to the driver.
And since xquake uses mmap, mmap is considered important :-)
and no sane application uses ALaw/uLaw these days anyway.
In short, playing a Sun .au file as follows:
cat my_file.au > /dev/dsp
does not work. Instead, you may use the play script from
Chris Bagwell's sox-12.14 package (available from the URL
below) to play many different audio file formats.
The script automatically determines the audio format
and does do audio conversions if necessary.
http://home.sprynet.com/sprynet/cbagwell/projects.html
Blocking vs. nonblocking IO
---------------------------
Unlike OSS/Free this driver honours the O_NONBLOCK file flag
not only during open, but also during read and write.
This is an effort to make the sound driver interface more
regular. Timidity has problems with this; a patch
is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html.
(Timidity patched will also run on OSS/Free).
MIDI UART
---------
The driver supports a simple MIDI UART interface, with
no ioctl's supported.
MIDI synthesizer
----------------
This soundcard does not have any hardware MIDI synthesizer;
MIDI synthesis has to be done in software. To allow this
the driver/soundcard supports two PCM (/dev/dsp) interfaces.
There is a freely available software package that allows
MIDI file playback on this soundcard called Timidity.
See http://www.cgs.fi/~tt/timidity/.
Thomas Sailer
t.sailer@alumni.ethz.ch
......@@ -2940,13 +2940,6 @@ L: linux-kernel@vger.kernel.org
L: linux-pci@atrey.karlin.mff.cuni.cz
S: Supported
PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES)
P: Thomas Sailer
M: sailer@ife.ee.ethz.ch
L: linux-sound@vger.kernel.org
W: http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html
S: Maintained
PCI SUBSYSTEM
P: Greg Kroah-Hartman
M: gregkh@suse.de
......
......@@ -36,7 +36,6 @@ obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o
obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o
obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
......
......@@ -2,12 +2,6 @@
# Makefile for the DMA sound driver
#
dmasound_pmac-y += dmasound_awacs.o \
trans_16.o dac3550a.o tas_common.o \
tas3001c.o tas3001c_tables.o \
tas3004.o tas3004_tables.o
obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o
obj-$(CONFIG_DMASOUND_PMAC) += dmasound_core.o dmasound_pmac.o
obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o
obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o
/*********************************************************/
/* This file was written by someone, somewhere, sometime */
/* And is released into the Public Domain */
/*********************************************************/
#ifndef _AWACS_DEFS_H_
#define _AWACS_DEFS_H_
/*******************************/
/* AWACs Audio Register Layout */
/*******************************/
struct awacs_regs {
unsigned control; /* Audio control register */
unsigned pad0[3];
unsigned codec_ctrl; /* Codec control register */
unsigned pad1[3];
unsigned codec_stat; /* Codec status register */
unsigned pad2[3];
unsigned clip_count; /* Clipping count register */
unsigned pad3[3];
unsigned byteswap; /* Data is little-endian if 1 */
};
/*******************/
/* Audio Bit Masks */
/*******************/
/* Audio Control Reg Bit Masks */
/* ----- ------- --- --- ----- */
#define MASK_ISFSEL (0xf) /* Input SubFrame Select */
#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */
#define MASK_RATE (0x7 << 8) /* Sound Rate */
#define MASK_CNTLERR (0x1 << 11) /* Error */
#define MASK_PORTCHG (0x1 << 12) /* Port Change */
#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */
#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */
#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */
/* Audio Codec Control Reg Bit Masks */
/* ----- ----- ------- --- --- ----- */
#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */
#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */
#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */
#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */
/* Audio Codec Control Address Values / Masks */
/* ----- ----- ------- ------- ------ - ----- */
#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */
#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */
#define MASK_ADDR_GAIN MASK_ADDR0
#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */
#define MASK_ADDR_MUTE MASK_ADDR1
#define MASK_ADDR_RATE MASK_ADDR1
#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */
#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */
#define MASK_ADDR_VOLHD MASK_ADDR2
#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */
#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */
#define MASK_ADDR_VOLSPK MASK_ADDR4
/* additional registers of screamer */
#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */
#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */
#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */
/* Address 0 Bit Masks & Macros */
/* ------- - --- ----- - ------ */
#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */
#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */
#define MASK_GAINLINE (0x1 << 8) /* Disable Mic preamp */
#define MASK_GAINMIC (0x0 << 8) /* Enable Mic preamp */
#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */
#define MASK_MUX_MIC (0x1 << 10) /* Select Mic in MUX */
#define MASK_MUX_AUDIN (0x1 << 11) /* Select Audio In in MUX */
#define MASK_MUX_LINE MASK_MUX_AUDIN
#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT)
#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT)
#define DEF_CD_GAIN 0x00bb
#define DEF_MIC_GAIN 0x00cc
/* Address 1 Bit Masks */
/* ------- - --- ----- */
#define MASK_ADDR1RES1 (0x3) /* Reserved */
#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */
#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */
#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */
#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */
#define MASK_SPKMUTE MASK_CMUTE
#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */
#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */
#define MASK_HDMUTE MASK_AMUTE
#define MASK_PAROUT0 (0x1 << 10) /* Parallel Output 0 */
#define MASK_PAROUT1 (0x2 << 10) /* Parallel Output 1 */
#define MASK_MIC_BOOST (0x4) /* screamer mic boost */
#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */
#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */
#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */
#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */
#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */
#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */
#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */
#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */
/* Address 2 & 4 Bit Masks & Macros */
/* ------- - - - --- ----- - ------ */
#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */
#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */
#define MASK_ADDR4RES1 MASK_ADDR2RES1
#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */
#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */
#define MASK_ADDR4RES2 MASK_ADDR2RES2
#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT))
#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT)
/* Audio Codec Status Reg Bit Masks */
/* ----- ----- ------ --- --- ----- */
#define MASK_EXTEND (0x1 << 23) /* Extend */
#define MASK_VALID (0x1 << 22) /* Valid Data? */
#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */
#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */
#define MASK_ERRCODE (0xf << 16) /* Error Code */
#define MASK_REVISION (0xf << 12) /* Revision Number */
#define MASK_MFGID (0xf << 8) /* Mfg. ID */
#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */
#define MASK_INPPORT (0xf) /* Input Port */
#define MASK_HDPCONN 8 /* headphone plugged in */
/* Clipping Count Reg Bit Masks */
/* -------- ----- --- --- ----- */
#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */
#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */
/* DBDMA ChannelStatus Bit Masks */
/* ----- ------------- --- ----- */
#define MASK_CSERR (0x1 << 7) /* Error */
#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */
#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */
#define MASK_WAIT (0x1) /* Wait */
/* Various Rates */
/* ------- ----- */
#define RATE_48000 (0x0 << 8) /* 48 kHz */
#define RATE_44100 (0x0 << 8) /* 44.1 kHz */
#define RATE_32000 (0x1 << 8) /* 32 kHz */
#define RATE_29400 (0x1 << 8) /* 29.4 kHz */
#define RATE_24000 (0x2 << 8) /* 24 kHz */
#define RATE_22050 (0x2 << 8) /* 22.05 kHz */
#define RATE_19200 (0x3 << 8) /* 19.2 kHz */
#define RATE_17640 (0x3 << 8) /* 17.64 kHz */
#define RATE_16000 (0x4 << 8) /* 16 kHz */
#define RATE_14700 (0x4 << 8) /* 14.7 kHz */
#define RATE_12000 (0x5 << 8) /* 12 kHz */
#define RATE_11025 (0x5 << 8) /* 11.025 kHz */
#define RATE_9600 (0x6 << 8) /* 9.6 kHz */
#define RATE_8820 (0x6 << 8) /* 8.82 kHz */
#define RATE_8000 (0x7 << 8) /* 8 kHz */
#define RATE_7350 (0x7 << 8) /* 7.35 kHz */
#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */
/*******************/
/* Burgundy values */
/*******************/
#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
/* These are all default values for the burgundy */
#define DEF_BURGUNDY_INPSEL21 (0xAA)
#define DEF_BURGUNDY_INPSEL3 (0x0A)
#define DEF_BURGUNDY_GAINCD (0x33)
#define DEF_BURGUNDY_GAINLINE (0x44)
#define DEF_BURGUNDY_GAINMIC (0x44)
#define DEF_BURGUNDY_GAINMODEM (0x06)
/* Remember: lowest volume here is 0x9b */
#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
#define DEF_BURGUNDY_VOLLINE (0x00000000)
#define DEF_BURGUNDY_VOLMIC (0x00000000)
#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF)
#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
#define DEF_BURGUNDY_ATTENHP (0xCC)
/*********************/
/* i2s layout values */
/*********************/
#define I2S_REG_INT_CTL 0x00
#define I2S_REG_SERIAL_FORMAT 0x10
#define I2S_REG_CODEC_MSG_OUT 0x20
#define I2S_REG_CODEC_MSG_IN 0x30
#define I2S_REG_FRAME_COUNT 0x40
#define I2S_REG_FRAME_MATCH 0x50
#define I2S_REG_DATAWORD_SIZES 0x60
#define I2S_REG_PEAKLEVEL_SEL 0x70
#define I2S_REG_PEAKLEVEL_IN0 0x80
#define I2S_REG_PEAKLEVEL_IN1 0x90
#endif /* _AWACS_DEFS_H_ */
/*
* Driver for the i2c/i2s based DAC3550a sound chip used
* on some Apple iBooks. Also known as "DACA".
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include "dmasound.h"
/* FYI: This code was derived from the tas3001c.c Texas/Tumbler mixer
* control code, as well as info derived from the AppleDACAAudio driver
* from Darwin CVS (main thing I derived being register numbers and
* values, as well as when to make the calls). */
#define I2C_DRIVERID_DACA (0xFDCB)
#define DACA_VERSION "0.1"
#define DACA_DATE "20010930"
static int cur_left_vol;
static int cur_right_vol;
static struct i2c_client *daca_client;
static int daca_attach_adapter(struct i2c_adapter *adapter);
static int daca_detect_client(struct i2c_adapter *adapter, int address);
static int daca_detach_client(struct i2c_client *client);
struct i2c_driver daca_driver = {
.driver = {
.name = "DAC3550A driver V " DACA_VERSION,
},
.id = I2C_DRIVERID_DACA,
.attach_adapter = daca_attach_adapter,
.detach_client = daca_detach_client,
};
#define VOL_MAX ((1<<20) - 1)
void daca_get_volume(uint * left_vol, uint *right_vol)
{
*left_vol = cur_left_vol >> 5;
*right_vol = cur_right_vol >> 5;
}
int daca_set_volume(uint left_vol, uint right_vol)
{
unsigned short voldata;
if (!daca_client)
return -1;
/* Derived from experience, not from any specific values */
left_vol <<= 5;
right_vol <<= 5;
if (left_vol > VOL_MAX)
left_vol = VOL_MAX;
if (right_vol > VOL_MAX)
right_vol = VOL_MAX;
voldata = ((left_vol >> 14) & 0x3f) << 8;
voldata |= (right_vol >> 14) & 0x3f;
if (i2c_smbus_write_word_data(daca_client, 2, voldata) < 0) {
printk("daca: failed to set volume \n");
return -1;
}
cur_left_vol = left_vol;
cur_right_vol = right_vol;
return 0;
}
int daca_leave_sleep(void)
{
if (!daca_client)
return -1;
/* Do a short sleep, just to make sure I2C bus is awake and paying
* attention to us
*/
msleep(20);
/* Write the sample rate reg the value it needs */
i2c_smbus_write_byte_data(daca_client, 1, 8);
daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
/* Another short delay, just to make sure the other I2C bus writes
* have taken...
*/
msleep(20);
/* Write the global config reg - invert right power amp,
* DAC on, use 5-volt mode */
i2c_smbus_write_byte_data(daca_client, 3, 0x45);
return 0;
}
int daca_enter_sleep(void)
{
if (!daca_client)
return -1;
i2c_smbus_write_byte_data(daca_client, 1, 8);
daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
/* Write the global config reg - invert right power amp,
* DAC on, enter low-power mode, use 5-volt mode
*/
i2c_smbus_write_byte_data(daca_client, 3, 0x65);
return 0;
}
static int daca_attach_adapter(struct i2c_adapter *adapter)
{
if (!strncmp(adapter->name, "mac-io", 6))
daca_detect_client(adapter, 0x4d);
return 0;
}
static int daca_init_client(struct i2c_client * new_client)
{
/*
* Probe is not working with the current i2c-keywest
* driver. We try to use addr 0x4d on each adapters
* instead, by setting the format register.
*
* FIXME: I'm sure that can be obtained from the
* device-tree. --BenH.
*/
/* Write the global config reg - invert right power amp,
* DAC on, use 5-volt mode
*/
if (i2c_smbus_write_byte_data(new_client, 3, 0x45))
return -1;
i2c_smbus_write_byte_data(new_client, 1, 8);
daca_client = new_client;
daca_set_volume(15000, 15000);
return 0;
}
static int daca_detect_client(struct i2c_adapter *adapter, int address)
{
const char *client_name = "DAC 3550A Digital Equalizer";
struct i2c_client *new_client;
int rc = -ENODEV;
new_client = kzalloc(sizeof(*new_client), GFP_KERNEL);
if (!new_client)
return -ENOMEM;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &daca_driver;
new_client->flags = 0;
strcpy(new_client->name, client_name);
if (daca_init_client(new_client))
goto bail;
/* Tell the i2c layer a new client has arrived */
if (i2c_attach_client(new_client))
goto bail;
return 0;
bail:
kfree(new_client);
return rc;
}
static int daca_detach_client(struct i2c_client *client)
{
if (client == daca_client)
daca_client = NULL;
i2c_detach_client(client);
kfree(client);
return 0;
}
void daca_cleanup(void)
{
i2c_del_driver(&daca_driver);
}
int daca_init(void)
{
printk("dac3550a driver version %s (%s)\n",DACA_VERSION,DACA_DATE);
return i2c_add_driver(&daca_driver);
}
......@@ -59,7 +59,6 @@ static inline int ioctl_return(int __user *addr, int value)
*/
#undef HAS_8BIT_TABLES
#undef HAS_RECORD
#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
......@@ -83,10 +82,6 @@ static inline int ioctl_return(int __user *addr, int value)
#define DEFAULT_N_BUFFERS 4
#define DEFAULT_BUFF_SIZE (1<<15)
#if defined(CONFIG_DMASOUND_PMAC) || defined(CONFIG_DMASOUND_PMAC_MODULE)
#define HAS_RECORD
#endif
/*
* Initialization
*/
......@@ -168,9 +163,6 @@ struct sound_settings {
SETTINGS soft; /* software settings */
SETTINGS dsp; /* /dev/dsp default settings */
TRANS *trans_write; /* supported translations */
#ifdef HAS_RECORD
TRANS *trans_read; /* supported translations */
#endif
int volume_left; /* volume (range is machine dependent) */
int volume_right;
int bass; /* tone (range is machine dependent) */
......@@ -253,11 +245,6 @@ struct sound_queue {
extern struct sound_queue dmasound_write_sq;
#define write_sq dmasound_write_sq
#ifdef HAS_RECORD
extern struct sound_queue dmasound_read_sq;
#define read_sq dmasound_read_sq
#endif
extern int dmasound_catchRadius;
#define catchRadius dmasound_catchRadius
......
/*
* linux/sound/oss/dmasound/dmasound_awacs.c
*
* PowerMac `AWACS' and `Burgundy' DMA Sound Driver
* with some limited support for DACA & Tumbler
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and
* history prior to 2001/01/26.
*
* 26/01/2001 ed 0.1 Iain Sandoe
* - added version info.
* - moved dbdma command buffer allocation to PMacXXXSqSetup()
* - fixed up beep dbdma cmd buffers
*
* 08/02/2001 [0.2]
* - make SNDCTL_DSP_GETFMTS return the correct info for the h/w
* - move soft format translations to a separate file
* - [0.3] make SNDCTL_DSP_GETCAPS return correct info.
* - [0.4] more informative machine name strings.
* - [0.5]
* - record changes.
* - made the default_hard/soft entries.
* 04/04/2001 [0.6]
* - minor correction to bit assignments in awacs_defs.h
* - incorporate mixer changes from 2.2.x back-port.
* - take out passthru as a rec input (it isn't).
* - make Input Gain slider work the 'right way up'.
* - try to make the mixer sliders more logical - so now the
* input selectors are just two-state (>50% == ON) and the
* Input Gain slider handles the rest of the gain issues.
* - try to pick slider representations that most closely match
* the actual use - e.g. IGain for input gain...
* - first stab at over/under-run detection.
* - minor cosmetic changes to IRQ identification.
* - fix bug where rates > max would be reported as supported.
* - first stab at over/under-run detection.
* - make use of i2c for mixer settings conditional on perch
* rather than cuda (some machines without perch have cuda).
* - fix bug where TX stops when dbdma status comes up "DEAD"
* so far only reported on PowerComputing clones ... but.
* - put in AWACS/Screamer register write timeouts.
* - part way to partitioning the init() stuff
* - first pass at 'tumbler' stuff (not support - just an attempt
* to allow the driver to load on new G4s).
* 01/02/2002 [0.7] - BenH
* - all sort of minor bits went in since the latest update, I
* bumped the version number for that reason
*
* 07/26/2002 [0.8] - BenH
* - More minor bits since last changelog (I should be more careful
* with those)
* - Support for snapper & better tumbler integration by Toby Sargeant
* - Headphone detect for scremer by Julien Blache
* - More tumbler fixed by Andreas Schwab
* 11/29/2003 [0.8.1] - Renzo Davoli (King Enzo)
* - Support for Snapper line in
* - snapper input resampling (for rates < 44100)
* - software line gain control
*/
/* GENERAL FIXME/TODO: check that the assumptions about what is written to
mac-io is valid for DACA & Tumbler.
This driver is in bad need of a rewrite. The dbdma code has to be split,
some proper device-tree parsing code has to be written, etc...
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/soundcard.h>
#include <linux/adb.h>
#include <linux/nvram.h>
#include <linux/tty.h>
#include <linux/vt_kern.h>
#include <linux/spinlock.h>
#include <linux/kmod.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/mutex.h>
#ifdef CONFIG_ADB_CUDA
#include <linux/cuda.h>
#endif
#ifdef CONFIG_ADB_PMU
#include <linux/pmu.h>
#endif
#include <asm/uaccess.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/dbdma.h>
#include <asm/pmac_feature.h>
#include <asm/irq.h>
#include <asm/nvram.h>
#include "awacs_defs.h"
#include "dmasound.h"
#include "tas3001c.h"
#include "tas3004.h"
#include "tas_common.h"
#define DMASOUND_AWACS_REVISION 0
#define DMASOUND_AWACS_EDITION 7
#define AWACS_SNAPPER 110 /* fake revision # for snapper */
#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */
#define AWACS_TUMBLER 90 /* fake revision # for tumbler */
#define AWACS_DACA 80 /* fake revision # for daca (ibook) */
#define AWACS_AWACS 2 /* holding revision for AWACS */
#define AWACS_SCREAMER 3 /* holding revision for Screamer */
/*
* Interrupt numbers and addresses, & info obtained from the device tree.
*/
static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
static volatile struct awacs_regs __iomem *awacs;
static volatile u32 __iomem *i2s;
static volatile struct dbdma_regs __iomem *awacs_txdma, *awacs_rxdma;
static int awacs_rate_index;
static int awacs_subframe;
static struct device_node* awacs_node;
static struct device_node* i2s_node;
static struct resource awacs_rsrc[3];
static char awacs_name[64];
static int awacs_revision;
static int awacs_sleeping;
static DEFINE_MUTEX(dmasound_mutex);
static int sound_device_id; /* exists after iMac revA */
static int hw_can_byteswap = 1 ; /* most pmac sound h/w can */
/* model info */
/* To be replaced with better interaction with pmac_feature.c */
static int is_pbook_3X00;
static int is_pbook_g3;
/* expansion info */
static int has_perch;
static int has_ziva;
/* for earlier powerbooks which need fiddling with mac-io to enable
* cd etc.
*/
static unsigned char __iomem *latch_base;
static unsigned char __iomem *macio_base;
/*
* Space for the DBDMA command blocks.
*/
static void *awacs_tx_cmd_space;
static volatile struct dbdma_cmd *awacs_tx_cmds;
static int number_of_tx_cmd_buffers;
static void *awacs_rx_cmd_space;
static volatile struct dbdma_cmd *awacs_rx_cmds;
static int number_of_rx_cmd_buffers;
/*
* Cached values of AWACS registers (we can't read them).
* Except on the burgundy (and screamer). XXX
*/
int awacs_reg[8];
int awacs_reg1_save;
/* tracking values for the mixer contents
*/
static int spk_vol;
static int line_vol;
static int passthru_vol;
static int ip_gain; /* mic preamp settings */
static int rec_lev = 0x4545 ; /* default CD gain 69 % */
static int mic_lev;
static int cd_lev = 0x6363 ; /* 99 % */
static int line_lev;
static int hdp_connected;
/*
* Stuff for outputting a beep. The values range from -327 to +327
* so we can multiply by an amplitude in the range 0..100 to get a
* signed short value to put in the output buffer.
*/
static short beep_wform[256] = {
0, 40, 79, 117, 153, 187, 218, 245,
269, 288, 304, 316, 323, 327, 327, 324,
318, 310, 299, 288, 275, 262, 249, 236,
224, 213, 204, 196, 190, 186, 183, 182,
182, 183, 186, 189, 192, 196, 200, 203,
206, 208, 209, 209, 209, 207, 204, 201,
197, 193, 188, 183, 179, 174, 170, 166,
163, 161, 160, 159, 159, 160, 161, 162,
164, 166, 168, 169, 171, 171, 171, 170,
169, 167, 163, 159, 155, 150, 144, 139,
133, 128, 122, 117, 113, 110, 107, 105,
103, 103, 103, 103, 104, 104, 105, 105,
105, 103, 101, 97, 92, 86, 78, 68,
58, 45, 32, 18, 3, -11, -26, -41,
-55, -68, -79, -88, -95, -100, -102, -102,
-99, -93, -85, -75, -62, -48, -33, -16,
0, 16, 33, 48, 62, 75, 85, 93,
99, 102, 102, 100, 95, 88, 79, 68,
55, 41, 26, 11, -3, -18, -32, -45,
-58, -68, -78, -86, -92, -97, -101, -103,
-105, -105, -105, -104, -104, -103, -103, -103,
-103, -105, -107, -110, -113, -117, -122, -128,
-133, -139, -144, -150, -155, -159, -163, -167,
-169, -170, -171, -171, -171, -169, -168, -166,
-164, -162, -161, -160, -159, -159, -160, -161,
-163, -166, -170, -174, -179, -183, -188, -193,
-197, -201, -204, -207, -209, -209, -209, -208,
-206, -203, -200, -196, -192, -189, -186, -183,
-182, -182, -183, -186, -190, -196, -204, -213,
-224, -236, -249, -262, -275, -288, -299, -310,
-318, -324, -327, -327, -323, -316, -304, -288,
-269, -245, -218, -187, -153, -117, -79, -40,
};
/* beep support */
#define BEEP_SRATE 22050 /* 22050 Hz sample rate */
#define BEEP_BUFLEN 512
#define BEEP_VOLUME 15 /* 0 - 100 */
static int beep_vol = BEEP_VOLUME;
static int beep_playing;
static int awacs_beep_state;
static short *beep_buf;
static void *beep_dbdma_cmd_space;
static volatile struct dbdma_cmd *beep_dbdma_cmd;
/* Burgundy functions */
static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
static unsigned awacs_burgundy_rcw(unsigned addr);
static void awacs_burgundy_write_volume(unsigned address, int volume);
static int awacs_burgundy_read_volume(unsigned address);
static void awacs_burgundy_write_mvolume(unsigned address, int volume);
static int awacs_burgundy_read_mvolume(unsigned address);
/* we will allocate a single 'emergency' dbdma cmd block to use if the
tx status comes up "DEAD". This happens on some PowerComputing Pmac
clones, either owing to a bug in dbdma or some interaction between
IDE and sound. However, this measure would deal with DEAD status if
if appeared elsewhere.
for the sake of memory efficiency we'll allocate this cmd as part of
the beep cmd stuff.
*/
static volatile struct dbdma_cmd *emergency_dbdma_cmd;
#ifdef CONFIG_PM
/*
* Stuff for restoring after a sleep.
*/
static void awacs_sleep_notify(struct pmu_sleep_notifier *self, int when);
struct pmu_sleep_notifier awacs_sleep_notifier = {
awacs_sleep_notify, SLEEP_LEVEL_SOUND,
};
#endif /* CONFIG_PM */
/* for (soft) sample rate translations */
int expand_bal; /* Balance factor for expanding (not volume!) */
int expand_read_bal; /* Balance factor for expanding reads (not volume!) */
/*** Low level stuff *********************************************************/
static void *PMacAlloc(unsigned int size, gfp_t flags);
static void PMacFree(void *ptr, unsigned int size);
static int PMacIrqInit(void);
#ifdef MODULE
static void PMacIrqCleanup(void);
#endif
static void PMacSilence(void);
static void PMacInit(void);
static int PMacSetFormat(int format);
static int PMacSetVolume(int volume);
static void PMacPlay(void);
static void PMacRecord(void);
static irqreturn_t pmac_awacs_tx_intr(int irq, void *devid);
static irqreturn_t pmac_awacs_rx_intr(int irq, void *devid);
static irqreturn_t pmac_awacs_intr(int irq, void *devid);
static void awacs_write(int val);
static int awacs_get_volume(int reg, int lshift);
static int awacs_volume_setter(int volume, int n, int mute, int lshift);
/*** Mid level stuff **********************************************************/
static int PMacMixerIoctl(u_int cmd, u_long arg);
static int PMacWriteSqSetup(void);
static int PMacReadSqSetup(void);
static void PMacAbortRead(void);
extern TRANS transAwacsNormal ;
extern TRANS transAwacsExpand ;
extern TRANS transAwacsNormalRead ;
extern TRANS transAwacsExpandRead ;
extern int daca_init(void);
extern void daca_cleanup(void);
extern int daca_set_volume(uint left_vol, uint right_vol);
extern void daca_get_volume(uint * left_vol, uint *right_vol);
extern int daca_enter_sleep(void);
extern int daca_leave_sleep(void);
#define TRY_LOCK() \
if ((rc = mutex_lock_interruptible(&dmasound_mutex)) != 0) \
return rc;
#define LOCK() mutex_lock(&dmasound_mutex);
#define UNLOCK() mutex_unlock(&dmasound_mutex);
/* We use different versions that the ones provided in dmasound.h
*
* FIXME: Use different names ;)
*/
#undef IOCTL_IN
#undef IOCTL_OUT
#define IOCTL_IN(arg, ret) \
rc = get_user(ret, (int __user *)(arg)); \
if (rc) break;
#define IOCTL_OUT(arg, ret) \
ioctl_return2((int __user *)(arg), ret)
static inline int ioctl_return2(int __user *addr, int value)
{
return value < 0 ? value : put_user(value, addr);
}
/*** AE - TUMBLER / SNAPPER START ************************************************/
int gpio_audio_reset, gpio_audio_reset_pol;
int gpio_amp_mute, gpio_amp_mute_pol;
int gpio_headphone_mute, gpio_headphone_mute_pol;
int gpio_headphone_detect, gpio_headphone_detect_pol;
int gpio_headphone_irq;
int
setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol)
{
struct device_node *gpiop;
struct device_node *np;
const u32* pp;
int ret = -ENODEV;
gpiop = of_find_node_by_name(NULL, "gpio");
if (!gpiop)
goto done;
np = of_get_next_child(gpiop, NULL);
while(np != 0) {
if (name) {
const char *property =
of_get_property(np,"audio-gpio",NULL);
if (property != 0 && strcmp(property,name) == 0)
break;
} else if (compatible && of_device_is_compatible(np, compatible))
break;
np = of_get_next_child(gpiop, np);
}
if (!np)
goto done;
pp = of_get_property(np, "AAPL,address", NULL);
if (!pp)
goto done;
*gpio_addr = (*pp) & 0x0000ffff;
pp = of_get_property(np, "audio-gpio-active-state", NULL);
if (pp)
*gpio_pol = *pp;
else
*gpio_pol = 1;
ret = irq_of_parse_and_map(np, 0);
done:
of_node_put(np);
of_node_put(gpiop);
return ret;
}
static inline void
write_audio_gpio(int gpio_addr, int data)
{
if (!gpio_addr)
return;
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_addr, data ? 0x05 : 0x04);
}
static inline int
read_audio_gpio(int gpio_addr)
{
if (!gpio_addr)
return 0;
return ((pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_addr, 0) & 0x02) !=0);
}
/*
* Headphone interrupt via GPIO (Tumbler, Snapper, DACA)
*/
static irqreturn_t
headphone_intr(int irq, void *devid)
{
unsigned long flags;
spin_lock_irqsave(&dmasound.lock, flags);
if (read_audio_gpio(gpio_headphone_detect) == gpio_headphone_detect_pol) {
printk(KERN_INFO "Audio jack plugged, muting speakers.\n");
write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
tas_output_device_change(sound_device_id,TAS_OUTPUT_HEADPHONES,0);
} else {
printk(KERN_INFO "Audio jack unplugged, enabling speakers.\n");
write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
tas_output_device_change(sound_device_id,TAS_OUTPUT_INTERNAL_SPKR,0);
}
spin_unlock_irqrestore(&dmasound.lock, flags);
return IRQ_HANDLED;
}
/* Initialize tumbler */
static int
tas_dmasound_init(void)
{
setup_audio_gpio(
"audio-hw-reset",
NULL,
&gpio_audio_reset,
&gpio_audio_reset_pol);
setup_audio_gpio(
"amp-mute",
NULL,
&gpio_amp_mute,
&gpio_amp_mute_pol);
setup_audio_gpio("headphone-mute",
NULL,
&gpio_headphone_mute,
&gpio_headphone_mute_pol);
gpio_headphone_irq = setup_audio_gpio(
"headphone-detect",
NULL,
&gpio_headphone_detect,
&gpio_headphone_detect_pol);
/* Fix some broken OF entries in desktop machines */
if (!gpio_headphone_irq)
gpio_headphone_irq = setup_audio_gpio(
NULL,
"keywest-gpio15",
&gpio_headphone_detect,
&gpio_headphone_detect_pol);
write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
msleep(100);
write_audio_gpio(gpio_audio_reset, !gpio_audio_reset_pol);
msleep(100);
if (gpio_headphone_irq) {
if (request_irq(gpio_headphone_irq,headphone_intr,0,"Headphone detect",NULL) < 0) {
printk(KERN_ERR "tumbler: Can't request headphone interrupt\n");
gpio_headphone_irq = 0;
} else {
u8 val;
/* Activate headphone status interrupts */
val = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_headphone_detect, 0);
pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_headphone_detect, val | 0x80);
/* Trigger it */
headphone_intr(0, NULL);
}
}
if (!gpio_headphone_irq) {
/* Some machine enter this case ? */
printk(KERN_WARNING "tumbler: Headphone detect IRQ not found, enabling all outputs !\n");
write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
}
return 0;
}
static int
tas_dmasound_cleanup(void)
{
if (gpio_headphone_irq)
free_irq(gpio_headphone_irq, NULL);
return 0;
}
/* We don't support 48k yet */
static int tas_freqs[1] = { 44100 } ;
static int tas_freqs_ok[1] = { 1 } ;
/* don't know what to do really - just have to leave it where
* OF left things
*/
static int
tas_set_frame_rate(void)
{
if (i2s) {
out_le32(i2s + (I2S_REG_SERIAL_FORMAT >> 2), 0x41190000);
out_le32(i2s + (I2S_REG_DATAWORD_SIZES >> 2), 0x02000200);
}
dmasound.hard.speed = 44100 ;
awacs_rate_index = 0 ;
return 44100 ;
}
static int
tas_mixer_ioctl(u_int cmd, u_long arg)
{
int __user *argp = (int __user *)arg;
int data;
int rc;
rc=tas_device_ioctl(cmd, arg);
if (rc != -EINVAL) {
return rc;
}
if ((cmd & ~0xff) == MIXER_WRITE(0) &&
tas_supported_mixers() & (1<<(cmd & 0xff))) {
rc = get_user(data, argp);
if (rc<0) return rc;
tas_set_mixer_level(cmd & 0xff, data);
tas_get_mixer_level(cmd & 0xff, &data);
return ioctl_return2(argp, data);
}
if ((cmd & ~0xff) == MIXER_READ(0) &&
tas_supported_mixers() & (1<<(cmd & 0xff))) {
tas_get_mixer_level(cmd & 0xff, &data);
return ioctl_return2(argp, data);
}
switch(cmd) {
case SOUND_MIXER_READ_DEVMASK:
data = tas_supported_mixers() | SOUND_MASK_SPEAKER;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_STEREODEVS:
data = tas_stereo_mixers();
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_CAPS:
rc = IOCTL_OUT(arg, 0);
break;
case SOUND_MIXER_READ_RECMASK:
// XXX FIXME: find a way to check what is really available */
data = SOUND_MASK_LINE | SOUND_MASK_MIC;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECSRC:
if (awacs_reg[0] & MASK_MUX_AUDIN)
data |= SOUND_MASK_LINE;
if (awacs_reg[0] & MASK_MUX_MIC)
data |= SOUND_MASK_MIC;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
data =0;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_SPEAKER: /* really bell volume */
IOCTL_IN(arg, data);
beep_vol = data & 0xff;
/* fall through */
case SOUND_MIXER_READ_SPEAKER:
rc = IOCTL_OUT(arg, (beep_vol<<8) | beep_vol);
break;
case SOUND_MIXER_OUTMASK:
case SOUND_MIXER_OUTSRC:
default:
rc = -EINVAL;
}
return rc;
}
static void __init
tas_init_frame_rates(const unsigned int *prop, unsigned int l)
{
int i ;
if (prop) {
for (i=0; i<1; i++)
tas_freqs_ok[i] = 0;
for (l /= sizeof(int); l > 0; --l) {
unsigned int r = *prop++;
/* Apple 'Fixed' format */
if (r >= 0x10000)
r >>= 16;
for (i = 0; i < 1; ++i) {
if (r == tas_freqs[i]) {
tas_freqs_ok[i] = 1;
break;
}
}
}
}
/* else we assume that all the rates are available */
}
/*** AE - TUMBLER / SNAPPER END ************************************************/
/*** Low level stuff *********************************************************/
/*
* PCI PowerMac, with AWACS, Screamer, Burgundy, DACA or Tumbler and DBDMA.
*/
static void *PMacAlloc(unsigned int size, gfp_t flags)
{
return kmalloc(size, flags);
}
static void PMacFree(void *ptr, unsigned int size)
{
kfree(ptr);
}
static int __init PMacIrqInit(void)
{
if (awacs)
if (request_irq(awacs_irq, pmac_awacs_intr, 0, "Built-in Sound misc", NULL))
return 0;
if (request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "Built-in Sound out", NULL)
|| request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "Built-in Sound in", NULL))
return 0;
return 1;
}
#ifdef MODULE
static void PMacIrqCleanup(void)
{
/* turn off input & output dma */
DBDMA_DO_STOP(awacs_txdma);
DBDMA_DO_STOP(awacs_rxdma);
if (awacs)
/* disable interrupts from awacs interface */
out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
/* Switch off the sound clock */
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
/* Make sure proper bits are set on pismo & tipb */
if ((machine_is_compatible("PowerBook3,1") ||
machine_is_compatible("PowerBook3,2")) && awacs) {
awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
msleep(200);
}
if (awacs)
free_irq(awacs_irq, NULL);
free_irq(awacs_tx_irq, NULL);
free_irq(awacs_rx_irq, NULL);
if (awacs)
iounmap(awacs);
if (i2s)
iounmap(i2s);
iounmap(awacs_txdma);
iounmap(awacs_rxdma);
release_mem_region(awacs_rsrc[0].start,
awacs_rsrc[0].end - awacs_rsrc[0].start + 1);
release_mem_region(awacs_rsrc[1].start,
awacs_rsrc[1].end - awacs_rsrc[1].start + 1);
release_mem_region(awacs_rsrc[2].start,
awacs_rsrc[2].end - awacs_rsrc[2].start + 1);
kfree(awacs_tx_cmd_space);
kfree(awacs_rx_cmd_space);
kfree(beep_dbdma_cmd_space);
kfree(beep_buf);
#ifdef CONFIG_PM
pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
#endif
}
#endif /* MODULE */
static void PMacSilence(void)
{
/* turn off output dma */
DBDMA_DO_STOP(awacs_txdma);
}
/* don't know what to do really - just have to leave it where
* OF left things
*/
static int daca_set_frame_rate(void)
{
if (i2s) {
out_le32(i2s + (I2S_REG_SERIAL_FORMAT >> 2), 0x41190000);
out_le32(i2s + (I2S_REG_DATAWORD_SIZES >> 2), 0x02000200);
}
dmasound.hard.speed = 44100 ;
awacs_rate_index = 0 ;
return 44100 ;
}
static int awacs_freqs[8] = {
44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
};
static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
static int
awacs_set_frame_rate(int desired, int catch_r)
{
int tolerance, i = 8 ;
/*
* If we have a sample rate which is within catchRadius percent
* of the requested value, we don't have to expand the samples.
* Otherwise choose the next higher rate.
* N.B.: burgundy awacs only works at 44100 Hz.
*/
do {
tolerance = catch_r * awacs_freqs[--i] / 100;
if (awacs_freqs_ok[i]
&& dmasound.soft.speed <= awacs_freqs[i] + tolerance)
break;
} while (i > 0);
dmasound.hard.speed = awacs_freqs[i];
awacs_rate_index = i;
out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 );
awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
awacs_write(awacs_reg[1] | MASK_ADDR1);
return dmasound.hard.speed;
}
static int
burgundy_set_frame_rate(void)
{
awacs_rate_index = 0 ;
awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) ;
/* XXX disable error interrupt on burgundy for now */
out_le32(&awacs->control, MASK_IEPC | 0 | 0x11 | MASK_IEE);
return 44100 ;
}
static int
set_frame_rate(int desired, int catch_r)
{
switch (awacs_revision) {
case AWACS_BURGUNDY:
dmasound.hard.speed = burgundy_set_frame_rate();
break ;
case AWACS_TUMBLER:
case AWACS_SNAPPER:
dmasound.hard.speed = tas_set_frame_rate();
break ;
case AWACS_DACA:
dmasound.hard.speed =
daca_set_frame_rate();
break ;
default:
dmasound.hard.speed = awacs_set_frame_rate(desired,
catch_r);
break ;
}
return dmasound.hard.speed ;
}
static void
awacs_recalibrate(void)
{
/* Sorry for the horrible delays... I hope to get that improved
* by making the whole PM process asynchronous in a future version
*/
msleep(750);
awacs_reg[1] |= MASK_CMUTE | MASK_AMUTE;
awacs_write(awacs_reg[1] | MASK_RECALIBRATE | MASK_ADDR1);
msleep(1000);
awacs_write(awacs_reg[1] | MASK_ADDR1);
}
static void PMacInit(void)
{
int tolerance;
switch (dmasound.soft.format) {
case AFMT_S16_LE:
case AFMT_U16_LE:
if (hw_can_byteswap)
dmasound.hard.format = AFMT_S16_LE;
else
dmasound.hard.format = AFMT_S16_BE;
break;
default:
dmasound.hard.format = AFMT_S16_BE;
break;
}
dmasound.hard.stereo = 1;
dmasound.hard.size = 16;
/* set dmasound.hard.speed - on the basis of what we want (soft)
* and the tolerance we'll allow.
*/
set_frame_rate(dmasound.soft.speed, catchRadius) ;
tolerance = (catchRadius * dmasound.hard.speed) / 100;
if (dmasound.soft.speed >= dmasound.hard.speed - tolerance) {
dmasound.trans_write = &transAwacsNormal;
dmasound.trans_read = &transAwacsNormalRead;
} else {
dmasound.trans_write = &transAwacsExpand;
dmasound.trans_read = &transAwacsExpandRead;
}
if (awacs) {
if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
out_le32(&awacs->byteswap, BS_VAL);
else
out_le32(&awacs->byteswap, 0);
}
expand_bal = -dmasound.soft.speed;
expand_read_bal = -dmasound.soft.speed;
}
static int PMacSetFormat(int format)
{
int size;
int req_format = format;
switch (format) {
case AFMT_QUERY:
return dmasound.soft.format;
case AFMT_MU_LAW:
case AFMT_A_LAW:
case AFMT_U8:
case AFMT_S8:
size = 8;
break;
case AFMT_S16_LE:
if(!hw_can_byteswap)
format = AFMT_S16_BE;
case AFMT_S16_BE:
size = 16;
break;
case AFMT_U16_LE:
if(!hw_can_byteswap)
format = AFMT_U16_BE;
case AFMT_U16_BE:
size = 16;
break;
default: /* :-) */
printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n",
format);
size = 8;
format = AFMT_U8;
}
if (req_format == format) {
dmasound.soft.format = format;
dmasound.soft.size = size;
if (dmasound.minDev == SND_DEV_DSP) {
dmasound.dsp.format = format;
dmasound.dsp.size = size;
}
}
return format;
}
#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99))
#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15))
static int awacs_get_volume(int reg, int lshift)
{
int volume;
volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf);
volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8;
return volume;
}
static int awacs_volume_setter(int volume, int n, int mute, int lshift)
{
int r1, rn;
if (mute && volume == 0) {
r1 = awacs_reg[1] | mute;
} else {
r1 = awacs_reg[1] & ~mute;
rn = awacs_reg[n] & ~(0xf | (0xf << lshift));
rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift);
rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf;
awacs_reg[n] = rn;
awacs_write((n << 12) | rn);
volume = awacs_get_volume(rn, lshift);
}
if (r1 != awacs_reg[1]) {
awacs_reg[1] = r1;
awacs_write(r1 | MASK_ADDR1);
}
return volume;
}
static int PMacSetVolume(int volume)
{
printk(KERN_WARNING "Bogus call to PMacSetVolume !\n");
return 0;
}
static void awacs_setup_for_beep(int speed)
{
out_le32(&awacs->control,
(in_le32(&awacs->control) & ~0x1f00)
| ((speed > 0 ? speed : awacs_rate_index) << 8));
if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE) && speed == -1)
out_le32(&awacs->byteswap, BS_VAL);
else
out_le32(&awacs->byteswap, 0);
}
/* CHECK: how much of this *really* needs IRQs masked? */
static void __PMacPlay(void)
{
volatile struct dbdma_cmd *cp;
int next_frg, count;
count = 300 ; /* > two cycles at the lowest sample rate */
/* what we want to send next */
next_frg = (write_sq.front + write_sq.active) % write_sq.max_count;
if (awacs_beep_state) {
/* sound takes precedence over beeps */
/* stop the dma channel */
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
while ( (in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1);
if (awacs)
awacs_setup_for_beep(-1);
out_le32(&awacs_txdma->cmdptr,
virt_to_bus(&(awacs_tx_cmds[next_frg])));
beep_playing = 0;
awacs_beep_state = 0;
}
/* this won't allow more than two frags to be in the output queue at
once. (or one, if the max frags is 2 - because count can't exceed
2 in that case)
*/
while (write_sq.active < 2 && write_sq.active < write_sq.count) {
count = (write_sq.count == write_sq.active + 1) ?
write_sq.rear_size:write_sq.block_size ;
if (count < write_sq.block_size) {
if (!write_sq.syncing) /* last block not yet filled,*/
break; /* and we're not syncing or POST-ed */
else {
/* pretend the block is full to force a new
block to be started on the next write */
write_sq.rear_size = write_sq.block_size ;
write_sq.syncing &= ~2 ; /* clear POST */
}
}
cp = &awacs_tx_cmds[next_frg];
st_le16(&cp->req_count, count);
st_le16(&cp->xfer_status, 0);
st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
/* put a STOP at the end of the queue - but only if we have
space for it. This means that, if we under-run and we only
have two fragments, we might re-play sound from an existing
queued frag. I guess the solution to that is not to set two
frags if you are likely to under-run...
*/
if (write_sq.count < write_sq.max_count) {
if (++next_frg >= write_sq.max_count)
next_frg = 0 ; /* wrap */
/* if we get here then we've underrun so we will stop*/
st_le16(&awacs_tx_cmds[next_frg].command, DBDMA_STOP);
}
/* set the dbdma controller going, if it is not already */
if (write_sq.active == 0)
out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
(void)in_le32(&awacs_txdma->status);
out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
++write_sq.active;
}
}
static void PMacPlay(void)
{
LOCK();
if (!awacs_sleeping) {
unsigned long flags;
spin_lock_irqsave(&dmasound.lock, flags);
__PMacPlay();
spin_unlock_irqrestore(&dmasound.lock, flags);
}
UNLOCK();
}
static void PMacRecord(void)
{
unsigned long flags;
if (read_sq.active)
return;
spin_lock_irqsave(&dmasound.lock, flags);
/* This is all we have to do......Just start it up.
*/
out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
read_sq.active = 1;
spin_unlock_irqrestore(&dmasound.lock, flags);
}
/* if the TX status comes up "DEAD" - reported on some Power Computing machines
we need to re-start the dbdma - but from a different physical start address
and with a different transfer length. It would get very messy to do this
with the normal dbdma_cmd blocks - we would have to re-write the buffer start
addresses each time. So, we will keep a single dbdma_cmd block which can be
fiddled with.
When DEAD status is first reported the content of the faulted dbdma block is
copied into the emergency buffer and we note that the buffer is in use.
we then bump the start physical address by the amount that was successfully
output before it died.
On any subsequent DEAD result we just do the bump-ups (we know that we are
already using the emergency dbdma_cmd).
CHECK: this just tries to "do it". It is possible that we should abandon
xfers when the number of residual bytes gets below a certain value - I can
see that this might cause a loop-forever if too small a transfer causes
DEAD status. However this is a TODO for now - we'll see what gets reported.
When we get a successful transfer result with the emergency buffer we just
pretend that it completed using the original dmdma_cmd and carry on. The
'next_cmd' field will already point back to the original loop of blocks.
*/
static irqreturn_t
pmac_awacs_tx_intr(int irq, void *devid)
{
int i = write_sq.front;
int stat;
int i_nowrap = write_sq.front;
volatile struct dbdma_cmd *cp;
/* != 0 when we are dealing with a DEAD xfer */
static int emergency_in_use;
spin_lock(&dmasound.lock);
while (write_sq.active > 0) { /* we expect to have done something*/
if (emergency_in_use) /* we are dealing with DEAD xfer */
cp = emergency_dbdma_cmd ;
else
cp = &awacs_tx_cmds[i];
stat = ld_le16(&cp->xfer_status);
if (stat & DEAD) {
unsigned short req, res ;
unsigned int phy ;
#ifdef DEBUG_DMASOUND
printk("dmasound_pmac: tx-irq: xfer died - patching it up...\n") ;
#endif
/* to clear DEAD status we must first clear RUN
set it to quiescent to be on the safe side */
(void)in_le32(&awacs_txdma->status);
out_le32(&awacs_txdma->control,
(RUN|PAUSE|FLUSH|WAKE) << 16);
write_sq.died++ ;
if (!emergency_in_use) { /* new problem */
memcpy((void *)emergency_dbdma_cmd, (void *)cp,
sizeof(struct dbdma_cmd));
emergency_in_use = 1;
cp = emergency_dbdma_cmd;
}
/* now bump the values to reflect the amount
we haven't yet shifted */
req = ld_le16(&cp->req_count);
res = ld_le16(&cp->res_count);
phy = ld_le32(&cp->phy_addr);
phy += (req - res);
st_le16(&cp->req_count, res);
st_le16(&cp->res_count, 0);
st_le16(&cp->xfer_status, 0);
st_le32(&cp->phy_addr, phy);
st_le32(&cp->cmd_dep, virt_to_bus(&awacs_tx_cmds[(i+1)%write_sq.max_count]));
st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS);
/* point at our patched up command block */
out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
/* we must re-start the controller */
(void)in_le32(&awacs_txdma->status);
/* should complete clearing the DEAD status */
out_le32(&awacs_txdma->control,
((RUN|WAKE) << 16) + (RUN|WAKE));
break; /* this block is still going */
}
if ((stat & ACTIVE) == 0)
break; /* this frame is still going */
if (emergency_in_use)
emergency_in_use = 0 ; /* done that */
--write_sq.count;
--write_sq.active;
i_nowrap++;
if (++i >= write_sq.max_count)
i = 0;
}
/* if we stopped and we were not sync-ing - then we under-ran */
if( write_sq.syncing == 0 ){
stat = in_le32(&awacs_txdma->status) ;
/* we hit the dbdma_stop */
if( (stat & ACTIVE) == 0 ) write_sq.xruns++ ;
}
/* if we used some data up then wake the writer to supply some more*/
if (i_nowrap != write_sq.front)
WAKE_UP(write_sq.action_queue);
write_sq.front = i;
/* but make sure we funnel what we've already got */\
if (!awacs_sleeping)
__PMacPlay();
/* make the wake-on-empty conditional on syncing */
if (!write_sq.active && (write_sq.syncing & 1))
WAKE_UP(write_sq.sync_queue); /* any time we're empty */
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static irqreturn_t
pmac_awacs_rx_intr(int irq, void *devid)
{
int stat ;
/* For some reason on my PowerBook G3, I get one interrupt
* when the interrupt vector is installed (like something is
* pending). This happens before the dbdma is initialized by
* us, so I just check the command pointer and if it is zero,
* just blow it off.
*/
if (in_le32(&awacs_rxdma->cmdptr) == 0)
return IRQ_HANDLED;
/* We also want to blow 'em off when shutting down.
*/
if (read_sq.active == 0)
return IRQ_HANDLED;
spin_lock(&dmasound.lock);
/* Check multiple buffers in case we were held off from
* interrupt processing for a long time. Geeze, I really hope
* this doesn't happen.
*/
while ((stat=awacs_rx_cmds[read_sq.rear].xfer_status)) {
/* if we got a "DEAD" status then just log it for now.
and try to restart dma.
TODO: figure out how best to fix it up
*/
if (stat & DEAD){
#ifdef DEBUG_DMASOUND
printk("dmasound_pmac: rx-irq: DIED - attempting resurection\n");
#endif
/* to clear DEAD status we must first clear RUN
set it to quiescent to be on the safe side */
(void)in_le32(&awacs_txdma->status);
out_le32(&awacs_txdma->control,
(RUN|PAUSE|FLUSH|WAKE) << 16);
awacs_rx_cmds[read_sq.rear].xfer_status = 0;
awacs_rx_cmds[read_sq.rear].res_count = 0;
read_sq.died++ ;
(void)in_le32(&awacs_txdma->status);
/* re-start the same block */
out_le32(&awacs_rxdma->cmdptr,
virt_to_bus(&awacs_rx_cmds[read_sq.rear]));
/* we must re-start the controller */
(void)in_le32(&awacs_rxdma->status);
/* should complete clearing the DEAD status */
out_le32(&awacs_rxdma->control,
((RUN|WAKE) << 16) + (RUN|WAKE));
spin_unlock(&dmasound.lock);
return IRQ_HANDLED; /* try this block again */
}
/* Clear status and move on to next buffer.
*/
awacs_rx_cmds[read_sq.rear].xfer_status = 0;
read_sq.rear++;
/* Wrap the buffer ring.
*/
if (read_sq.rear >= read_sq.max_active)
read_sq.rear = 0;
/* If we have caught up to the front buffer, bump it.
* This will cause weird (but not fatal) results if the
* read loop is currently using this buffer. The user is
* behind in this case anyway, so weird things are going
* to happen.
*/
if (read_sq.rear == read_sq.front) {
read_sq.front++;
read_sq.xruns++ ; /* we overan */
if (read_sq.front >= read_sq.max_active)
read_sq.front = 0;
}
}
WAKE_UP(read_sq.action_queue);
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static irqreturn_t
pmac_awacs_intr(int irq, void *devid)
{
int ctrl;
int status;
int r1;
spin_lock(&dmasound.lock);
ctrl = in_le32(&awacs->control);
status = in_le32(&awacs->codec_stat);
if (ctrl & MASK_PORTCHG) {
/* tested on Screamer, should work on others too */
if (awacs_revision == AWACS_SCREAMER) {
if (((status & MASK_HDPCONN) >> 3) && (hdp_connected == 0)) {
hdp_connected = 1;
r1 = awacs_reg[1] | MASK_SPKMUTE;
awacs_reg[1] = r1;
awacs_write(r1 | MASK_ADDR_MUTE);
} else if (((status & MASK_HDPCONN) >> 3 == 0) && (hdp_connected == 1)) {
hdp_connected = 0;
r1 = awacs_reg[1] & ~MASK_SPKMUTE;
awacs_reg[1] = r1;
awacs_write(r1 | MASK_ADDR_MUTE);
}
}
}
if (ctrl & MASK_CNTLERR) {
int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
/* CHECK: we just swallow burgundy errors at the moment..*/
if (err != 0 && awacs_revision != AWACS_BURGUNDY)
printk(KERN_ERR "dmasound_pmac: error %x\n", err);
}
/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
out_le32(&awacs->control, ctrl);
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static void
awacs_write(int val)
{
int count = 300 ;
if (awacs_revision >= AWACS_DACA || !awacs)
return ;
while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
udelay(1) ; /* timeout is > 2 samples at lowest rate */
out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
(void)in_le32(&awacs->byteswap);
}
/* this is called when the beep timer expires... it will be called even
if the beep has been overidden by other sound output.
*/
static void awacs_nosound(unsigned long xx)
{
unsigned long flags;
int count = 600 ; /* > four samples at lowest rate */
spin_lock_irqsave(&dmasound.lock, flags);
if (beep_playing) {
st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
while ((in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1);
if (awacs)
awacs_setup_for_beep(-1);
beep_playing = 0;
}
spin_unlock_irqrestore(&dmasound.lock, flags);
}
/*
* We generate the beep with a single dbdma command that loops a buffer
* forever - without generating interrupts.
*
* So, to stop it you have to stop dma output as per awacs_nosound.
*/
static int awacs_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
unsigned long flags;
int beep_speed = 0;
int srate;
int period, ncycles, nsamples;
int i, j, f;
short *p;
static int beep_hz_cache;
static int beep_nsamples_cache;
static int beep_volume_cache;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL:
if (hz)
hz = 1000;
break;
case SND_TONE:
break;
default:
return -1;
}
if (beep_buf == NULL)
return -1;
/* quick-hack fix for DACA, Burgundy & Tumbler */
if (awacs_revision >= AWACS_DACA){
srate = 44100 ;
} else {
for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
if (awacs_freqs_ok[i])
beep_speed = i;
srate = awacs_freqs[beep_speed];
}
if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
/* cancel beep currently playing */
awacs_nosound(0);
return 0;
}
spin_lock_irqsave(&dmasound.lock, flags);
if (beep_playing || write_sq.active || beep_buf == NULL) {
spin_unlock_irqrestore(&dmasound.lock, flags);
return -1; /* too hard, sorry :-( */
}
beep_playing = 1;
st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
spin_unlock_irqrestore(&dmasound.lock, flags);
if (hz == beep_hz_cache && beep_vol == beep_volume_cache) {
nsamples = beep_nsamples_cache;
} else {
period = srate * 256 / hz; /* fixed point */
ncycles = BEEP_BUFLEN * 256 / period;
nsamples = (period * ncycles) >> 8;
f = ncycles * 65536 / nsamples;
j = 0;
p = beep_buf;
for (i = 0; i < nsamples; ++i, p += 2) {
p[0] = p[1] = beep_wform[j >> 8] * beep_vol;
j = (j + f) & 0xffff;
}
beep_hz_cache = hz;
beep_volume_cache = beep_vol;
beep_nsamples_cache = nsamples;
}
st_le16(&beep_dbdma_cmd->req_count, nsamples*4);
st_le16(&beep_dbdma_cmd->xfer_status, 0);
st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd));
st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf));
awacs_beep_state = 1;
spin_lock_irqsave(&dmasound.lock, flags);
if (beep_playing) { /* i.e. haven't been terminated already */
int count = 300 ;
out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
while ((in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1); /* timeout > 2 samples at lowest rate*/
if (awacs)
awacs_setup_for_beep(beep_speed);
out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
(void)in_le32(&awacs_txdma->status);
out_le32(&awacs_txdma->control, RUN | (RUN << 16));
}
spin_unlock_irqrestore(&dmasound.lock, flags);
return 0;
}
/* used in init and for wake-up */
static void
load_awacs(void)
{
awacs_write(awacs_reg[0] + MASK_ADDR0);
awacs_write(awacs_reg[1] + MASK_ADDR1);
awacs_write(awacs_reg[2] + MASK_ADDR2);
awacs_write(awacs_reg[4] + MASK_ADDR4);
if (awacs_revision == AWACS_SCREAMER) {
awacs_write(awacs_reg[5] + MASK_ADDR5);
msleep(100);
awacs_write(awacs_reg[6] + MASK_ADDR6);
msleep(2);
awacs_write(awacs_reg[1] + MASK_ADDR1);
awacs_write(awacs_reg[7] + MASK_ADDR7);
}
if (awacs) {
if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
out_le32(&awacs->byteswap, BS_VAL);
else
out_le32(&awacs->byteswap, 0);
}
}
#ifdef CONFIG_PM
/*
* Save state when going to sleep, restore it afterwards.
*/
/* FIXME: sort out disabling/re-enabling of read stuff as well */
static void awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
unsigned long flags;
switch (when) {
case PBOOK_SLEEP_NOW:
LOCK();
awacs_sleeping = 1;
/* Tell the rest of the driver we are now going to sleep */
mb();
if (awacs_revision == AWACS_SCREAMER ||
awacs_revision == AWACS_AWACS) {
awacs_reg1_save = awacs_reg[1];
awacs_reg[1] |= MASK_AMUTE | MASK_CMUTE;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
}
PMacSilence();
/* stop rx - if going - a bit of a daft user... but */
out_le32(&awacs_rxdma->control, (RUN|WAKE|FLUSH << 16));
/* deny interrupts */
if (awacs)
disable_irq(awacs_irq);
disable_irq(awacs_tx_irq);
disable_irq(awacs_rx_irq);
/* Chip specific sleep code */
switch (awacs_revision) {
case AWACS_TUMBLER:
case AWACS_SNAPPER:
write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
tas_enter_sleep();
write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
break ;
case AWACS_DACA:
daca_enter_sleep();
break ;
case AWACS_BURGUNDY:
break ;
case AWACS_SCREAMER:
case AWACS_AWACS:
default:
out_le32(&awacs->control, 0x11) ;
break ;
}
/* Disable sound clock */
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
/* According to Darwin, we do that after turning off the sound
* chip clock. All this will have to be cleaned up once we properly
* parse the OF sound-objects
*/
if ((machine_is_compatible("PowerBook3,1") ||
machine_is_compatible("PowerBook3,2")) && awacs) {
awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
msleep(200);
}
break;
case PBOOK_WAKE:
/* Enable sound clock */
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 1);
if ((machine_is_compatible("PowerBook3,1") ||
machine_is_compatible("PowerBook3,2")) && awacs) {
msleep(100);
awacs_reg[1] &= ~(MASK_PAROUT0 | MASK_PAROUT1);
awacs_write(MASK_ADDR1 | awacs_reg[1]);
msleep(300);
} else
msleep(1000);
/* restore settings */
switch (awacs_revision) {
case AWACS_TUMBLER:
case AWACS_SNAPPER:
write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
msleep(100);
write_audio_gpio(gpio_audio_reset, !gpio_audio_reset_pol);
msleep(150);
tas_leave_sleep(); /* Stub for now */
headphone_intr(0, NULL);
break;
case AWACS_DACA:
msleep(10); /* Check this !!! */
daca_leave_sleep();
break ; /* dont know how yet */
case AWACS_BURGUNDY:
break ;
case AWACS_SCREAMER:
case AWACS_AWACS:
default:
load_awacs() ;
break ;
}
/* Recalibrate chip */
if (awacs_revision == AWACS_SCREAMER && awacs)
awacs_recalibrate();
/* Make sure dma is stopped */
PMacSilence();
if (awacs)
enable_irq(awacs_irq);
enable_irq(awacs_tx_irq);
enable_irq(awacs_rx_irq);
if (awacs) {
/* OK, allow ints back again */
out_le32(&awacs->control, MASK_IEPC
| (awacs_rate_index << 8) | 0x11
| (awacs_revision < AWACS_DACA ? MASK_IEE: 0));
}
if (macio_base && is_pbook_g3) {
/* FIXME: should restore the setup we had...*/
out_8(macio_base + 0x37, 3);
} else if (is_pbook_3X00) {
in_8(latch_base + 0x190);
}
/* Remove mute */
if (awacs_revision == AWACS_SCREAMER ||
awacs_revision == AWACS_AWACS) {
awacs_reg[1] = awacs_reg1_save;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
}
awacs_sleeping = 0;
/* Resume pending sounds. */
/* we don't try to restart input... */
spin_lock_irqsave(&dmasound.lock, flags);
__PMacPlay();
spin_unlock_irqrestore(&dmasound.lock, flags);
UNLOCK();
}
}
#endif /* CONFIG_PM */
/* All the burgundy functions: */
/* Waits for busy flag to clear */
static inline void
awacs_burgundy_busy_wait(void)
{
int count = 50; /* > 2 samples at 44k1 */
while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
udelay(1) ;
}
static inline void
awacs_burgundy_extend_wait(void)
{
int count = 50 ; /* > 2 samples at 44k1 */
while ((!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) && count--)
udelay(1) ;
count = 50;
while ((in_le32(&awacs->codec_stat) & MASK_EXTEND) && count--)
udelay(1);
}
static void
awacs_burgundy_wcw(unsigned addr, unsigned val)
{
out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
awacs_burgundy_busy_wait();
out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
awacs_burgundy_busy_wait();
out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
awacs_burgundy_busy_wait();
out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
awacs_burgundy_busy_wait();
}
static unsigned
awacs_burgundy_rcw(unsigned addr)
{
unsigned val = 0;
unsigned long flags;
/* should have timeouts here */
spin_lock_irqsave(&dmasound.lock, flags);
out_le32(&awacs->codec_ctrl, addr + 0x100000);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
out_le32(&awacs->codec_ctrl, addr + 0x100100);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8;
out_le32(&awacs->codec_ctrl, addr + 0x100200);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16;
out_le32(&awacs->codec_ctrl, addr + 0x100300);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24;
spin_unlock_irqrestore(&dmasound.lock, flags);
return val;
}
static void
awacs_burgundy_wcb(unsigned addr, unsigned val)
{
out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
awacs_burgundy_busy_wait();
}
static unsigned
awacs_burgundy_rcb(unsigned addr)
{
unsigned val = 0;
unsigned long flags;
/* should have timeouts here */
spin_lock_irqsave(&dmasound.lock, flags);
out_le32(&awacs->codec_ctrl, addr + 0x100000);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
spin_unlock_irqrestore(&dmasound.lock, flags);
return val;
}
static int
awacs_burgundy_check(void)
{
/* Checks to see the chip is alive and kicking */
int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE;
return error == 0xf0000;
}
static int
awacs_burgundy_init(void)
{
if (awacs_burgundy_check()) {
printk(KERN_WARNING "dmasound_pmac: burgundy not working :-(\n");
return 1;
}
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES,
DEF_BURGUNDY_OUTPUTENABLES);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
DEF_BURGUNDY_MORE_OUTPUTENABLES);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
DEF_BURGUNDY_OUTPUTSELECTS);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21,
DEF_BURGUNDY_INPSEL21);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3,
DEF_BURGUNDY_INPSEL3);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD,
DEF_BURGUNDY_GAINCD);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE,
DEF_BURGUNDY_GAINLINE);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC,
DEF_BURGUNDY_GAINMIC);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM,
DEF_BURGUNDY_GAINMODEM);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER,
DEF_BURGUNDY_ATTENSPEAKER);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT,
DEF_BURGUNDY_ATTENLINEOUT);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP,
DEF_BURGUNDY_ATTENHP);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME,
DEF_BURGUNDY_MASTER_VOLUME);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD,
DEF_BURGUNDY_VOLCD);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE,
DEF_BURGUNDY_VOLLINE);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC,
DEF_BURGUNDY_VOLMIC);
return 0;
}
static void
awacs_burgundy_write_volume(unsigned address, int volume)
{
int hardvolume,lvolume,rvolume;
lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0;
rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0;
hardvolume = lvolume + (rvolume << 16);
awacs_burgundy_wcw(address, hardvolume);
}
static int
awacs_burgundy_read_volume(unsigned address)
{
int softvolume,wvolume;
wvolume = awacs_burgundy_rcw(address);
softvolume = (wvolume & 0xff) - 155;
softvolume += (((wvolume >> 16) & 0xff) - 155)<<8;
return softvolume > 0 ? softvolume : 0;
}
static int
awacs_burgundy_read_mvolume(unsigned address)
{
int lvolume,rvolume,wvolume;
wvolume = awacs_burgundy_rcw(address);
wvolume &= 0xffff;
rvolume = (wvolume & 0xff) - 155;
lvolume = ((wvolume & 0xff00)>>8) - 155;
return lvolume + (rvolume << 8);
}
static void
awacs_burgundy_write_mvolume(unsigned address, int volume)
{
int lvolume,rvolume,hardvolume;
lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0;
rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0;
hardvolume = lvolume + (rvolume << 8);
hardvolume += (hardvolume << 16);
awacs_burgundy_wcw(address, hardvolume);
}
/* End burgundy functions */
/* Set up output volumes on machines with the 'perch/whisper' extension card.
* this has an SGS i2c chip (7433) which is accessed using the cuda.
*
* TODO: split this out and make use of the other parts of the SGS chip to
* do Bass, Treble etc.
*/
static void
awacs_enable_amp(int spkr_vol)
{
#ifdef CONFIG_ADB_CUDA
struct adb_request req;
if (sys_ctrler != SYS_CTRLER_CUDA)
return;
/* turn on headphones */
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 4, 0);
while (!req.complete) cuda_poll();
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 6, 0);
while (!req.complete) cuda_poll();
/* turn on speaker */
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100);
while (!req.complete) cuda_poll();
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100);
while (!req.complete) cuda_poll();
cuda_request(&req, NULL, 5, CUDA_PACKET,
CUDA_GET_SET_IIC, 0x8a, 1, 0x29);
while (!req.complete) cuda_poll();
#endif /* CONFIG_ADB_CUDA */
}
/*** Mid level stuff *********************************************************/
/*
* /dev/mixer abstraction
*/
static void do_line_lev(int data)
{
line_lev = data ;
awacs_reg[0] &= ~MASK_MUX_AUDIN;
if ((data & 0xff) >= 50)
awacs_reg[0] |= MASK_MUX_AUDIN;
awacs_write(MASK_ADDR0 | awacs_reg[0]);
}
static void do_ip_gain(int data)
{
ip_gain = data ;
data &= 0xff;
awacs_reg[0] &= ~MASK_GAINLINE;
if (awacs_revision == AWACS_SCREAMER) {
awacs_reg[6] &= ~MASK_MIC_BOOST ;
if (data >= 33) {
awacs_reg[0] |= MASK_GAINLINE;
if( data >= 66)
awacs_reg[6] |= MASK_MIC_BOOST ;
}
awacs_write(MASK_ADDR6 | awacs_reg[6]) ;
} else {
if (data >= 50)
awacs_reg[0] |= MASK_GAINLINE;
}
awacs_write(MASK_ADDR0 | awacs_reg[0]);
}
static void do_mic_lev(int data)
{
mic_lev = data ;
data &= 0xff;
awacs_reg[0] &= ~MASK_MUX_MIC;
if (data >= 50)
awacs_reg[0] |= MASK_MUX_MIC;
awacs_write(MASK_ADDR0 | awacs_reg[0]);
}
static void do_cd_lev(int data)
{
cd_lev = data ;
awacs_reg[0] &= ~MASK_MUX_CD;
if ((data & 0xff) >= 50)
awacs_reg[0] |= MASK_MUX_CD;
awacs_write(MASK_ADDR0 | awacs_reg[0]);
}
static void do_rec_lev(int data)
{
int left, right ;
rec_lev = data ;
/* need to fudge this to use the volume setter routine */
left = 100 - (data & 0xff) ; if( left < 0 ) left = 0 ;
right = 100 - ((data >> 8) & 0xff) ; if( right < 0 ) right = 0 ;
left |= (right << 8 );
left = awacs_volume_setter(left, 0, 0, 4);
}
static void do_passthru_vol(int data)
{
passthru_vol = data ;
awacs_reg[1] &= ~MASK_LOOPTHRU;
if (awacs_revision == AWACS_SCREAMER) {
if( data ) { /* switch it on for non-zero */
awacs_reg[1] |= MASK_LOOPTHRU;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
}
data = awacs_volume_setter(data, 5, 0, 6) ;
} else {
if ((data & 0xff) >= 50)
awacs_reg[1] |= MASK_LOOPTHRU;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
}
}
static int awacs_mixer_ioctl(u_int cmd, u_long arg)
{
int data;
int rc;
switch (cmd) {
case SOUND_MIXER_READ_CAPS:
/* say we will allow multiple inputs? prob. wrong
so I'm switching it to single */
return IOCTL_OUT(arg, 1);
case SOUND_MIXER_READ_DEVMASK:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
| SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD
| SOUND_MASK_IGAIN | SOUND_MASK_RECLEV
| SOUND_MASK_ALTPCM
| SOUND_MASK_MONITOR;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECMASK:
data = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECSRC:
data = 0;
if (awacs_reg[0] & MASK_MUX_AUDIN)
data |= SOUND_MASK_LINE;
if (awacs_reg[0] & MASK_MUX_MIC)
data |= SOUND_MASK_MIC;
if (awacs_reg[0] & MASK_MUX_CD)
data |= SOUND_MASK_CD;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
data &= (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD);
awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
| MASK_MUX_AUDIN);
if (data & SOUND_MASK_LINE)
awacs_reg[0] |= MASK_MUX_AUDIN;
if (data & SOUND_MASK_MIC)
awacs_reg[0] |= MASK_MUX_MIC;
if (data & SOUND_MASK_CD)
awacs_reg[0] |= MASK_MUX_CD;
awacs_write(awacs_reg[0] | MASK_ADDR0);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_STEREODEVS:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER| SOUND_MASK_RECLEV ;
if (awacs_revision == AWACS_SCREAMER)
data |= SOUND_MASK_MONITOR ;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
line_vol = data ;
awacs_volume_setter(data, 2, 0, 6);
/* fall through */
case SOUND_MIXER_READ_VOLUME:
rc = IOCTL_OUT(arg, line_vol);
break;
case SOUND_MIXER_WRITE_SPEAKER:
IOCTL_IN(arg, data);
spk_vol = data ;
if (has_perch)
awacs_enable_amp(data);
else
(void)awacs_volume_setter(data, 4, MASK_CMUTE, 6);
/* fall though */
case SOUND_MIXER_READ_SPEAKER:
rc = IOCTL_OUT(arg, spk_vol);
break;
case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
IOCTL_IN(arg, data);
beep_vol = data & 0xff;
/* fall through */
case SOUND_MIXER_READ_ALTPCM:
rc = IOCTL_OUT(arg, beep_vol);
break;
case SOUND_MIXER_WRITE_LINE:
IOCTL_IN(arg, data);
do_line_lev(data) ;
/* fall through */
case SOUND_MIXER_READ_LINE:
rc = IOCTL_OUT(arg, line_lev);
break;
case SOUND_MIXER_WRITE_IGAIN:
IOCTL_IN(arg, data);
do_ip_gain(data) ;
/* fall through */
case SOUND_MIXER_READ_IGAIN:
rc = IOCTL_OUT(arg, ip_gain);
break;
case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data);
do_mic_lev(data);
/* fall through */
case SOUND_MIXER_READ_MIC:
rc = IOCTL_OUT(arg, mic_lev);
break;
case SOUND_MIXER_WRITE_CD:
IOCTL_IN(arg, data);
do_cd_lev(data);
/* fall through */
case SOUND_MIXER_READ_CD:
rc = IOCTL_OUT(arg, cd_lev);
break;
case SOUND_MIXER_WRITE_RECLEV:
IOCTL_IN(arg, data);
do_rec_lev(data) ;
/* fall through */
case SOUND_MIXER_READ_RECLEV:
rc = IOCTL_OUT(arg, rec_lev);
break;
case MIXER_WRITE(SOUND_MIXER_MONITOR):
IOCTL_IN(arg, data);
do_passthru_vol(data) ;
/* fall through */
case MIXER_READ(SOUND_MIXER_MONITOR):
rc = IOCTL_OUT(arg, passthru_vol);
break;
default:
rc = -EINVAL;
}
return rc;
}
static void awacs_mixer_init(void)
{
awacs_volume_setter(line_vol, 2, 0, 6);
if (has_perch)
awacs_enable_amp(spk_vol);
else
(void)awacs_volume_setter(spk_vol, 4, MASK_CMUTE, 6);
do_line_lev(line_lev) ;
do_ip_gain(ip_gain) ;
do_mic_lev(mic_lev) ;
do_cd_lev(cd_lev) ;
do_rec_lev(rec_lev) ;
do_passthru_vol(passthru_vol) ;
}
static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
{
int data;
int rc;
/* We are, we are, we are... Burgundy or better */
switch(cmd) {
case SOUND_MIXER_READ_DEVMASK:
data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
SOUND_MASK_LINE | SOUND_MASK_MIC |
SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECMASK:
data = SOUND_MASK_LINE | SOUND_MASK_MIC
| SOUND_MASK_CD;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECSRC:
data = 0;
if (awacs_reg[0] & MASK_MUX_AUDIN)
data |= SOUND_MASK_LINE;
if (awacs_reg[0] & MASK_MUX_MIC)
data |= SOUND_MASK_MIC;
if (awacs_reg[0] & MASK_MUX_CD)
data |= SOUND_MASK_CD;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
data &= (SOUND_MASK_LINE
| SOUND_MASK_MIC | SOUND_MASK_CD);
awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
| MASK_MUX_AUDIN);
if (data & SOUND_MASK_LINE)
awacs_reg[0] |= MASK_MUX_AUDIN;
if (data & SOUND_MASK_MIC)
awacs_reg[0] |= MASK_MUX_MIC;
if (data & SOUND_MASK_CD)
awacs_reg[0] |= MASK_MUX_CD;
awacs_write(awacs_reg[0] | MASK_ADDR0);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_STEREODEVS:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
| SOUND_MASK_RECLEV | SOUND_MASK_CD
| SOUND_MASK_LINE;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_CAPS:
rc = IOCTL_OUT(arg, 0);
break;
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data);
/* Fall through */
case SOUND_MIXER_READ_VOLUME:
rc = IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
break;
case SOUND_MIXER_WRITE_SPEAKER:
IOCTL_IN(arg, data);
if (!(data & 0xff)) {
/* Mute the left speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2);
} else {
/* Unmute the left speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2);
}
if (!(data & 0xff00)) {
/* Mute the right speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4);
} else {
/* Unmute the right speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4);
}
data = (((data&0xff)*16)/100 > 0xf ? 0xf :
(((data&0xff)*16)/100)) +
((((data>>8)*16)/100 > 0xf ? 0xf :
((((data>>8)*16)/100)))<<4);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data);
/* Fall through */
case SOUND_MIXER_READ_SPEAKER:
data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
rc = IOCTL_OUT(arg, (~data) & 0x0000ffff);
break;
case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
IOCTL_IN(arg, data);
beep_vol = data & 0xff;
/* fall through */
case SOUND_MIXER_READ_ALTPCM:
rc = IOCTL_OUT(arg, beep_vol);
break;
case SOUND_MIXER_WRITE_LINE:
IOCTL_IN(arg, data);
awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);
/* fall through */
case SOUND_MIXER_READ_LINE:
data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data);
/* Mic is mono device */
data = (data << 8) + (data << 24);
awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
/* fall through */
case SOUND_MIXER_READ_MIC:
data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);
data <<= 24;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_CD:
IOCTL_IN(arg, data);
awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
/* fall through */
case SOUND_MIXER_READ_CD:
data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_RECLEV:
IOCTL_IN(arg, data);
data = awacs_volume_setter(data, 0, 0, 4);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECLEV:
data = awacs_get_volume(awacs_reg[0], 4);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_OUTMASK:
case SOUND_MIXER_OUTSRC:
default:
rc = -EINVAL;
}
return rc;
}
static int daca_mixer_ioctl(u_int cmd, u_long arg)
{
int data;
int rc;
/* And the DACA's no genius either! */
switch(cmd) {
case SOUND_MIXER_READ_DEVMASK:
data = SOUND_MASK_VOLUME;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECMASK:
data = 0;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_RECSRC:
data = 0;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
data =0;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_STEREODEVS:
data = SOUND_MASK_VOLUME;
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_READ_CAPS:
rc = IOCTL_OUT(arg, 0);
break;
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
daca_set_volume(data, data);
/* Fall through */
case SOUND_MIXER_READ_VOLUME:
daca_get_volume(& data, &data);
rc = IOCTL_OUT(arg, data);
break;
case SOUND_MIXER_OUTMASK:
case SOUND_MIXER_OUTSRC:
default:
rc = -EINVAL;
}
return rc;
}
static int PMacMixerIoctl(u_int cmd, u_long arg)
{
int rc;
/* Different IOCTLS for burgundy and, eventually, DACA & Tumbler */
TRY_LOCK();
switch (awacs_revision){
case AWACS_BURGUNDY:
rc = burgundy_mixer_ioctl(cmd, arg);
break ;
case AWACS_DACA:
rc = daca_mixer_ioctl(cmd, arg);
break;
case AWACS_TUMBLER:
case AWACS_SNAPPER:
rc = tas_mixer_ioctl(cmd, arg);
break ;
default: /* ;-)) */
rc = awacs_mixer_ioctl(cmd, arg);
}
UNLOCK();
return rc;
}
static void PMacMixerInit(void)
{
switch (awacs_revision) {
case AWACS_TUMBLER:
printk("AE-Init tumbler mixer\n");
break ;
case AWACS_SNAPPER:
printk("AE-Init snapper mixer\n");
break ;
case AWACS_DACA:
case AWACS_BURGUNDY:
break ; /* don't know yet */
case AWACS_AWACS:
case AWACS_SCREAMER:
default:
awacs_mixer_init() ;
break ;
}
}
/* Write/Read sq setup functions:
Check to see if we have enough (or any) dbdma cmd buffers for the
user's fragment settings. If not, allocate some. If this fails we will
point at the beep buffer - as an emergency provision - to stop dma tromping
on some random bit of memory (if someone lets it go anyway).
The command buffers are then set up to point to the fragment buffers
(allocated elsewhere). We need n+1 commands the last of which holds
a NOP + loop to start.
*/
static int PMacWriteSqSetup(void)
{
int i, count = 600 ;
volatile struct dbdma_cmd *cp;
LOCK();
/* stop the controller from doing any output - if it isn't already.
it _should_ be before this is called anyway */
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
while ((in_le32(&awacs_txdma->status) & RUN) && count--)
udelay(1);
#ifdef DEBUG_DMASOUND
if (count <= 0)
printk("dmasound_pmac: write sq setup: timeout waiting for dma to stop\n");
#endif
if ((write_sq.max_count + 1) > number_of_tx_cmd_buffers) {
kfree(awacs_tx_cmd_space);
number_of_tx_cmd_buffers = 0;
/* we need nbufs + 1 (for the loop) and we should request + 1
again because the DBDMA_ALIGN might pull the start up by up
to sizeof(struct dbdma_cmd) - 4.
*/
awacs_tx_cmd_space = kmalloc
((write_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
GFP_KERNEL);
if (awacs_tx_cmd_space == NULL) {
/* don't leave it dangling - nasty but better than a
random address */
out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
printk(KERN_ERR
"dmasound_pmac: can't allocate dbdma cmd buffers"
", driver disabled\n");
UNLOCK();
return -ENOMEM;
}
awacs_tx_cmds = (volatile struct dbdma_cmd *)
DBDMA_ALIGN(awacs_tx_cmd_space);
number_of_tx_cmd_buffers = write_sq.max_count + 1;
}
cp = awacs_tx_cmds;
memset((void *)cp, 0, (write_sq.max_count+1) * sizeof(struct dbdma_cmd));
for (i = 0; i < write_sq.max_count; ++i, ++cp) {
st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i]));
}
st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
/* point the controller at the command stack - ready to go */
out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
UNLOCK();
return 0;
}
static int PMacReadSqSetup(void)
{
int i, count = 600;
volatile struct dbdma_cmd *cp;
LOCK();
/* stop the controller from doing any input - if it isn't already.
it _should_ be before this is called anyway */
out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
while ((in_le32(&awacs_rxdma->status) & RUN) && count--)
udelay(1);
#ifdef DEBUG_DMASOUND
if (count <= 0)
printk("dmasound_pmac: read sq setup: timeout waiting for dma to stop\n");
#endif
if ((read_sq.max_count+1) > number_of_rx_cmd_buffers ) {
kfree(awacs_rx_cmd_space);
number_of_rx_cmd_buffers = 0;
/* we need nbufs + 1 (for the loop) and we should request + 1 again
because the DBDMA_ALIGN might pull the start up by up to
sizeof(struct dbdma_cmd) - 4 (assuming kmalloc aligns 32 bits).
*/
awacs_rx_cmd_space = kmalloc
((read_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
GFP_KERNEL);
if (awacs_rx_cmd_space == NULL) {
/* don't leave it dangling - nasty but better than a
random address */
out_le32(&awacs_rxdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
printk(KERN_ERR
"dmasound_pmac: can't allocate dbdma cmd buffers"
", driver disabled\n");
UNLOCK();
return -ENOMEM;
}
awacs_rx_cmds = (volatile struct dbdma_cmd *)
DBDMA_ALIGN(awacs_rx_cmd_space);
number_of_rx_cmd_buffers = read_sq.max_count + 1 ;
}
cp = awacs_rx_cmds;
memset((void *)cp, 0, (read_sq.max_count+1) * sizeof(struct dbdma_cmd));
/* Set dma buffers up in a loop */
for (i = 0; i < read_sq.max_count; i++,cp++) {
st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i]));
st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
st_le16(&cp->req_count, read_sq.block_size);
st_le16(&cp->xfer_status, 0);
}
/* The next two lines make the thing loop around.
*/
st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));
/* point the controller at the command stack - ready to go */
out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
UNLOCK();
return 0;
}
/* TODO: this needs work to guarantee that when it returns DMA has stopped
but in a more elegant way than is done here....
*/
static void PMacAbortRead(void)
{
int i;
volatile struct dbdma_cmd *cp;
LOCK();
/* give it a chance to update the output and provide the IRQ
that is expected.
*/
out_le32(&awacs_rxdma->control, ((FLUSH) << 16) + FLUSH );
cp = awacs_rx_cmds;
for (i = 0; i < read_sq.max_count; i++,cp++)
st_le16(&cp->command, DBDMA_STOP);
/*
* We should probably wait for the thing to stop before we
* release the memory.
*/
msleep(100) ; /* give it a (small) chance to act */
/* apply the sledgehammer approach - just stop it now */
out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
UNLOCK();
}
extern char *get_afmt_string(int);
static int PMacStateInfo(char *b, size_t sp)
{
int i, len = 0;
len = sprintf(b,"HW rates: ");
switch (awacs_revision){
case AWACS_DACA:
case AWACS_BURGUNDY:
len += sprintf(b,"44100 ") ;
break ;
case AWACS_TUMBLER:
case AWACS_SNAPPER:
for (i=0; i<1; i++){
if (tas_freqs_ok[i])
len += sprintf(b+len,"%d ", tas_freqs[i]) ;
}
break ;
case AWACS_AWACS:
case AWACS_SCREAMER:
default:
for (i=0; i<8; i++){
if (awacs_freqs_ok[i])
len += sprintf(b+len,"%d ", awacs_freqs[i]) ;
}
break ;
}
len += sprintf(b+len,"s/sec\n") ;
if (len < sp) {
len += sprintf(b+len,"HW AFMTS: ");
i = AFMT_U16_BE ;
while (i) {
if (i & dmasound.mach.hardware_afmts)
len += sprintf(b+len,"%s ",
get_afmt_string(i & dmasound.mach.hardware_afmts));
i >>= 1 ;
}
len += sprintf(b+len,"\n") ;
}
return len ;
}
/*** Machine definitions *****************************************************/
static SETTINGS def_hard = {
.format = AFMT_S16_BE,
.stereo = 1,
.size = 16,
.speed = 44100
} ;
static SETTINGS def_soft = {
.format = AFMT_S16_BE,
.stereo = 1,
.size = 16,
.speed = 44100
} ;
static MACHINE machPMac = {
.name = awacs_name,
.name2 = "PowerMac Built-in Sound",
.owner = THIS_MODULE,
.dma_alloc = PMacAlloc,
.dma_free = PMacFree,
.irqinit = PMacIrqInit,
#ifdef MODULE
.irqcleanup = PMacIrqCleanup,
#endif /* MODULE */
.init = PMacInit,
.silence = PMacSilence,
.setFormat = PMacSetFormat,
.setVolume = PMacSetVolume,
.play = PMacPlay,
.record = NULL, /* default to no record */
.mixer_init = PMacMixerInit,
.mixer_ioctl = PMacMixerIoctl,
.write_sq_setup = PMacWriteSqSetup,
.read_sq_setup = PMacReadSqSetup,
.state_info = PMacStateInfo,
.abort_read = PMacAbortRead,
.min_dsp_speed = 7350,
.max_dsp_speed = 44100,
.version = ((DMASOUND_AWACS_REVISION<<8) + DMASOUND_AWACS_EDITION)
};
/*** Config & Setup **********************************************************/
/* Check for pmac models that we care about in terms of special actions.
*/
void __init
set_model(void)
{
/* portables/lap-tops */
if (machine_is_compatible("AAPL,3400/2400") ||
machine_is_compatible("AAPL,3500")) {
is_pbook_3X00 = 1 ;
}
if (machine_is_compatible("PowerBook1,1") || /* lombard */
machine_is_compatible("AAPL,PowerBook1998")){ /* wallstreet */
is_pbook_g3 = 1 ;
return ;
}
}
/* Get the OF node that tells us about the registers, interrupts etc. to use
for sound IO.
On most machines the sound IO OF node is the 'davbus' node. On newer pmacs
with DACA (& Tumbler) the node to use is i2s-a. On much older machines i.e.
before 9500 there is no davbus node and we have to use the 'awacs' property.
In the latter case we signal this by setting the codec value - so that the
code that looks for chip properties knows how to go about it.
*/
static struct device_node* __init
get_snd_io_node(void)
{
struct device_node *np;
/* set up awacs_node for early OF which doesn't have a full set of
* properties on davbus
*/
awacs_node = of_find_node_by_name(NULL, "awacs");
if (awacs_node)
awacs_revision = AWACS_AWACS;
/* powermac models after 9500 (other than those which use DACA or
* Tumbler) have a node called "davbus".
*/
np = of_find_node_by_name(NULL, "davbus");
/*
* if we didn't find a davbus device, try 'i2s-a' since
* this seems to be what iBooks (& Tumbler) have.
*/
if (np == NULL) {
i2s_node = of_find_node_by_name(NULL, "i2s-a");
np = of_node_get(i2s_node);
}
/* if we didn't find this - perhaps we are on an early model
* which _only_ has an 'awacs' node
*/
if (np == NULL && awacs_node)
np = of_node_get(awacs_node);
/* if we failed all these return null - this will cause the
* driver to give up...
*/
return np ;
}
/* Get the OF node that contains the info about the sound chip, inputs s-rates
etc.
This node does not exist (or contains much reduced info) on earlier machines
we have to deduce the info other ways for these.
*/
static struct device_node* __init
get_snd_info_node(struct device_node *io)
{
struct device_node *info;
for_each_node_by_name(info, "sound")
if (info->parent == io)
break;
return info;
}
/* Find out what type of codec we have.
*/
static int __init
get_codec_type(struct device_node *info)
{
/* already set if pre-davbus model and info will be NULL */
int codec = awacs_revision ;
if (info) {
/* must do awacs first to allow screamer to overide it */
if (of_device_is_compatible(info, "awacs"))
codec = AWACS_AWACS ;
if (of_device_is_compatible(info, "screamer"))
codec = AWACS_SCREAMER;
if (of_device_is_compatible(info, "burgundy"))
codec = AWACS_BURGUNDY ;
if (of_device_is_compatible(info, "daca"))
codec = AWACS_DACA;
if (of_device_is_compatible(info, "tumbler"))
codec = AWACS_TUMBLER;
if (of_device_is_compatible(info, "snapper"))
codec = AWACS_SNAPPER;
}
return codec ;
}
/* find out what type, if any, of expansion card we have
*/
static void __init
get_expansion_type(void)
{
struct device_node *dn;
dn = of_find_node_by_name(NULL, "perch");
if (dn != NULL)
has_perch = 1;
of_node_put(dn);
dn = of_find_node_by_name(NULL, "pb-ziva-pc");
if (dn != NULL)
has_ziva = 1;
of_node_put(dn);
/* need to work out how we deal with iMac SRS module */
}
/* set up frame rates.
* I suspect that these routines don't quite go about it the right way:
* - where there is more than one rate - I think that the first property
* value is the number of rates.
* TODO: check some more device trees and modify accordingly
* Set dmasound.mach.max_dsp_rate on the basis of these routines.
*/
static void __init
awacs_init_frame_rates(const unsigned int *prop, unsigned int l)
{
int i ;
if (prop) {
for (i=0; i<8; i++)
awacs_freqs_ok[i] = 0 ;
for (l /= sizeof(int); l > 0; --l) {
unsigned int r = *prop++;
/* Apple 'Fixed' format */
if (r >= 0x10000)
r >>= 16;
for (i = 0; i < 8; ++i) {
if (r == awacs_freqs[i]) {
awacs_freqs_ok[i] = 1;
break;
}
}
}
}
/* else we assume that all the rates are available */
}
static void __init
burgundy_init_frame_rates(const unsigned int *prop, unsigned int l)
{
int temp[9] ;
int i = 0 ;
if (prop) {
for (l /= sizeof(int); l > 0; --l) {
unsigned int r = *prop++;
/* Apple 'Fixed' format */
if (r >= 0x10000)
r >>= 16;
temp[i] = r ;
i++ ; if(i>=9) i=8;
}
}
#ifdef DEBUG_DMASOUND
if (i > 1){
int j;
printk("dmasound_pmac: burgundy with multiple frame rates\n");
for(j=0; j<i; j++)
printk("%d ", temp[j]) ;
printk("\n") ;
}
#endif
}
static void __init
daca_init_frame_rates(const unsigned int *prop, unsigned int l)
{
int temp[9] ;
int i = 0 ;
if (prop) {
for (l /= sizeof(int); l > 0; --l) {
unsigned int r = *prop++;
/* Apple 'Fixed' format */
if (r >= 0x10000)
r >>= 16;
temp[i] = r ;
i++ ; if(i>=9) i=8;
}
}
#ifdef DEBUG_DMASOUND
if (i > 1){
int j;
printk("dmasound_pmac: DACA with multiple frame rates\n");
for(j=0; j<i; j++)
printk("%d ", temp[j]) ;
printk("\n") ;
}
#endif
}
static void __init
init_frame_rates(const unsigned int *prop, unsigned int l)
{
switch (awacs_revision) {
case AWACS_TUMBLER:
case AWACS_SNAPPER:
tas_init_frame_rates(prop, l);
break ;
case AWACS_DACA:
daca_init_frame_rates(prop, l);
break ;
case AWACS_BURGUNDY:
burgundy_init_frame_rates(prop, l);
break ;
default:
awacs_init_frame_rates(prop, l);
break ;
}
}
/* find things/machines that can't do mac-io byteswap
*/
static void __init
set_hw_byteswap(struct device_node *io)
{
struct device_node *mio ;
unsigned int kl = 0 ;
/* if seems that Keylargo can't byte-swap */
for (mio = io->parent; mio ; mio = mio->parent) {
if (strcmp(mio->name, "mac-io") == 0) {
if (of_device_is_compatible(mio, "Keylargo"))
kl = 1;
break;
}
}
hw_can_byteswap = !kl;
}
/* Allocate the resources necessary for beep generation. This cannot be (quite)
done statically (yet) because we cannot do virt_to_bus() on static vars when
the code is loaded as a module.
for the sake of saving the possibility that two allocations will incur the
overhead of two pull-ups in DBDMA_ALIGN() we allocate the 'emergency' dmdma
command here as well... even tho' it is not part of the beep process.
*/
int32_t
__init setup_beep(void)
{
/* Initialize beep stuff */
/* want one cmd buffer for beeps, and a second one for emergencies
- i.e. dbdma error conditions.
ask for three to allow for pull up in DBDMA_ALIGN().
*/
beep_dbdma_cmd_space =
kmalloc((2 + 1) * sizeof(struct dbdma_cmd), GFP_KERNEL);
if(beep_dbdma_cmd_space == NULL) {
printk(KERN_ERR "dmasound_pmac: no beep dbdma cmd space\n") ;
return -ENOMEM ;
}
beep_dbdma_cmd = (volatile struct dbdma_cmd *)
DBDMA_ALIGN(beep_dbdma_cmd_space);
/* set up emergency dbdma cmd */
emergency_dbdma_cmd = beep_dbdma_cmd+1 ;
beep_buf = kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
if (beep_buf == NULL) {
printk(KERN_ERR "dmasound_pmac: no memory for beep buffer\n");
kfree(beep_dbdma_cmd_space) ;
return -ENOMEM ;
}
return 0 ;
}
static struct input_dev *awacs_beep_dev;
int __init dmasound_awacs_init(void)
{
struct device_node *io = NULL, *info = NULL;
int vol, res;
if (!machine_is(powermac))
return -ENODEV;
awacs_subframe = 0;
awacs_revision = 0;
hw_can_byteswap = 1 ; /* most can */
/* look for models we need to handle specially */
set_model() ;
/* find the OF node that tells us about the dbdma stuff
*/
io = get_snd_io_node();
if (io == NULL) {
#ifdef DEBUG_DMASOUND
printk("dmasound_pmac: couldn't find sound io OF node\n");
#endif
goto no_device;
}
/* find the OF node that tells us about the sound sub-system
* this doesn't exist on pre-davbus machines (earlier than 9500)
*/
if (awacs_revision != AWACS_AWACS) { /* set for pre-davbus */
info = get_snd_info_node(io) ;
if (info == NULL){
#ifdef DEBUG_DMASOUND
printk("dmasound_pmac: couldn't find 'sound' OF node\n");
#endif
goto no_device;
}
}
awacs_revision = get_codec_type(info) ;
if (awacs_revision == 0) {
#ifdef DEBUG_DMASOUND
printk("dmasound_pmac: couldn't find a Codec we can handle\n");
#endif
goto no_device; /* we don't know this type of h/w */
}
/* set up perch, ziva, SRS or whatever else we have as sound
* expansion.
*/
get_expansion_type();
/* we've now got enough information to make up the audio topology.
* we will map the sound part of mac-io now so that we can probe for
* other info if necessary (early AWACS we want to read chip ids)
*/
if (of_get_address(io, 2, NULL, NULL) == NULL) {
/* OK - maybe we need to use the 'awacs' node (on earlier
* machines).
*/
if (awacs_node) {
of_node_put(io);
io = of_node_get(awacs_node);
if (of_get_address(io, 2, NULL, NULL) == NULL) {
printk("dmasound_pmac: can't use %s\n",
io->full_name);
goto no_device;
}
} else
printk("dmasound_pmac: can't use %s\n", io->full_name);
}
if (of_address_to_resource(io, 0, &awacs_rsrc[0]) ||
request_mem_region(awacs_rsrc[0].start,
awacs_rsrc[0].end - awacs_rsrc[0].start + 1,
" (IO)") == NULL) {
printk(KERN_ERR "dmasound: can't request IO resource !\n");
goto no_device;
}
if (of_address_to_resource(io, 1, &awacs_rsrc[1]) ||
request_mem_region(awacs_rsrc[1].start,
awacs_rsrc[1].end - awacs_rsrc[1].start + 1,
" (tx dma)") == NULL) {
release_mem_region(awacs_rsrc[0].start,
awacs_rsrc[0].end - awacs_rsrc[0].start + 1);
printk(KERN_ERR "dmasound: can't request Tx DMA resource !\n");
goto no_device;
}
if (of_address_to_resource(io, 2, &awacs_rsrc[2]) ||
request_mem_region(awacs_rsrc[2].start,
awacs_rsrc[2].end - awacs_rsrc[2].start + 1,
" (rx dma)") == NULL) {
release_mem_region(awacs_rsrc[0].start,
awacs_rsrc[0].end - awacs_rsrc[0].start + 1);
release_mem_region(awacs_rsrc[1].start,
awacs_rsrc[1].end - awacs_rsrc[1].start + 1);
printk(KERN_ERR "dmasound: can't request Rx DMA resource !\n");
goto no_device;
}
awacs_beep_dev = input_allocate_device();
if (!awacs_beep_dev) {
release_mem_region(awacs_rsrc[0].start,
awacs_rsrc[0].end - awacs_rsrc[0].start + 1);
release_mem_region(awacs_rsrc[1].start,
awacs_rsrc[1].end - awacs_rsrc[1].start + 1);
release_mem_region(awacs_rsrc[2].start,
awacs_rsrc[2].end - awacs_rsrc[2].start + 1);
printk(KERN_ERR "dmasound: can't allocate input device !\n");
goto no_device;
}
awacs_beep_dev->name = "dmasound beeper";
awacs_beep_dev->phys = "macio/input0";
awacs_beep_dev->id.bustype = BUS_HOST;
awacs_beep_dev->event = awacs_beep_event;
awacs_beep_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
awacs_beep_dev->evbit[0] = BIT(EV_SND);
/* all OF versions I've seen use this value */
if (i2s_node)
i2s = ioremap(awacs_rsrc[0].start, 0x1000);
else
awacs = ioremap(awacs_rsrc[0].start, 0x1000);
awacs_txdma = ioremap(awacs_rsrc[1].start, 0x100);
awacs_rxdma = ioremap(awacs_rsrc[2].start, 0x100);
/* first of all make sure that the chip is powered up....*/
pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, io, 0, 1);
if (awacs_revision == AWACS_SCREAMER && awacs)
awacs_recalibrate();
awacs_irq = irq_of_parse_and_map(io, 0);
awacs_tx_irq = irq_of_parse_and_map(io, 1);
awacs_rx_irq = irq_of_parse_and_map(io, 2);
/* Hack for legacy crap that will be killed someday */
of_node_put(awacs_node);
awacs_node = of_node_get(io);
/* if we have an awacs or screamer - probe the chip to make
* sure we have the right revision.
*/
if (awacs_revision <= AWACS_SCREAMER){
uint32_t temp, rev, mfg ;
/* find out the awacs revision from the chip */
temp = in_le32(&awacs->codec_stat);
rev = (temp >> 12) & 0xf;
mfg = (temp >> 8) & 0xf;
#ifdef DEBUG_DMASOUND
printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
#endif
if (rev >= AWACS_SCREAMER)
awacs_revision = AWACS_SCREAMER ;
else
awacs_revision = rev ;
}
dmasound.mach = machPMac;
/* find out other bits & pieces from OF, these may be present
only on some models ... so be careful.
*/
/* in the absence of a frame rates property we will use the defaults
*/
if (info) {
const unsigned int *prop;
unsigned int l;
sound_device_id = 0;
/* device ID appears post g3 b&w */
prop = of_get_property(info, "device-id", NULL);
if (prop != 0)
sound_device_id = *prop;
/* look for a property saying what sample rates
are available */
prop = of_get_property(info, "sample-rates", &l);
if (prop == 0)
prop = of_get_property(info, "output-frame-rates", &l);
/* if it's there use it to set up frame rates */
init_frame_rates(prop, l) ;
of_node_put(info);
info = NULL;
}
if (awacs)
out_le32(&awacs->control, 0x11); /* set everything quiesent */
set_hw_byteswap(io) ; /* figure out if the h/w can do it */
#ifdef CONFIG_NVRAM
/* get default volume from nvram */
vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
#else
vol = 0;
#endif
/* set up tracking values */
spk_vol = vol * 100 ;
spk_vol /= 7 ; /* get set value to a percentage */
spk_vol |= (spk_vol << 8) ; /* equal left & right */
line_vol = passthru_vol = spk_vol ;
/* fill regs that are shared between AWACS & Burgundy */
awacs_reg[2] = vol + (vol << 6);
awacs_reg[4] = vol + (vol << 6);
awacs_reg[5] = vol + (vol << 6); /* screamer has loopthru vol control */
awacs_reg[6] = 0; /* maybe should be vol << 3 for PCMCIA speaker */
awacs_reg[7] = 0;
awacs_reg[0] = MASK_MUX_CD;
awacs_reg[1] = MASK_LOOPTHRU;
/* FIXME: Only machines with external SRS module need MASK_PAROUT */
if (has_perch || sound_device_id == 0x5
|| /*sound_device_id == 0x8 ||*/ sound_device_id == 0xb)
awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
switch (awacs_revision) {
case AWACS_TUMBLER:
tas_register_driver(&tas3001c_hooks);
tas_init(I2C_DRIVERID_TAS3001C, I2C_DRIVERNAME_TAS3001C);
tas_dmasound_init();
tas_post_init();
break ;
case AWACS_SNAPPER:
tas_register_driver(&tas3004_hooks);
tas_init(I2C_DRIVERID_TAS3004,I2C_DRIVERNAME_TAS3004);
tas_dmasound_init();
tas_post_init();
break;
case AWACS_DACA:
daca_init();
break;
case AWACS_BURGUNDY:
awacs_burgundy_init();
break ;
case AWACS_SCREAMER:
case AWACS_AWACS:
default:
load_awacs();
break ;
}
/* enable/set-up external modules - when we know how */
if (has_perch)
awacs_enable_amp(100 * 0x101);
/* Reset dbdma channels */
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
while (in_le32(&awacs_txdma->status) & RUN)
udelay(1);
out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
while (in_le32(&awacs_rxdma->status) & RUN)
udelay(1);
/* Initialize beep stuff */
if ((res=setup_beep()))
return res ;
#ifdef CONFIG_PM
pmu_register_sleep_notifier(&awacs_sleep_notifier);
#endif /* CONFIG_PM */
/* Powerbooks have odd ways of enabling inputs such as
an expansion-bay CD or sound from an internal modem
or a PC-card modem. */
if (is_pbook_3X00) {
/*
* Enable CD and PC-card sound inputs.
* This is done by reading from address
* f301a000, + 0x10 to enable the expansion-bay
* CD sound input, + 0x80 to enable the PC-card
* sound input. The 0x100 enables the SCSI bus
* terminator power.
*/
latch_base = ioremap (0xf301a000, 0x1000);
in_8(latch_base + 0x190);
} else if (is_pbook_g3) {
struct device_node* mio;
macio_base = NULL;
for (mio = io->parent; mio; mio = mio->parent) {
if (strcmp(mio->name, "mac-io") == 0) {
struct resource r;
if (of_address_to_resource(mio, 0, &r) == 0)
macio_base = ioremap(r.start, 0x40);
break;
}
}
/*
* Enable CD sound input.
* The relevant bits for writing to this byte are 0x8f.
* I haven't found out what the 0x80 bit does.
* For the 0xf bits, writing 3 or 7 enables the CD
* input, any other value disables it. Values
* 1, 3, 5, 7 enable the microphone. Values 0, 2,
* 4, 6, 8 - f enable the input from the modem.
* -- paulus.
*/
if (macio_base)
out_8(macio_base + 0x37, 3);
}
if (hw_can_byteswap)
dmasound.mach.hardware_afmts = (AFMT_S16_BE | AFMT_S16_LE) ;
else
dmasound.mach.hardware_afmts = AFMT_S16_BE ;
/* shut out chips that do output only.
* may need to extend this to machines which have no inputs - even tho'
* they use screamer - IIRC one of the powerbooks is like this.
*/
if (awacs_revision != AWACS_DACA) {
dmasound.mach.capabilities = DSP_CAP_DUPLEX ;
dmasound.mach.record = PMacRecord ;
}
dmasound.mach.default_hard = def_hard ;
dmasound.mach.default_soft = def_soft ;
switch (awacs_revision) {
case AWACS_BURGUNDY:
sprintf(awacs_name, "PowerMac Burgundy ") ;
break ;
case AWACS_DACA:
sprintf(awacs_name, "PowerMac DACA ") ;
break ;
case AWACS_TUMBLER:
sprintf(awacs_name, "PowerMac Tumbler ") ;
break ;
case AWACS_SNAPPER:
sprintf(awacs_name, "PowerMac Snapper ") ;
break ;
case AWACS_SCREAMER:
sprintf(awacs_name, "PowerMac Screamer ") ;
break ;
case AWACS_AWACS:
default:
sprintf(awacs_name, "PowerMac AWACS rev %d ", awacs_revision) ;
break ;
}
/*
* XXX: we should handle errors here, but that would mean
* rewriting the whole init code. later..
*/
input_register_device(awacs_beep_dev);
of_node_put(io);
return dmasound_init();
no_device:
of_node_put(info);
of_node_put(awacs_node);
of_node_put(i2s_node);
of_node_put(io);
return -ENODEV ;
}
static void __exit dmasound_awacs_cleanup(void)
{
input_unregister_device(awacs_beep_dev);
switch (awacs_revision) {
case AWACS_TUMBLER:
case AWACS_SNAPPER:
tas_dmasound_cleanup();
tas_cleanup();
break ;
case AWACS_DACA:
daca_cleanup();
break;
}
dmasound_deinit();
of_node_put(awacs_node);
of_node_put(i2s_node);
}
MODULE_DESCRIPTION("PowerMac built-in audio driver.");
MODULE_LICENSE("GPL");
module_init(dmasound_awacs_init);
module_exit(dmasound_awacs_cleanup);
......@@ -202,13 +202,6 @@ module_param(numWriteBufs, int, 0);
static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ; /* in bytes */
module_param(writeBufSize, int, 0);
#ifdef HAS_RECORD
static unsigned int numReadBufs = DEFAULT_N_BUFFERS;
module_param(numReadBufs, int, 0);
static unsigned int readBufSize = DEFAULT_BUFF_SIZE; /* in bytes */
module_param(readBufSize, int, 0);
#endif
MODULE_LICENSE("GPL");
#ifdef MODULE
......@@ -403,10 +396,6 @@ static void mixer_init(void)
struct sound_queue dmasound_write_sq;
static void sq_reset_output(void) ;
#ifdef HAS_RECORD
struct sound_queue dmasound_read_sq;
static void sq_reset_input(void) ;
#endif
static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
{
......@@ -530,12 +519,6 @@ printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
sq->rear = -1;
setup_func = dmasound.mach.write_sq_setup;
}
#ifdef HAS_RECORD
else {
sq->rear = 0;
setup_func = dmasound.mach.read_sq_setup;
}
#endif
if (setup_func)
return setup_func();
return 0 ;
......@@ -672,13 +655,6 @@ static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
}
if (file->f_mode & FMODE_WRITE )
poll_wait(file, &write_sq.action_queue, wait);
#ifdef HAS_RECORD
if (file->f_mode & FMODE_READ)
poll_wait(file, &read_sq.action_queue, wait);
if (file->f_mode & FMODE_READ)
if (read_sq.block_size - read_sq.rear_size > 0)
mask |= POLLIN | POLLRDNORM;
#endif
if (file->f_mode & FMODE_WRITE)
if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
mask |= POLLOUT | POLLWRNORM;
......@@ -686,101 +662,6 @@ static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
}
#ifdef HAS_RECORD
/*
* Here is how the values are used for reading.
* The value 'active' simply indicates the DMA is running. This is done
* so the driver semantics are DMA starts when the first read is posted.
* The value 'front' indicates the buffer we should next send to the user.
* The value 'rear' indicates the buffer the DMA is currently filling.
* When 'front' == 'rear' the buffer "ring" is empty (we always have an
* empty available). The 'rear_size' is used to track partial offsets
* into the buffer we are currently returning to the user.
* This level (> [1.5]) doesn't care what strategy the LL driver uses with
* DMA on over-run. It can leave it running (and keep active == 1) or it
* can kill it and set active == 0 in which case this routine will spot
* it and restart the DMA.
*/
static ssize_t sq_read(struct file *file, char __user *dst, size_t uLeft,
loff_t *ppos)
{
ssize_t uRead, bLeft, bUsed, uUsed;
if (uLeft == 0)
return 0;
/* cater for the compatibility mode - record compiled in but no LL */
if (dmasound.mach.record == NULL)
return -EINVAL ;
/* see comment in sq_write()
*/
if( shared_resources_initialised == 0) {
dmasound.mach.init() ;
shared_resources_initialised = 1 ;
}
/* set up the sq if it is not already done. see comments in sq_write().
*/
if (read_sq.locked == 0) {
if ((uRead = sq_setup(&read_sq)) < 0)
return uRead ;
}
uRead = 0;
/* Move what the user requests, depending upon other options.
*/
while (uLeft > 0) {
/* we happened to get behind and the LL driver killed DMA
then we should set it going again. This also sets it
going the first time through.
*/
if ( !read_sq.active )
dmasound.mach.record();
/* When front == rear, the DMA is not done yet.
*/
while (read_sq.front == read_sq.rear) {
if (read_sq.open_mode & O_NONBLOCK) {
return uRead > 0 ? uRead : -EAGAIN;
}
SLEEP(read_sq.action_queue);
if (signal_pending(current))
return uRead > 0 ? uRead : -EINTR;
}
/* The amount we move is either what is left in the
* current buffer or what the user wants.
*/
bLeft = read_sq.block_size - read_sq.rear_size;
bUsed = read_sq.rear_size;
uUsed = sound_copy_translate(dmasound.trans_read, dst, uLeft,
read_sq.buffers[read_sq.front],
&bUsed, bLeft);
if (uUsed <= 0)
return uUsed;
dst += uUsed;
uRead += uUsed;
uLeft -= uUsed;
read_sq.rear_size += bUsed;
if (read_sq.rear_size >= read_sq.block_size) {
read_sq.rear_size = 0;
read_sq.front++;
if (read_sq.front >= read_sq.max_active)
read_sq.front = 0;
}
}
return uRead;
}
#endif /* HAS_RECORD */
static inline void sq_init_waitqueue(struct sound_queue *sq)
{
init_waitqueue_head(&sq->action_queue);
......@@ -854,23 +735,6 @@ static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode,
#define write_sq_open(file) \
sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
#ifdef HAS_RECORD
#define read_sq_init_waitqueue() sq_init_waitqueue(&read_sq)
#if 0 /* blocking open() */
#define read_sq_wake_up(file) sq_wake_up(&read_sq, file, FMODE_READ)
#endif
#define read_sq_release_buffers() sq_release_buffers(&read_sq)
#define read_sq_open(file) \
sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize )
#else
#define read_sq_init_waitqueue() do {} while (0)
#if 0 /* blocking open() */
#define read_sq_wake_up(file) do {} while (0)
#endif
#define read_sq_release_buffers() do {} while (0)
#define sq_reset_input() do {} while (0)
#endif
static int sq_open(struct inode *inode, struct file *file)
{
int rc;
......@@ -881,25 +745,11 @@ static int sq_open(struct inode *inode, struct file *file)
rc = write_sq_open(file); /* checks the f_mode */
if (rc)
goto out;
#ifdef HAS_RECORD
if (dmasound.mach.record) {
rc = read_sq_open(file); /* checks the f_mode */
if (rc)
goto out;
} else { /* no record function installed; in compat mode */
if (file->f_mode & FMODE_READ) {
/* TODO: if O_RDWR, release any resources grabbed by write part */
rc = -ENXIO;
goto out;
}
}
#else /* !HAS_RECORD */
if (file->f_mode & FMODE_READ) {
/* TODO: if O_RDWR, release any resources grabbed by write part */
rc = -ENXIO ; /* I think this is what is required by open(2) */
goto out;
}
#endif /* HAS_RECORD */
if (dmasound.mach.sq_open)
dmasound.mach.sq_open(file->f_mode);
......@@ -956,43 +806,9 @@ static void sq_reset_output(void)
write_sq.user_frag_size = 0 ;
}
#ifdef HAS_RECORD
static void sq_reset_input(void)
{
if (dmasound.mach.record && read_sq.active) {
if (dmasound.mach.abort_read) { /* this routine must really be present */
read_sq.syncing = 1 ;
/* this can use the read_sq.sync_queue to sleep if
necessary - it should not return until DMA
is really stopped - because we might deallocate
the buffers as the next action...
*/
dmasound.mach.abort_read() ;
} else {
printk(KERN_ERR
"dmasound_core: %s has no abort_read()!! all bets are off\n",
dmasound.mach.name) ;
}
}
read_sq.syncing =
read_sq.active =
read_sq.front =
read_sq.count =
read_sq.rear = 0 ;
/* OK - we can unlock the parameters and fragment settings */
read_sq.locked = 0 ;
read_sq.user_frags = 0 ;
read_sq.user_frag_size = 0 ;
}
#endif
static void sq_reset(void)
{
sq_reset_output() ;
sq_reset_input() ;
/* we could consider resetting the shared_resources_owner here... but I
think it is probably still rather non-obvious to application writer
*/
......@@ -1038,17 +854,6 @@ static int sq_release(struct inode *inode, struct file *file)
lock_kernel();
#ifdef HAS_RECORD
/* probably best to do the read side first - so that time taken to do it
overlaps with playing any remaining output samples.
*/
if (file->f_mode & FMODE_READ) {
sq_reset_input() ; /* make sure dma is stopped and all is quiet */
read_sq_release_buffers();
read_sq.busy = 0;
}
#endif
if (file->f_mode & FMODE_WRITE) {
if (write_sq.busy)
rc = sq_fsync(file, file->f_path.dentry);
......@@ -1105,11 +910,6 @@ static int shared_resources_are_mine(mode_t md)
static int queues_are_quiescent(void)
{
#ifdef HAS_RECORD
if (dmasound.mach.record)
if (read_sq.locked)
return 0 ;
#endif
if (write_sq.locked)
return 0 ;
return 1 ;
......@@ -1185,13 +985,6 @@ static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
the read_sq ones.
*/
size = 0 ;
#ifdef HAS_RECORD
if (dmasound.mach.record && (file->f_mode & FMODE_READ)) {
if ( !read_sq.locked )
sq_setup(&read_sq) ; /* set params */
size = read_sq.user_frag_size ;
}
#endif
if (file->f_mode & FMODE_WRITE) {
if ( !write_sq.locked )
sq_setup(&write_sq) ;
......@@ -1214,8 +1007,6 @@ static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
everything - read, however, is killed imediately.
*/
result = 0 ;
if ((file->f_mode & FMODE_READ) && dmasound.mach.record)
sq_reset_input() ;
if (file->f_mode & FMODE_WRITE) {
result = sq_fsync(file, file->f_path.dentry);
sq_reset_output() ;
......@@ -1294,13 +1085,6 @@ static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
result = 0 ;
nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
size = data & 0xffff;
#ifdef HAS_RECORD
if ((file->f_mode & FMODE_READ) && dmasound.mach.record) {
result = set_queue_frags(&read_sq, nbufs, size) ;
if (result)
return result ;
}
#endif
if (file->f_mode & FMODE_WRITE) {
result = set_queue_frags(&write_sq, nbufs, size) ;
if (result)
......@@ -1348,20 +1132,6 @@ static const struct file_operations sq_fops =
.release = sq_release,
};
#ifdef HAS_RECORD
static const struct file_operations sq_fops_record =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = sq_write,
.poll = sq_poll,
.ioctl = sq_ioctl,
.open = sq_open,
.release = sq_release,
.read = sq_read,
};
#endif
static int sq_init(void)
{
const struct file_operations *fops = &sq_fops;
......@@ -1369,10 +1139,6 @@ static int sq_init(void)
int sq_unit;
#endif
#ifdef HAS_RECORD
if (dmasound.mach.record)
fops = &sq_fops_record;
#endif
sq_unit = register_sound_dsp(fops, -1);
if (sq_unit < 0) {
printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
......@@ -1380,7 +1146,6 @@ static int sq_init(void)
}
write_sq_init_waitqueue();
read_sq_init_waitqueue();
/* These parameters will be restored for every clean open()
* in the case of multiple open()s (e.g. dsp0 & dsp1) they
......@@ -1406,11 +1171,7 @@ static int sq_init(void)
driver.
*/
#ifdef HAS_RECORD
#define STAT_BUFF_LEN 1024
#else
#define STAT_BUFF_LEN 768
#endif
/* this is how much space we will allow the low-level driver to use
in the stat buffer. Currently, 2 * (80 character line + <NL>).
......@@ -1518,11 +1279,6 @@ static int state_open(struct inode *inode, struct file *file)
len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
len += sprintf(buffer+len,"%9s:%8d%6d\n",
"write", write_sq.numBufs, write_sq.bufSize) ;
#ifdef HAS_RECORD
if (dmasound.mach.record)
len += sprintf(buffer+len,"%9s:%8d%6d\n",
"read", read_sq.numBufs, read_sq.bufSize) ;
#endif
len += sprintf(buffer+len,
"Current : MaxFrg FragSiz MaxAct Frnt Rear "
"Cnt RrSize A B S L xruns\n") ;
......@@ -1531,14 +1287,6 @@ static int state_open(struct inode *inode, struct file *file)
write_sq.max_active, write_sq.front, write_sq.rear,
write_sq.count, write_sq.rear_size, write_sq.active,
write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
#ifdef HAS_RECORD
if (dmasound.mach.record)
len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
"read", read_sq.max_count, read_sq.block_size,
read_sq.max_active, read_sq.front, read_sq.rear,
read_sq.count, read_sq.rear_size, read_sq.active,
read_sq.busy, read_sq.syncing, read_sq.locked, read_sq.xruns) ;
#endif
#ifdef DEBUG_DMASOUND
printk("dmasound: stat buffer used %d bytes\n", len) ;
#endif
......@@ -1638,13 +1386,6 @@ int dmasound_init(void)
(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
numWriteBufs, writeBufSize) ;
#ifdef HAS_RECORD
if (dmasound.mach.record)
printk(KERN_INFO
"Read will use %4d fragments of %7d bytes as default\n",
numReadBufs, readBufSize) ;
#endif
return 0;
}
......@@ -1659,7 +1400,6 @@ void dmasound_deinit(void)
}
write_sq_release_buffers();
read_sq_release_buffers();
if (mixer_unit >= 0)
unregister_sound_mixer(mixer_unit);
......@@ -1684,36 +1424,12 @@ static int dmasound_setup(char *str)
*/
switch (ints[0]) {
#ifdef HAS_RECORD
case 5:
if ((ints[5] < 0) || (ints[5] > MAX_CATCH_RADIUS))
printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
else
catchRadius = ints[5];
/* fall through */
case 4:
if (ints[4] < MIN_BUFFERS)
printk("dmasound_setup: invalid number of read buffers, using default = %d\n",
numReadBufs);
else
numReadBufs = ints[4];
/* fall through */
case 3:
if ((size = ints[3]) < 256) /* check for small buffer specs */
size <<= 10 ;
if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
printk("dmasound_setup: invalid read buffer size, using default = %d\n", readBufSize);
else
readBufSize = size;
/* fall through */
#else
case 3:
if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
else
catchRadius = ints[3];
/* fall through */
#endif
case 2:
if (ints[1] < MIN_BUFFERS)
printk("dmasound_setup: invalid number of buffers, using default = %d\n", numWriteBufs);
......@@ -1830,9 +1546,6 @@ EXPORT_SYMBOL(dmasound_init);
EXPORT_SYMBOL(dmasound_deinit);
#endif
EXPORT_SYMBOL(dmasound_write_sq);
#ifdef HAS_RECORD
EXPORT_SYMBOL(dmasound_read_sq);
#endif
EXPORT_SYMBOL(dmasound_catchRadius);
#ifdef HAS_8BIT_TABLES
EXPORT_SYMBOL(dmasound_ulaw2dma8);
......
/*
* Driver for the i2c/i2s based TA3004 sound chip used
* on some Apple hardware. Also known as "snapper".
*
* Tobias Sargeant <tobias.sargeant@bigpond.com>
* Based upon, tas3001c.c by Christopher C. Chimelis <chris@debian.org>:
*
* TODO:
* -----
* * Enable control over input line 2 (is this connected?)
* * Implement sleep support (at least mute everything and
* * set gains to minimum during sleep)
* * Look into some of Darwin's tweaks regarding the mute
* * lines (delays & different behaviour on some HW)
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <linux/workqueue.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/prom.h>
#include "dmasound.h"
#include "tas_common.h"
#include "tas3001c.h"
#include "tas_ioctl.h"
#define TAS3001C_BIQUAD_FILTER_COUNT 6
#define TAS3001C_BIQUAD_CHANNEL_COUNT 2
#define VOL_DEFAULT (100 * 4 / 5)
#define INPUT_DEFAULT (100 * 4 / 5)
#define BASS_DEFAULT (100 / 2)
#define TREBLE_DEFAULT (100 / 2)
struct tas3001c_data_t {
struct tas_data_t super;
int device_id;
int output_id;
int speaker_id;
struct tas_drce_t drce_state;
struct work_struct change;
};
static const union tas_biquad_t
tas3001c_eq_unity={
.buf = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }
};
static inline unsigned char db_to_regval(short db) {
int r=0;
r=(db+0x59a0) / 0x60;
if (r < 0x91) return 0x91;
if (r > 0xef) return 0xef;
return r;
}
static inline short quantize_db(short db) {
return db_to_regval(db) * 0x60 - 0x59a0;
}
static inline int
register_width(enum tas3001c_reg_t r)
{
switch(r) {
case TAS3001C_REG_MCR:
case TAS3001C_REG_TREBLE:
case TAS3001C_REG_BASS:
return 1;
case TAS3001C_REG_DRC:
return 2;
case TAS3001C_REG_MIXER1:
case TAS3001C_REG_MIXER2:
return 3;
case TAS3001C_REG_VOLUME:
return 6;
case TAS3001C_REG_LEFT_BIQUAD0:
case TAS3001C_REG_LEFT_BIQUAD1:
case TAS3001C_REG_LEFT_BIQUAD2:
case TAS3001C_REG_LEFT_BIQUAD3:
case TAS3001C_REG_LEFT_BIQUAD4:
case TAS3001C_REG_LEFT_BIQUAD5:
case TAS3001C_REG_LEFT_BIQUAD6:
case TAS3001C_REG_RIGHT_BIQUAD0:
case TAS3001C_REG_RIGHT_BIQUAD1:
case TAS3001C_REG_RIGHT_BIQUAD2:
case TAS3001C_REG_RIGHT_BIQUAD3:
case TAS3001C_REG_RIGHT_BIQUAD4:
case TAS3001C_REG_RIGHT_BIQUAD5:
case TAS3001C_REG_RIGHT_BIQUAD6:
return 15;
default:
return 0;
}
}
static int
tas3001c_write_register( struct tas3001c_data_t *self,
enum tas3001c_reg_t reg_num,
char *data,
uint write_mode)
{
if (reg_num==TAS3001C_REG_MCR ||
reg_num==TAS3001C_REG_BASS ||
reg_num==TAS3001C_REG_TREBLE) {
return tas_write_byte_register(&self->super,
(uint)reg_num,
*data,
write_mode);
} else {
return tas_write_register(&self->super,
(uint)reg_num,
register_width(reg_num),
data,
write_mode);
}
}
static int
tas3001c_sync_register( struct tas3001c_data_t *self,
enum tas3001c_reg_t reg_num)
{
if (reg_num==TAS3001C_REG_MCR ||
reg_num==TAS3001C_REG_BASS ||
reg_num==TAS3001C_REG_TREBLE) {
return tas_sync_byte_register(&self->super,
(uint)reg_num,
register_width(reg_num));
} else {
return tas_sync_register(&self->super,
(uint)reg_num,
register_width(reg_num));
}
}
static int
tas3001c_read_register( struct tas3001c_data_t *self,
enum tas3001c_reg_t reg_num,
char *data,
uint write_mode)
{
return tas_read_register(&self->super,
(uint)reg_num,
register_width(reg_num),
data);
}
static inline int
tas3001c_fast_load(struct tas3001c_data_t *self, int fast)
{
if (fast)
self->super.shadow[TAS3001C_REG_MCR][0] |= 0x80;
else
self->super.shadow[TAS3001C_REG_MCR][0] &= 0x7f;
return tas3001c_sync_register(self,TAS3001C_REG_MCR);
}
static uint
tas3001c_supported_mixers(struct tas3001c_data_t *self)
{
return SOUND_MASK_VOLUME |
SOUND_MASK_PCM |
SOUND_MASK_ALTPCM |
SOUND_MASK_TREBLE |
SOUND_MASK_BASS;
}
static int
tas3001c_mixer_is_stereo(struct tas3001c_data_t *self,int mixer)
{
switch(mixer) {
case SOUND_MIXER_VOLUME:
return 1;
default:
return 0;
}
}
static uint
tas3001c_stereo_mixers(struct tas3001c_data_t *self)
{
uint r=tas3001c_supported_mixers(self);
uint i;
for (i=1; i<SOUND_MIXER_NRDEVICES; i++)
if (r&(1<<i) && !tas3001c_mixer_is_stereo(self,i))
r &= ~(1<<i);
return r;
}
static int
tas3001c_get_mixer_level(struct tas3001c_data_t *self,int mixer,uint *level)
{
if (!self)
return -1;
*level=self->super.mixer[mixer];
return 0;
}
static int
tas3001c_set_mixer_level(struct tas3001c_data_t *self,int mixer,uint level)
{
int rc;
tas_shadow_t *shadow;
uint temp;
uint offset=0;
if (!self)
return -1;
shadow=self->super.shadow;
if (!tas3001c_mixer_is_stereo(self,mixer))
level = tas_mono_to_stereo(level);
switch(mixer) {
case SOUND_MIXER_VOLUME:
temp = tas3001c_gain.master[level&0xff];
shadow[TAS3001C_REG_VOLUME][0] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_VOLUME][1] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_VOLUME][2] = (temp >> 0) & 0xff;
temp = tas3001c_gain.master[(level>>8)&0xff];
shadow[TAS3001C_REG_VOLUME][3] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_VOLUME][4] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_VOLUME][5] = (temp >> 0) & 0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
break;
case SOUND_MIXER_ALTPCM:
/* tas3001c_fast_load(self, 1); */
level = tas_mono_to_stereo(level);
temp = tas3001c_gain.mixer[level&0xff];
shadow[TAS3001C_REG_MIXER2][offset+0] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_MIXER2][offset+1] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_MIXER2][offset+2] = (temp >> 0) & 0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
/* tas3001c_fast_load(self, 0); */
break;
case SOUND_MIXER_PCM:
/* tas3001c_fast_load(self, 1); */
level = tas_mono_to_stereo(level);
temp = tas3001c_gain.mixer[level&0xff];
shadow[TAS3001C_REG_MIXER1][offset+0] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_MIXER1][offset+1] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_MIXER1][offset+2] = (temp >> 0) & 0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
/* tas3001c_fast_load(self, 0); */
break;
case SOUND_MIXER_TREBLE:
temp = tas3001c_gain.treble[level&0xff];
shadow[TAS3001C_REG_TREBLE][0]=temp&0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
break;
case SOUND_MIXER_BASS:
temp = tas3001c_gain.bass[level&0xff];
shadow[TAS3001C_REG_BASS][0]=temp&0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_BASS);
break;
default:
rc = -1;
break;
}
if (rc < 0)
return rc;
self->super.mixer[mixer]=level;
return 0;
}
static int
tas3001c_leave_sleep(struct tas3001c_data_t *self)
{
unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
if (!self)
return -1;
/* Make sure something answers on the i2c bus */
if (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,
WRITE_NORMAL|FORCE_WRITE) < 0)
return -1;
tas3001c_fast_load(self, 1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);
tas3001c_fast_load(self, 0);
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
return 0;
}
static int
tas3001c_enter_sleep(struct tas3001c_data_t *self)
{
/* Stub for now, but I have the details on low-power mode */
if (!self)
return -1;
return 0;
}
static int
tas3001c_sync_biquad( struct tas3001c_data_t *self,
u_int channel,
u_int filter)
{
enum tas3001c_reg_t reg;
if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
return tas3001c_sync_register(self,reg);
}
static int
tas3001c_write_biquad_shadow( struct tas3001c_data_t *self,
u_int channel,
u_int filter,
const union tas_biquad_t *biquad)
{
tas_shadow_t *shadow=self->super.shadow;
enum tas3001c_reg_t reg;
if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
SET_4_20(shadow[reg], 0,biquad->coeff.b0);
SET_4_20(shadow[reg], 3,biquad->coeff.b1);
SET_4_20(shadow[reg], 6,biquad->coeff.b2);
SET_4_20(shadow[reg], 9,biquad->coeff.a1);
SET_4_20(shadow[reg],12,biquad->coeff.a2);
return 0;
}
static int
tas3001c_write_biquad( struct tas3001c_data_t *self,
u_int channel,
u_int filter,
const union tas_biquad_t *biquad)
{
int rc;
rc=tas3001c_write_biquad_shadow(self, channel, filter, biquad);
if (rc < 0) return rc;
return tas3001c_sync_biquad(self, channel, filter);
}
static int
tas3001c_write_biquad_list( struct tas3001c_data_t *self,
u_int filter_count,
u_int flags,
struct tas_biquad_ctrl_t *biquads)
{
int i;
int rc;
if (flags & TAS_BIQUAD_FAST_LOAD) tas3001c_fast_load(self,1);
for (i=0; i<filter_count; i++) {
rc=tas3001c_write_biquad(self,
biquads[i].channel,
biquads[i].filter,
&biquads[i].data);
if (rc < 0) break;
}
if (flags & TAS_BIQUAD_FAST_LOAD) {
tas3001c_fast_load(self,0);
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
}
return rc;
}
static int
tas3001c_read_biquad( struct tas3001c_data_t *self,
u_int channel,
u_int filter,
union tas_biquad_t *biquad)
{
tas_shadow_t *shadow=self->super.shadow;
enum tas3001c_reg_t reg;
if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
biquad->coeff.b0=GET_4_20(shadow[reg], 0);
biquad->coeff.b1=GET_4_20(shadow[reg], 3);
biquad->coeff.b2=GET_4_20(shadow[reg], 6);
biquad->coeff.a1=GET_4_20(shadow[reg], 9);
biquad->coeff.a2=GET_4_20(shadow[reg],12);
return 0;
}
static int
tas3001c_eq_rw( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
struct tas_biquad_ctrl_t biquad;
void __user *argp = (void __user *)arg;
if (copy_from_user(&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
if (cmd & SIOC_IN) {
rc=tas3001c_write_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
}
if (cmd & SIOC_OUT) {
rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
}
return 0;
}
static int
tas3001c_eq_list_rw( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
int filter_count;
int flags;
int i,j;
char sync_required[2][6];
struct tas_biquad_ctrl_t biquad;
struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg;
memset(sync_required,0,sizeof(sync_required));
if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int)))
return -EFAULT;
if (copy_from_user(&flags, &argp->flags, sizeof(int)))
return -EFAULT;
if (cmd & SIOC_IN) {
}
for (i=0; i < filter_count; i++) {
if (copy_from_user(&biquad, &argp->biquads[i],
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
if (cmd & SIOC_IN) {
sync_required[biquad.channel][biquad.filter]=1;
rc=tas3001c_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
}
if (cmd & SIOC_OUT) {
rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
if (copy_to_user(&argp->biquads[i], &biquad,
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
}
}
if (cmd & SIOC_IN) {
if (flags & TAS_BIQUAD_FAST_LOAD) tas3001c_fast_load(self,1);
for (i=0; i<2; i++) {
for (j=0; j<6; j++) {
if (sync_required[i][j]) {
rc=tas3001c_sync_biquad(self, i, j);
if (rc < 0) return rc;
}
}
}
if (flags & TAS_BIQUAD_FAST_LOAD) {
tas3001c_fast_load(self,0);
/* now we need to set up the mixers again,
because leaving fast mode resets them. */
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
}
}
return 0;
}
static int
tas3001c_update_drce( struct tas3001c_data_t *self,
int flags,
struct tas_drce_t *drce)
{
tas_shadow_t *shadow;
shadow=self->super.shadow;
shadow[TAS3001C_REG_DRC][1] = 0xc1;
if (flags & TAS_DRCE_THRESHOLD) {
self->drce_state.threshold=quantize_db(drce->threshold);
shadow[TAS3001C_REG_DRC][2] = db_to_regval(self->drce_state.threshold);
}
if (flags & TAS_DRCE_ENABLE) {
self->drce_state.enable = drce->enable;
}
if (!self->drce_state.enable) {
shadow[TAS3001C_REG_DRC][0] = 0xf0;
}
#ifdef DEBUG_DRCE
printk("DRCE IOCTL: set [ ENABLE:%x THRESH:%x\n",
self->drce_state.enable,
self->drce_state.threshold);
printk("DRCE IOCTL: reg [ %02x %02x ]\n",
(unsigned char)shadow[TAS3001C_REG_DRC][0],
(unsigned char)shadow[TAS3001C_REG_DRC][1]);
#endif
return tas3001c_sync_register(self, TAS3001C_REG_DRC);
}
static int
tas3001c_drce_rw( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
struct tas_drce_ctrl_t drce_ctrl;
void __user *argp = (void __user *)arg;
if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t)))
return -EFAULT;
#ifdef DEBUG_DRCE
printk("DRCE IOCTL: input [ FLAGS:%x ENABLE:%x THRESH:%x\n",
drce_ctrl.flags,
drce_ctrl.data.enable,
drce_ctrl.data.threshold);
#endif
if (cmd & SIOC_IN) {
rc = tas3001c_update_drce(self, drce_ctrl.flags, &drce_ctrl.data);
if (rc < 0)
return rc;
}
if (cmd & SIOC_OUT) {
if (drce_ctrl.flags & TAS_DRCE_ENABLE)
drce_ctrl.data.enable = self->drce_state.enable;
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD)
drce_ctrl.data.threshold = self->drce_state.threshold;
if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
return 0;
}
static void
tas3001c_update_device_parameters(struct tas3001c_data_t *self)
{
int i,j;
if (!self) return;
if (self->output_id == TAS_OUTPUT_HEADPHONES) {
tas3001c_fast_load(self, 1);
for (i=0; i<TAS3001C_BIQUAD_CHANNEL_COUNT; i++) {
for (j=0; j<TAS3001C_BIQUAD_FILTER_COUNT; j++) {
tas3001c_write_biquad(self, i, j, &tas3001c_eq_unity);
}
}
tas3001c_fast_load(self, 0);
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
return;
}
for (i=0; tas3001c_eq_prefs[i]; i++) {
struct tas_eq_pref_t *eq = tas3001c_eq_prefs[i];
if (eq->device_id == self->device_id &&
(eq->output_id == 0 || eq->output_id == self->output_id) &&
(eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) {
tas3001c_update_drce(self, TAS_DRCE_ALL, eq->drce);
tas3001c_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads);
break;
}
}
}
static void
tas3001c_device_change_handler(struct work_struct *work)
{
struct tas3001c_data_t *self;
self = container_of(work, struct tas3001c_data_t, change);
tas3001c_update_device_parameters(self);
}
static int
tas3001c_output_device_change( struct tas3001c_data_t *self,
int device_id,
int output_id,
int speaker_id)
{
self->device_id=device_id;
self->output_id=output_id;
self->speaker_id=speaker_id;
schedule_work(&self->change);
return 0;
}
static int
tas3001c_device_ioctl( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
uint __user *argp = (void __user *)arg;
switch (cmd) {
case TAS_READ_EQ:
case TAS_WRITE_EQ:
return tas3001c_eq_rw(self, cmd, arg);
case TAS_READ_EQ_LIST:
case TAS_WRITE_EQ_LIST:
return tas3001c_eq_list_rw(self, cmd, arg);
case TAS_READ_EQ_FILTER_COUNT:
put_user(TAS3001C_BIQUAD_FILTER_COUNT, argp);
return 0;
case TAS_READ_EQ_CHANNEL_COUNT:
put_user(TAS3001C_BIQUAD_CHANNEL_COUNT, argp);
return 0;
case TAS_READ_DRCE:
case TAS_WRITE_DRCE:
return tas3001c_drce_rw(self, cmd, arg);
case TAS_READ_DRCE_CAPS:
put_user(TAS_DRCE_ENABLE | TAS_DRCE_THRESHOLD, argp);
return 0;
case TAS_READ_DRCE_MIN:
case TAS_READ_DRCE_MAX: {
struct tas_drce_ctrl_t drce_ctrl;
if (copy_from_user(&drce_ctrl, argp,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) {
if (cmd == TAS_READ_DRCE_MIN) {
drce_ctrl.data.threshold=-36<<8;
} else {
drce_ctrl.data.threshold=-6<<8;
}
}
if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
}
return -EINVAL;
}
static int
tas3001c_init_mixer(struct tas3001c_data_t *self)
{
unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
/* Make sure something answers on the i2c bus */
if (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,
WRITE_NORMAL|FORCE_WRITE) < 0)
return -1;
tas3001c_fast_load(self, 1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD6);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD6);
tas3001c_fast_load(self, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT);
tas3001c_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT);
tas3001c_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT);
tas3001c_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT);
return 0;
}
static int
tas3001c_uninit_mixer(struct tas3001c_data_t *self)
{
tas3001c_set_mixer_level(self, SOUND_MIXER_VOLUME, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_PCM, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_BASS, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_TREBLE, 0);
return 0;
}
static int
tas3001c_init(struct i2c_client *client)
{
struct tas3001c_data_t *self;
size_t sz = sizeof(*self) + (TAS3001C_REG_MAX*sizeof(tas_shadow_t));
int i, j;
self = kzalloc(sz, GFP_KERNEL);
if (!self)
return -ENOMEM;
self->super.client = client;
self->super.shadow = (tas_shadow_t *)(self+1);
self->output_id = TAS_OUTPUT_HEADPHONES;
dev_set_drvdata(&client->dev, self);
for (i = 0; i < TAS3001C_BIQUAD_CHANNEL_COUNT; i++)
for (j = 0; j < TAS3001C_BIQUAD_FILTER_COUNT; j++)
tas3001c_write_biquad_shadow(self, i, j,
&tas3001c_eq_unity);
INIT_WORK(&self->change, tas3001c_device_change_handler);
return 0;
}
static void
tas3001c_uninit(struct tas3001c_data_t *self)
{
tas3001c_uninit_mixer(self);
kfree(self);
}
struct tas_driver_hooks_t tas3001c_hooks = {
.init = (tas_hook_init_t)tas3001c_init,
.post_init = (tas_hook_post_init_t)tas3001c_init_mixer,
.uninit = (tas_hook_uninit_t)tas3001c_uninit,
.get_mixer_level = (tas_hook_get_mixer_level_t)tas3001c_get_mixer_level,
.set_mixer_level = (tas_hook_set_mixer_level_t)tas3001c_set_mixer_level,
.enter_sleep = (tas_hook_enter_sleep_t)tas3001c_enter_sleep,
.leave_sleep = (tas_hook_leave_sleep_t)tas3001c_leave_sleep,
.supported_mixers = (tas_hook_supported_mixers_t)tas3001c_supported_mixers,
.mixer_is_stereo = (tas_hook_mixer_is_stereo_t)tas3001c_mixer_is_stereo,
.stereo_mixers = (tas_hook_stereo_mixers_t)tas3001c_stereo_mixers,
.output_device_change = (tas_hook_output_device_change_t)tas3001c_output_device_change,
.device_ioctl = (tas_hook_device_ioctl_t)tas3001c_device_ioctl
};
/*
* Header file for the i2c/i2s based TA3001c sound chip used
* on some Apple hardware. Also known as "tumbler".
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* Written by Christopher C. Chimelis <chris@debian.org>
*/
#ifndef _TAS3001C_H_
#define _TAS3001C_H_
#include <linux/types.h>
#include "tas_common.h"
#include "tas_eq_prefs.h"
/*
* Macros that correspond to the registers that we write to
* when setting the various values.
*/
#define TAS3001C_VERSION "0.3"
#define TAS3001C_DATE "20011214"
#define I2C_DRIVERNAME_TAS3001C "TAS3001c driver V " TAS3001C_VERSION
#define I2C_DRIVERID_TAS3001C (I2C_DRIVERID_TAS_BASE+0)
extern struct tas_driver_hooks_t tas3001c_hooks;
extern struct tas_gain_t tas3001c_gain;
extern struct tas_eq_pref_t *tas3001c_eq_prefs[];
enum tas3001c_reg_t {
TAS3001C_REG_MCR = 0x01,
TAS3001C_REG_DRC = 0x02,
TAS3001C_REG_VOLUME = 0x04,
TAS3001C_REG_TREBLE = 0x05,
TAS3001C_REG_BASS = 0x06,
TAS3001C_REG_MIXER1 = 0x07,
TAS3001C_REG_MIXER2 = 0x08,
TAS3001C_REG_LEFT_BIQUAD0 = 0x0a,
TAS3001C_REG_LEFT_BIQUAD1 = 0x0b,
TAS3001C_REG_LEFT_BIQUAD2 = 0x0c,
TAS3001C_REG_LEFT_BIQUAD3 = 0x0d,
TAS3001C_REG_LEFT_BIQUAD4 = 0x0e,
TAS3001C_REG_LEFT_BIQUAD5 = 0x0f,
TAS3001C_REG_LEFT_BIQUAD6 = 0x10,
TAS3001C_REG_RIGHT_BIQUAD0 = 0x13,
TAS3001C_REG_RIGHT_BIQUAD1 = 0x14,
TAS3001C_REG_RIGHT_BIQUAD2 = 0x15,
TAS3001C_REG_RIGHT_BIQUAD3 = 0x16,
TAS3001C_REG_RIGHT_BIQUAD4 = 0x17,
TAS3001C_REG_RIGHT_BIQUAD5 = 0x18,
TAS3001C_REG_RIGHT_BIQUAD6 = 0x19,
TAS3001C_REG_MAX = 0x20
};
#endif /* _TAS3001C_H_ */
#include "tas_common.h"
#include "tas_eq_prefs.h"
static struct tas_drce_t eqp_0e_2_1_drce = {
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0e_2_1_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
};
static struct tas_eq_pref_t eqp_0e_2_1 = {
.sample_rate = 44100,
.device_id = 0x0e,
.output_id = TAS_OUTPUT_EXTERNAL_SPKR,
.speaker_id = 0x01,
.drce = &eqp_0e_2_1_drce,
.filter_count = 12,
.biquads = eqp_0e_2_1_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_10_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -12.46 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_10_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0F4A12, 0xE16BDA, 0x0F4A12, 0xE173F0, 0x0E9C3A } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x02DD54, 0x05BAA8, 0x02DD54, 0xF8001D, 0x037532 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0E2FC7, 0xE4D5DC, 0x0D7477, 0xE4D5DC, 0x0BA43F } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0E7899, 0xE67CCA, 0x0D0E93, 0xE67CCA, 0x0B872D } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0F4A12, 0xE16BDA, 0x0F4A12, 0xE173F0, 0x0E9C3A } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x02DD54, 0x05BAA8, 0x02DD54, 0xF8001D, 0x037532 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0E2FC7, 0xE4D5DC, 0x0D7477, 0xE4D5DC, 0x0BA43F } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0E7899, 0xE67CCA, 0x0D0E93, 0xE67CCA, 0x0B872D } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
};
static struct tas_eq_pref_t eqp_10_1_0 = {
.sample_rate = 44100,
.device_id = 0x10,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_10_1_0_drce,
.filter_count = 12,
.biquads = eqp_10_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_15_2_1_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_15_2_1_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
};
static struct tas_eq_pref_t eqp_15_2_1 = {
.sample_rate = 44100,
.device_id = 0x15,
.output_id = TAS_OUTPUT_EXTERNAL_SPKR,
.speaker_id = 0x01,
.drce = &eqp_15_2_1_drce,
.filter_count = 12,
.biquads = eqp_15_2_1_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_15_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = 0.0 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_15_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FAD08, 0xE0A5EF, 0x0FAD08, 0xE0A79D, 0x0F5BBE } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x04B38D, 0x09671B, 0x04B38D, 0x000F71, 0x02BEC5 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FDD32, 0xE0A56F, 0x0F8A69, 0xE0A56F, 0x0F679C } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0FD284, 0xE135FB, 0x0F2161, 0xE135FB, 0x0EF3E5 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0E81B1, 0xE6283F, 0x0CE49D, 0xE6283F, 0x0B664F } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0F2D62, 0xE98797, 0x0D1E19, 0xE98797, 0x0C4B7B } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FAD08, 0xE0A5EF, 0x0FAD08, 0xE0A79D, 0x0F5BBE } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x04B38D, 0x09671B, 0x04B38D, 0x000F71, 0x02BEC5 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FDD32, 0xE0A56F, 0x0F8A69, 0xE0A56F, 0x0F679C } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0FD284, 0xE135FB, 0x0F2161, 0xE135FB, 0x0EF3E5 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0E81B1, 0xE6283F, 0x0CE49D, 0xE6283F, 0x0B664F } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0F2D62, 0xE98797, 0x0D1E19, 0xE98797, 0x0C4B7B } } },
};
static struct tas_eq_pref_t eqp_15_1_0 = {
.sample_rate = 44100,
.device_id = 0x15,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_15_1_0_drce,
.filter_count = 12,
.biquads = eqp_15_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_0f_2_1_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0f_2_1_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
};
static struct tas_eq_pref_t eqp_0f_2_1 = {
.sample_rate = 44100,
.device_id = 0x0f,
.output_id = TAS_OUTPUT_EXTERNAL_SPKR,
.speaker_id = 0x01,
.drce = &eqp_0f_2_1_drce,
.filter_count = 12,
.biquads = eqp_0f_2_1_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_0f_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0f_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
};
static struct tas_eq_pref_t eqp_0f_1_0 = {
.sample_rate = 44100,
.device_id = 0x0f,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_0f_1_0_drce,
.filter_count = 12,
.biquads = eqp_0f_1_0_biquads
};
/* ======================================================================== */
static uint tas3001c_master_tab[]={
0x0, 0x75, 0x9c, 0xbb,
0xdb, 0xfb, 0x11e, 0x143,
0x16b, 0x196, 0x1c3, 0x1f5,
0x229, 0x263, 0x29f, 0x2e1,
0x328, 0x373, 0x3c5, 0x41b,
0x478, 0x4dc, 0x547, 0x5b8,
0x633, 0x6b5, 0x740, 0x7d5,
0x873, 0x91c, 0x9d2, 0xa92,
0xb5e, 0xc39, 0xd22, 0xe19,
0xf20, 0x1037, 0x1161, 0x129e,
0x13ed, 0x1551, 0x16ca, 0x185d,
0x1a08, 0x1bcc, 0x1dac, 0x1fa7,
0x21c1, 0x23fa, 0x2655, 0x28d6,
0x2b7c, 0x2e4a, 0x3141, 0x3464,
0x37b4, 0x3b35, 0x3ee9, 0x42d3,
0x46f6, 0x4b53, 0x4ff0, 0x54ce,
0x59f2, 0x5f5f, 0x6519, 0x6b24,
0x7183, 0x783c, 0x7f53, 0x86cc,
0x8ead, 0x96fa, 0x9fba, 0xa8f2,
0xb2a7, 0xbce1, 0xc7a5, 0xd2fa,
0xdee8, 0xeb75, 0xf8aa, 0x1068e,
0x1152a, 0x12487, 0x134ad, 0x145a5,
0x1577b, 0x16a37, 0x17df5, 0x192bd,
0x1a890, 0x1bf7b, 0x1d78d, 0x1f0d1,
0x20b55, 0x22727, 0x24456, 0x262f2,
0x2830b
};
static uint tas3001c_mixer_tab[]={
0x0, 0x748, 0x9be, 0xbaf,
0xda4, 0xfb1, 0x11de, 0x1431,
0x16ad, 0x1959, 0x1c37, 0x1f4b,
0x2298, 0x2628, 0x29fb, 0x2e12,
0x327d, 0x3734, 0x3c47, 0x41b4,
0x4787, 0x4dbe, 0x546d, 0x5b86,
0x632e, 0x6b52, 0x7400, 0x7d54,
0x873b, 0x91c6, 0x9d1a, 0xa920,
0xb5e5, 0xc38c, 0xd21b, 0xe18f,
0xf1f5, 0x1036a, 0x1160f, 0x129d6,
0x13ed0, 0x1550c, 0x16ca0, 0x185c9,
0x1a07b, 0x1bcc3, 0x1dab9, 0x1fa75,
0x21c0f, 0x23fa3, 0x26552, 0x28d64,
0x2b7c9, 0x2e4a2, 0x31411, 0x3463b,
0x37b44, 0x3b353, 0x3ee94, 0x42d30,
0x46f55, 0x4b533, 0x4fefc, 0x54ce5,
0x59f25, 0x5f5f6, 0x65193, 0x6b23c,
0x71835, 0x783c3, 0x7f52c, 0x86cc0,
0x8eacc, 0x96fa5, 0x9fba0, 0xa8f1a,
0xb2a71, 0xbce0a, 0xc7a4a, 0xd2fa0,
0xdee7b, 0xeb752, 0xf8a9f, 0x1068e4,
0x1152a3, 0x12486a, 0x134ac8, 0x145a55,
0x1577ac, 0x16a370, 0x17df51, 0x192bc2,
0x1a88f8, 0x1bf7b7, 0x1d78c9, 0x1f0d04,
0x20b542, 0x227268, 0x244564, 0x262f26,
0x2830af
};
static uint tas3001c_treble_tab[]={
0x96, 0x95, 0x95, 0x94,
0x93, 0x92, 0x92, 0x91,
0x90, 0x90, 0x8f, 0x8e,
0x8d, 0x8d, 0x8c, 0x8b,
0x8a, 0x8a, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85,
0x85, 0x84, 0x83, 0x83,
0x82, 0x81, 0x80, 0x80,
0x7f, 0x7e, 0x7e, 0x7d,
0x7c, 0x7b, 0x7b, 0x7a,
0x79, 0x78, 0x78, 0x77,
0x76, 0x76, 0x75, 0x74,
0x73, 0x73, 0x72, 0x71,
0x71, 0x70, 0x6e, 0x6d,
0x6d, 0x6c, 0x6b, 0x6a,
0x69, 0x68, 0x67, 0x66,
0x65, 0x63, 0x62, 0x62,
0x60, 0x5f, 0x5d, 0x5c,
0x5a, 0x58, 0x56, 0x55,
0x53, 0x51, 0x4f, 0x4c,
0x4a, 0x48, 0x45, 0x43,
0x40, 0x3d, 0x3a, 0x37,
0x35, 0x32, 0x2e, 0x2a,
0x27, 0x22, 0x1e, 0x1a,
0x15, 0x11, 0xc, 0x7,
0x1
};
static uint tas3001c_bass_tab[]={
0x86, 0x83, 0x81, 0x7f,
0x7d, 0x7b, 0x79, 0x78,
0x76, 0x75, 0x74, 0x72,
0x71, 0x6f, 0x6e, 0x6d,
0x6c, 0x6b, 0x69, 0x67,
0x65, 0x64, 0x61, 0x60,
0x5e, 0x5d, 0x5c, 0x5b,
0x5a, 0x59, 0x58, 0x57,
0x56, 0x55, 0x55, 0x54,
0x53, 0x52, 0x50, 0x4f,
0x4d, 0x4c, 0x4b, 0x49,
0x47, 0x45, 0x44, 0x42,
0x41, 0x3f, 0x3e, 0x3d,
0x3c, 0x3b, 0x39, 0x38,
0x37, 0x36, 0x35, 0x34,
0x33, 0x31, 0x30, 0x2f,
0x2e, 0x2c, 0x2b, 0x2b,
0x29, 0x28, 0x27, 0x26,
0x25, 0x24, 0x22, 0x21,
0x20, 0x1e, 0x1c, 0x19,
0x18, 0x18, 0x17, 0x16,
0x15, 0x14, 0x13, 0x12,
0x11, 0x10, 0xf, 0xe,
0xd, 0xb, 0xa, 0x9,
0x8, 0x6, 0x4, 0x2,
0x1
};
struct tas_gain_t tas3001c_gain = {
.master = tas3001c_master_tab,
.treble = tas3001c_treble_tab,
.bass = tas3001c_bass_tab,
.mixer = tas3001c_mixer_tab
};
struct tas_eq_pref_t *tas3001c_eq_prefs[]={
&eqp_0e_2_1,
&eqp_10_1_0,
&eqp_15_2_1,
&eqp_15_1_0,
&eqp_0f_2_1,
&eqp_0f_1_0,
NULL
};
/*
* Driver for the i2c/i2s based TA3004 sound chip used
* on some Apple hardware. Also known as "snapper".
*
* Tobias Sargeant <tobias.sargeant@bigpond.com>
* Based upon tas3001c.c by Christopher C. Chimelis <chris@debian.org>:
*
* Input support by Renzo Davoli <renzo@cs.unibo.it>
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/prom.h>
#include "dmasound.h"
#include "tas_common.h"
#include "tas3004.h"
#include "tas_ioctl.h"
/* #define DEBUG_DRCE */
#define TAS3004_BIQUAD_FILTER_COUNT 7
#define TAS3004_BIQUAD_CHANNEL_COUNT 2
#define VOL_DEFAULT (100 * 4 / 5)
#define INPUT_DEFAULT (100 * 4 / 5)
#define BASS_DEFAULT (100 / 2)
#define TREBLE_DEFAULT (100 / 2)
struct tas3004_data_t {
struct tas_data_t super;
int device_id;
int output_id;
int speaker_id;
struct tas_drce_t drce_state;
struct work_struct change;
};
#define MAKE_TIME(sec,usec) (((sec)<<12) + (50000+(usec/10)*(1<<12))/100000)
#define MAKE_RATIO(i,f) (((i)<<8) + ((500+(f)*(1<<8))/1000))
static const union tas_biquad_t tas3004_eq_unity = {
.buf = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 },
};
static const struct tas_drce_t tas3004_drce_min = {
.enable = 1,
.above = { .val = MAKE_RATIO(16,0), .expand = 0 },
.below = { .val = MAKE_RATIO(2,0), .expand = 0 },
.threshold = -0x59a0,
.energy = MAKE_TIME(0, 1700),
.attack = MAKE_TIME(0, 1700),
.decay = MAKE_TIME(0, 1700),
};
static const struct tas_drce_t tas3004_drce_max = {
.enable = 1,
.above = { .val = MAKE_RATIO(1,500), .expand = 1 },
.below = { .val = MAKE_RATIO(2,0), .expand = 1 },
.threshold = -0x0,
.energy = MAKE_TIME(2,400000),
.attack = MAKE_TIME(2,400000),
.decay = MAKE_TIME(2,400000),
};
static const unsigned short time_constants[]={
MAKE_TIME(0, 1700),
MAKE_TIME(0, 3500),
MAKE_TIME(0, 6700),
MAKE_TIME(0, 13000),
MAKE_TIME(0, 26000),
MAKE_TIME(0, 53000),
MAKE_TIME(0,106000),
MAKE_TIME(0,212000),
MAKE_TIME(0,425000),
MAKE_TIME(0,850000),
MAKE_TIME(1,700000),
MAKE_TIME(2,400000),
};
static const unsigned short above_threshold_compression_ratio[]={
MAKE_RATIO( 1, 70),
MAKE_RATIO( 1,140),
MAKE_RATIO( 1,230),
MAKE_RATIO( 1,330),
MAKE_RATIO( 1,450),
MAKE_RATIO( 1,600),
MAKE_RATIO( 1,780),
MAKE_RATIO( 2, 0),
MAKE_RATIO( 2,290),
MAKE_RATIO( 2,670),
MAKE_RATIO( 3,200),
MAKE_RATIO( 4, 0),
MAKE_RATIO( 5,330),
MAKE_RATIO( 8, 0),
MAKE_RATIO(16, 0),
};
static const unsigned short above_threshold_expansion_ratio[]={
MAKE_RATIO(1, 60),
MAKE_RATIO(1,130),
MAKE_RATIO(1,190),
MAKE_RATIO(1,250),
MAKE_RATIO(1,310),
MAKE_RATIO(1,380),
MAKE_RATIO(1,440),
MAKE_RATIO(1,500)
};
static const unsigned short below_threshold_compression_ratio[]={
MAKE_RATIO(1, 70),
MAKE_RATIO(1,140),
MAKE_RATIO(1,230),
MAKE_RATIO(1,330),
MAKE_RATIO(1,450),
MAKE_RATIO(1,600),
MAKE_RATIO(1,780),
MAKE_RATIO(2, 0)
};
static const unsigned short below_threshold_expansion_ratio[]={
MAKE_RATIO(1, 60),
MAKE_RATIO(1,130),
MAKE_RATIO(1,190),
MAKE_RATIO(1,250),
MAKE_RATIO(1,310),
MAKE_RATIO(1,380),
MAKE_RATIO(1,440),
MAKE_RATIO(1,500),
MAKE_RATIO(1,560),
MAKE_RATIO(1,630),
MAKE_RATIO(1,690),
MAKE_RATIO(1,750),
MAKE_RATIO(1,810),
MAKE_RATIO(1,880),
MAKE_RATIO(1,940),
MAKE_RATIO(2, 0)
};
static inline int
search( unsigned short val,
const unsigned short *arr,
const int arrsize) {
/*
* This could be a binary search, but for small tables,
* a linear search is likely to be faster
*/
int i;
for (i=0; i < arrsize; i++)
if (arr[i] >= val)
goto _1;
return arrsize-1;
_1:
if (i == 0)
return 0;
return (arr[i]-val < val-arr[i-1]) ? i : i-1;
}
#define SEARCH(a, b) search(a, b, ARRAY_SIZE(b))
static inline int
time_index(unsigned short time)
{
return SEARCH(time, time_constants);
}
static inline int
above_threshold_compression_index(unsigned short ratio)
{
return SEARCH(ratio, above_threshold_compression_ratio);
}
static inline int
above_threshold_expansion_index(unsigned short ratio)
{
return SEARCH(ratio, above_threshold_expansion_ratio);
}
static inline int
below_threshold_compression_index(unsigned short ratio)
{
return SEARCH(ratio, below_threshold_compression_ratio);
}
static inline int
below_threshold_expansion_index(unsigned short ratio)
{
return SEARCH(ratio, below_threshold_expansion_ratio);
}
static inline unsigned char db_to_regval(short db) {
int r=0;
r=(db+0x59a0) / 0x60;
if (r < 0x91) return 0x91;
if (r > 0xef) return 0xef;
return r;
}
static inline short quantize_db(short db)
{
return db_to_regval(db) * 0x60 - 0x59a0;
}
static inline int
register_width(enum tas3004_reg_t r)
{
switch(r) {
case TAS3004_REG_MCR:
case TAS3004_REG_TREBLE:
case TAS3004_REG_BASS:
case TAS3004_REG_ANALOG_CTRL:
case TAS3004_REG_TEST1:
case TAS3004_REG_TEST2:
case TAS3004_REG_MCR2:
return 1;
case TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN:
case TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN:
return 3;
case TAS3004_REG_DRC:
case TAS3004_REG_VOLUME:
return 6;
case TAS3004_REG_LEFT_MIXER:
case TAS3004_REG_RIGHT_MIXER:
return 9;
case TAS3004_REG_TEST:
return 10;
case TAS3004_REG_LEFT_BIQUAD0:
case TAS3004_REG_LEFT_BIQUAD1:
case TAS3004_REG_LEFT_BIQUAD2:
case TAS3004_REG_LEFT_BIQUAD3:
case TAS3004_REG_LEFT_BIQUAD4:
case TAS3004_REG_LEFT_BIQUAD5:
case TAS3004_REG_LEFT_BIQUAD6:
case TAS3004_REG_RIGHT_BIQUAD0:
case TAS3004_REG_RIGHT_BIQUAD1:
case TAS3004_REG_RIGHT_BIQUAD2:
case TAS3004_REG_RIGHT_BIQUAD3:
case TAS3004_REG_RIGHT_BIQUAD4:
case TAS3004_REG_RIGHT_BIQUAD5:
case TAS3004_REG_RIGHT_BIQUAD6:
case TAS3004_REG_LEFT_LOUD_BIQUAD:
case TAS3004_REG_RIGHT_LOUD_BIQUAD:
return 15;
default:
return 0;
}
}
static int
tas3004_write_register( struct tas3004_data_t *self,
enum tas3004_reg_t reg_num,
char *data,
uint write_mode)
{
if (reg_num==TAS3004_REG_MCR ||
reg_num==TAS3004_REG_BASS ||
reg_num==TAS3004_REG_TREBLE ||
reg_num==TAS3004_REG_ANALOG_CTRL) {
return tas_write_byte_register(&self->super,
(uint)reg_num,
*data,
write_mode);
} else {
return tas_write_register(&self->super,
(uint)reg_num,
register_width(reg_num),
data,
write_mode);
}
}
static int
tas3004_sync_register( struct tas3004_data_t *self,
enum tas3004_reg_t reg_num)
{
if (reg_num==TAS3004_REG_MCR ||
reg_num==TAS3004_REG_BASS ||
reg_num==TAS3004_REG_TREBLE ||
reg_num==TAS3004_REG_ANALOG_CTRL) {
return tas_sync_byte_register(&self->super,
(uint)reg_num,
register_width(reg_num));
} else {
return tas_sync_register(&self->super,
(uint)reg_num,
register_width(reg_num));
}
}
static int
tas3004_read_register( struct tas3004_data_t *self,
enum tas3004_reg_t reg_num,
char *data,
uint write_mode)
{
return tas_read_register(&self->super,
(uint)reg_num,
register_width(reg_num),
data);
}
static inline int
tas3004_fast_load(struct tas3004_data_t *self, int fast)
{
if (fast)
self->super.shadow[TAS3004_REG_MCR][0] |= 0x80;
else
self->super.shadow[TAS3004_REG_MCR][0] &= 0x7f;
return tas3004_sync_register(self,TAS3004_REG_MCR);
}
static uint
tas3004_supported_mixers(struct tas3004_data_t *self)
{
return SOUND_MASK_VOLUME |
SOUND_MASK_PCM |
SOUND_MASK_ALTPCM |
SOUND_MASK_IMIX |
SOUND_MASK_TREBLE |
SOUND_MASK_BASS |
SOUND_MASK_MIC |
SOUND_MASK_LINE;
}
static int
tas3004_mixer_is_stereo(struct tas3004_data_t *self, int mixer)
{
switch(mixer) {
case SOUND_MIXER_VOLUME:
case SOUND_MIXER_PCM:
case SOUND_MIXER_ALTPCM:
case SOUND_MIXER_IMIX:
return 1;
default:
return 0;
}
}
static uint
tas3004_stereo_mixers(struct tas3004_data_t *self)
{
uint r = tas3004_supported_mixers(self);
uint i;
for (i=1; i<SOUND_MIXER_NRDEVICES; i++)
if (r&(1<<i) && !tas3004_mixer_is_stereo(self,i))
r &= ~(1<<i);
return r;
}
static int
tas3004_get_mixer_level(struct tas3004_data_t *self, int mixer, uint *level)
{
if (!self)
return -1;
*level = self->super.mixer[mixer];
return 0;
}
static int
tas3004_set_mixer_level(struct tas3004_data_t *self, int mixer, uint level)
{
int rc;
tas_shadow_t *shadow;
uint temp;
uint offset=0;
if (!self)
return -1;
shadow = self->super.shadow;
if (!tas3004_mixer_is_stereo(self,mixer))
level = tas_mono_to_stereo(level);
switch(mixer) {
case SOUND_MIXER_VOLUME:
temp = tas3004_gain.master[level&0xff];
SET_4_20(shadow[TAS3004_REG_VOLUME], 0, temp);
temp = tas3004_gain.master[(level>>8)&0xff];
SET_4_20(shadow[TAS3004_REG_VOLUME], 3, temp);
rc = tas3004_sync_register(self,TAS3004_REG_VOLUME);
break;
case SOUND_MIXER_IMIX:
offset += 3;
case SOUND_MIXER_ALTPCM:
offset += 3;
case SOUND_MIXER_PCM:
/*
* Don't load these in fast mode. The documentation
* says it can be done in either mode, but testing it
* shows that fast mode produces ugly clicking.
*/
/* tas3004_fast_load(self,1); */
temp = tas3004_gain.mixer[level&0xff];
SET_4_20(shadow[TAS3004_REG_LEFT_MIXER], offset, temp);
temp = tas3004_gain.mixer[(level>>8)&0xff];
SET_4_20(shadow[TAS3004_REG_RIGHT_MIXER], offset, temp);
rc = tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER);
if (rc == 0)
rc=tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER);
/* tas3004_fast_load(self,0); */
break;
case SOUND_MIXER_TREBLE:
temp = tas3004_gain.treble[level&0xff];
shadow[TAS3004_REG_TREBLE][0]=temp&0xff;
rc = tas3004_sync_register(self,TAS3004_REG_TREBLE);
break;
case SOUND_MIXER_BASS:
temp = tas3004_gain.bass[level&0xff];
shadow[TAS3004_REG_BASS][0]=temp&0xff;
rc = tas3004_sync_register(self,TAS3004_REG_BASS);
break;
case SOUND_MIXER_MIC:
if ((level&0xff)>0) {
software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff);
if (self->super.mixer[mixer] == 0) {
self->super.mixer[SOUND_MIXER_LINE] = 0;
shadow[TAS3004_REG_ANALOG_CTRL][0]=0xc2;
rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL);
} else rc=0;
} else {
self->super.mixer[SOUND_MIXER_LINE] = SW_INPUT_VOLUME_DEFAULT;
software_input_volume = SW_INPUT_VOLUME_SCALE *
(self->super.mixer[SOUND_MIXER_LINE]&0xff);
shadow[TAS3004_REG_ANALOG_CTRL][0]=0x00;
rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL);
}
break;
case SOUND_MIXER_LINE:
if (self->super.mixer[SOUND_MIXER_MIC] == 0) {
software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff);
rc=0;
}
break;
default:
rc = -1;
break;
}
if (rc < 0)
return rc;
self->super.mixer[mixer] = level;
return 0;
}
static int
tas3004_leave_sleep(struct tas3004_data_t *self)
{
unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
if (!self)
return -1;
/* Make sure something answers on the i2c bus */
if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr,
WRITE_NORMAL | FORCE_WRITE) < 0)
return -1;
tas3004_fast_load(self, 1);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6);
tas3004_fast_load(self, 0);
(void)tas3004_sync_register(self,TAS3004_REG_VOLUME);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER);
(void)tas3004_sync_register(self,TAS3004_REG_TREBLE);
(void)tas3004_sync_register(self,TAS3004_REG_BASS);
(void)tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL);
return 0;
}
static int
tas3004_enter_sleep(struct tas3004_data_t *self)
{
if (!self)
return -1;
return 0;
}
static int
tas3004_sync_biquad( struct tas3004_data_t *self,
u_int channel,
u_int filter)
{
enum tas3004_reg_t reg;
if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter;
return tas3004_sync_register(self,reg);
}
static int
tas3004_write_biquad_shadow( struct tas3004_data_t *self,
u_int channel,
u_int filter,
const union tas_biquad_t *biquad)
{
tas_shadow_t *shadow=self->super.shadow;
enum tas3004_reg_t reg;
if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter;
SET_4_20(shadow[reg], 0,biquad->coeff.b0);
SET_4_20(shadow[reg], 3,biquad->coeff.b1);
SET_4_20(shadow[reg], 6,biquad->coeff.b2);
SET_4_20(shadow[reg], 9,biquad->coeff.a1);
SET_4_20(shadow[reg],12,biquad->coeff.a2);
return 0;
}
static int
tas3004_write_biquad( struct tas3004_data_t *self,
u_int channel,
u_int filter,
const union tas_biquad_t *biquad)
{
int rc;
rc=tas3004_write_biquad_shadow(self, channel, filter, biquad);
if (rc < 0) return rc;
return tas3004_sync_biquad(self, channel, filter);
}
static int
tas3004_write_biquad_list( struct tas3004_data_t *self,
u_int filter_count,
u_int flags,
struct tas_biquad_ctrl_t *biquads)
{
int i;
int rc;
if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1);
for (i=0; i<filter_count; i++) {
rc=tas3004_write_biquad(self,
biquads[i].channel,
biquads[i].filter,
&biquads[i].data);
if (rc < 0) break;
}
if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,0);
return rc;
}
static int
tas3004_read_biquad( struct tas3004_data_t *self,
u_int channel,
u_int filter,
union tas_biquad_t *biquad)
{
tas_shadow_t *shadow=self->super.shadow;
enum tas3004_reg_t reg;
if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter;
biquad->coeff.b0=GET_4_20(shadow[reg], 0);
biquad->coeff.b1=GET_4_20(shadow[reg], 3);
biquad->coeff.b2=GET_4_20(shadow[reg], 6);
biquad->coeff.a1=GET_4_20(shadow[reg], 9);
biquad->coeff.a2=GET_4_20(shadow[reg],12);
return 0;
}
static int
tas3004_eq_rw( struct tas3004_data_t *self,
u_int cmd,
u_long arg)
{
void __user *argp = (void __user *)arg;
int rc;
struct tas_biquad_ctrl_t biquad;
if (copy_from_user((void *)&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
if (cmd & SIOC_IN) {
rc=tas3004_write_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
}
if (cmd & SIOC_OUT) {
rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
}
return 0;
}
static int
tas3004_eq_list_rw( struct tas3004_data_t *self,
u_int cmd,
u_long arg)
{
int rc = 0;
int filter_count;
int flags;
int i,j;
char sync_required[TAS3004_BIQUAD_CHANNEL_COUNT][TAS3004_BIQUAD_FILTER_COUNT];
struct tas_biquad_ctrl_t biquad;
struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg;
memset(sync_required,0,sizeof(sync_required));
if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int)))
return -EFAULT;
if (copy_from_user(&flags, &argp->flags, sizeof(int)))
return -EFAULT;
if (cmd & SIOC_IN) {
}
for (i=0; i < filter_count; i++) {
if (copy_from_user(&biquad, &argp->biquads[i],
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
if (cmd & SIOC_IN) {
sync_required[biquad.channel][biquad.filter]=1;
rc=tas3004_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
}
if (cmd & SIOC_OUT) {
rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
if (copy_to_user(&argp->biquads[i], &biquad,
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
}
}
if (cmd & SIOC_IN) {
/*
* This is OK for the tas3004. For the
* tas3001c, going into fast load mode causes
* the treble and bass to be reset to 0dB, and
* volume controls to be muted.
*/
if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1);
for (i=0; i<TAS3004_BIQUAD_CHANNEL_COUNT; i++) {
for (j=0; j<TAS3004_BIQUAD_FILTER_COUNT; j++) {
if (sync_required[i][j]) {
rc=tas3004_sync_biquad(self, i, j);
if (rc < 0) goto out;
}
}
}
out:
if (flags & TAS_BIQUAD_FAST_LOAD)
tas3004_fast_load(self,0);
}
return rc;
}
static int
tas3004_update_drce( struct tas3004_data_t *self,
int flags,
struct tas_drce_t *drce)
{
tas_shadow_t *shadow;
int i;
shadow=self->super.shadow;
if (flags & TAS_DRCE_ABOVE_RATIO) {
self->drce_state.above.expand = drce->above.expand;
if (drce->above.val == (1<<8)) {
self->drce_state.above.val = 1<<8;
shadow[TAS3004_REG_DRC][0] = 0x02;
} else if (drce->above.expand) {
i=above_threshold_expansion_index(drce->above.val);
self->drce_state.above.val=above_threshold_expansion_ratio[i];
shadow[TAS3004_REG_DRC][0] = 0x0a + (i<<3);
} else {
i=above_threshold_compression_index(drce->above.val);
self->drce_state.above.val=above_threshold_compression_ratio[i];
shadow[TAS3004_REG_DRC][0] = 0x08 + (i<<3);
}
}
if (flags & TAS_DRCE_BELOW_RATIO) {
self->drce_state.below.expand = drce->below.expand;
if (drce->below.val == (1<<8)) {
self->drce_state.below.val = 1<<8;
shadow[TAS3004_REG_DRC][1] = 0x02;
} else if (drce->below.expand) {
i=below_threshold_expansion_index(drce->below.val);
self->drce_state.below.val=below_threshold_expansion_ratio[i];
shadow[TAS3004_REG_DRC][1] = 0x08 + (i<<3);
} else {
i=below_threshold_compression_index(drce->below.val);
self->drce_state.below.val=below_threshold_compression_ratio[i];
shadow[TAS3004_REG_DRC][1] = 0x0a + (i<<3);
}
}
if (flags & TAS_DRCE_THRESHOLD) {
self->drce_state.threshold=quantize_db(drce->threshold);
shadow[TAS3004_REG_DRC][2] = db_to_regval(self->drce_state.threshold);
}
if (flags & TAS_DRCE_ENERGY) {
i=time_index(drce->energy);
self->drce_state.energy=time_constants[i];
shadow[TAS3004_REG_DRC][3] = 0x40 + (i<<4);
}
if (flags & TAS_DRCE_ATTACK) {
i=time_index(drce->attack);
self->drce_state.attack=time_constants[i];
shadow[TAS3004_REG_DRC][4] = 0x40 + (i<<4);
}
if (flags & TAS_DRCE_DECAY) {
i=time_index(drce->decay);
self->drce_state.decay=time_constants[i];
shadow[TAS3004_REG_DRC][5] = 0x40 + (i<<4);
}
if (flags & TAS_DRCE_ENABLE) {
self->drce_state.enable = drce->enable;
}
if (!self->drce_state.enable) {
shadow[TAS3004_REG_DRC][0] |= 0x01;
}
#ifdef DEBUG_DRCE
printk("DRCE: set [ ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n",
self->drce_state.enable,
self->drce_state.above.expand,self->drce_state.above.val,
self->drce_state.below.expand,self->drce_state.below.val,
self->drce_state.threshold,
self->drce_state.energy,
self->drce_state.attack,
self->drce_state.decay);
printk("DRCE: reg [ %02x %02x %02x %02x %02x %02x ]\n",
(unsigned char)shadow[TAS3004_REG_DRC][0],
(unsigned char)shadow[TAS3004_REG_DRC][1],
(unsigned char)shadow[TAS3004_REG_DRC][2],
(unsigned char)shadow[TAS3004_REG_DRC][3],
(unsigned char)shadow[TAS3004_REG_DRC][4],
(unsigned char)shadow[TAS3004_REG_DRC][5]);
#endif
return tas3004_sync_register(self, TAS3004_REG_DRC);
}
static int
tas3004_drce_rw( struct tas3004_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
struct tas_drce_ctrl_t drce_ctrl;
void __user *argp = (void __user *)arg;
if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t)))
return -EFAULT;
#ifdef DEBUG_DRCE
printk("DRCE: input [ FLAGS:%x ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n",
drce_ctrl.flags,
drce_ctrl.data.enable,
drce_ctrl.data.above.expand,drce_ctrl.data.above.val,
drce_ctrl.data.below.expand,drce_ctrl.data.below.val,
drce_ctrl.data.threshold,
drce_ctrl.data.energy,
drce_ctrl.data.attack,
drce_ctrl.data.decay);
#endif
if (cmd & SIOC_IN) {
rc = tas3004_update_drce(self, drce_ctrl.flags, &drce_ctrl.data);
if (rc < 0) return rc;
}
if (cmd & SIOC_OUT) {
if (drce_ctrl.flags & TAS_DRCE_ENABLE)
drce_ctrl.data.enable = self->drce_state.enable;
if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO)
drce_ctrl.data.above = self->drce_state.above;
if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO)
drce_ctrl.data.below = self->drce_state.below;
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD)
drce_ctrl.data.threshold = self->drce_state.threshold;
if (drce_ctrl.flags & TAS_DRCE_ENERGY)
drce_ctrl.data.energy = self->drce_state.energy;
if (drce_ctrl.flags & TAS_DRCE_ATTACK)
drce_ctrl.data.attack = self->drce_state.attack;
if (drce_ctrl.flags & TAS_DRCE_DECAY)
drce_ctrl.data.decay = self->drce_state.decay;
if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
return 0;
}
static void
tas3004_update_device_parameters(struct tas3004_data_t *self)
{
char data;
int i;
if (!self) return;
if (self->output_id == TAS_OUTPUT_HEADPHONES) {
/* turn on allPass when headphones are plugged in */
data = 0x02;
} else {
data = 0x00;
}
tas3004_write_register(self, TAS3004_REG_MCR2, &data, WRITE_NORMAL | FORCE_WRITE);
for (i=0; tas3004_eq_prefs[i]; i++) {
struct tas_eq_pref_t *eq = tas3004_eq_prefs[i];
if (eq->device_id == self->device_id &&
(eq->output_id == 0 || eq->output_id == self->output_id) &&
(eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) {
tas3004_update_drce(self, TAS_DRCE_ALL, eq->drce);
tas3004_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads);
break;
}
}
}
static void
tas3004_device_change_handler(struct work_struct *work)
{
struct tas3004_data_t *self;
self = container_of(work, struct tas3004_data_t, change);
tas3004_update_device_parameters(self);
}
static int
tas3004_output_device_change( struct tas3004_data_t *self,
int device_id,
int output_id,
int speaker_id)
{
self->device_id=device_id;
self->output_id=output_id;
self->speaker_id=speaker_id;
schedule_work(&self->change);
return 0;
}
static int
tas3004_device_ioctl( struct tas3004_data_t *self,
u_int cmd,
u_long arg)
{
uint __user *argp = (void __user *)arg;
switch (cmd) {
case TAS_READ_EQ:
case TAS_WRITE_EQ:
return tas3004_eq_rw(self, cmd, arg);
case TAS_READ_EQ_LIST:
case TAS_WRITE_EQ_LIST:
return tas3004_eq_list_rw(self, cmd, arg);
case TAS_READ_EQ_FILTER_COUNT:
put_user(TAS3004_BIQUAD_FILTER_COUNT, argp);
return 0;
case TAS_READ_EQ_CHANNEL_COUNT:
put_user(TAS3004_BIQUAD_CHANNEL_COUNT, argp);
return 0;
case TAS_READ_DRCE:
case TAS_WRITE_DRCE:
return tas3004_drce_rw(self, cmd, arg);
case TAS_READ_DRCE_CAPS:
put_user(TAS_DRCE_ENABLE |
TAS_DRCE_ABOVE_RATIO |
TAS_DRCE_BELOW_RATIO |
TAS_DRCE_THRESHOLD |
TAS_DRCE_ENERGY |
TAS_DRCE_ATTACK |
TAS_DRCE_DECAY,
argp);
return 0;
case TAS_READ_DRCE_MIN:
case TAS_READ_DRCE_MAX: {
struct tas_drce_ctrl_t drce_ctrl;
const struct tas_drce_t *drce_copy;
if (copy_from_user(&drce_ctrl, argp,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
if (cmd == TAS_READ_DRCE_MIN) {
drce_copy=&tas3004_drce_min;
} else {
drce_copy=&tas3004_drce_max;
}
if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO) {
drce_ctrl.data.above=drce_copy->above;
}
if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO) {
drce_ctrl.data.below=drce_copy->below;
}
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) {
drce_ctrl.data.threshold=drce_copy->threshold;
}
if (drce_ctrl.flags & TAS_DRCE_ENERGY) {
drce_ctrl.data.energy=drce_copy->energy;
}
if (drce_ctrl.flags & TAS_DRCE_ATTACK) {
drce_ctrl.data.attack=drce_copy->attack;
}
if (drce_ctrl.flags & TAS_DRCE_DECAY) {
drce_ctrl.data.decay=drce_copy->decay;
}
if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
}
return -EINVAL;
}
static int
tas3004_init_mixer(struct tas3004_data_t *self)
{
unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
/* Make sure something answers on the i2c bus */
if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr,
WRITE_NORMAL | FORCE_WRITE) < 0)
return -1;
tas3004_fast_load(self, 1);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5);
(void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5);
(void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6);
tas3004_sync_register(self, TAS3004_REG_DRC);
tas3004_sync_register(self, TAS3004_REG_MCR2);
tas3004_fast_load(self, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT);
tas3004_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT);
tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT);
tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT);
tas3004_set_mixer_level(self, SOUND_MIXER_LINE,SW_INPUT_VOLUME_DEFAULT);
return 0;
}
static int
tas3004_uninit_mixer(struct tas3004_data_t *self)
{
tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_PCM, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_BASS, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, 0);
tas3004_set_mixer_level(self, SOUND_MIXER_LINE, 0);
return 0;
}
static int
tas3004_init(struct i2c_client *client)
{
struct tas3004_data_t *self;
size_t sz = sizeof(*self) + (TAS3004_REG_MAX*sizeof(tas_shadow_t));
char drce_init[] = { 0x69, 0x22, 0x9f, 0xb0, 0x60, 0xa0 };
char mcr2 = 0;
int i, j;
self = kzalloc(sz, GFP_KERNEL);
if (!self)
return -ENOMEM;
self->super.client = client;
self->super.shadow = (tas_shadow_t *)(self+1);
self->output_id = TAS_OUTPUT_HEADPHONES;
dev_set_drvdata(&client->dev, self);
for (i = 0; i < TAS3004_BIQUAD_CHANNEL_COUNT; i++)
for (j = 0; j<TAS3004_BIQUAD_FILTER_COUNT; j++)
tas3004_write_biquad_shadow(self, i, j,
&tas3004_eq_unity);
tas3004_write_register(self, TAS3004_REG_MCR2, &mcr2, WRITE_SHADOW);
tas3004_write_register(self, TAS3004_REG_DRC, drce_init, WRITE_SHADOW);
INIT_WORK(&self->change, tas3004_device_change_handler);
return 0;
}
static void
tas3004_uninit(struct tas3004_data_t *self)
{
tas3004_uninit_mixer(self);
kfree(self);
}
struct tas_driver_hooks_t tas3004_hooks = {
.init = (tas_hook_init_t)tas3004_init,
.post_init = (tas_hook_post_init_t)tas3004_init_mixer,
.uninit = (tas_hook_uninit_t)tas3004_uninit,
.get_mixer_level = (tas_hook_get_mixer_level_t)tas3004_get_mixer_level,
.set_mixer_level = (tas_hook_set_mixer_level_t)tas3004_set_mixer_level,
.enter_sleep = (tas_hook_enter_sleep_t)tas3004_enter_sleep,
.leave_sleep = (tas_hook_leave_sleep_t)tas3004_leave_sleep,
.supported_mixers = (tas_hook_supported_mixers_t)tas3004_supported_mixers,
.mixer_is_stereo = (tas_hook_mixer_is_stereo_t)tas3004_mixer_is_stereo,
.stereo_mixers = (tas_hook_stereo_mixers_t)tas3004_stereo_mixers,
.output_device_change = (tas_hook_output_device_change_t)tas3004_output_device_change,
.device_ioctl = (tas_hook_device_ioctl_t)tas3004_device_ioctl
};
/*
* Header file for the i2c/i2s based TA3004 sound chip used
* on some Apple hardware. Also known as "tumbler".
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* Written by Christopher C. Chimelis <chris@debian.org>
*/
#ifndef _TAS3004_H_
#define _TAS3004_H_
#include <linux/types.h>
#include "tas_common.h"
#include "tas_eq_prefs.h"
/*
* Macros that correspond to the registers that we write to
* when setting the various values.
*/
#define TAS3004_VERSION "0.3"
#define TAS3004_DATE "20011214"
#define I2C_DRIVERNAME_TAS3004 "TAS3004 driver V " TAS3004_VERSION
#define I2C_DRIVERID_TAS3004 (I2C_DRIVERID_TAS_BASE+1)
extern struct tas_driver_hooks_t tas3004_hooks;
extern struct tas_gain_t tas3004_gain;
extern struct tas_eq_pref_t *tas3004_eq_prefs[];
enum tas3004_reg_t {
TAS3004_REG_MCR = 0x01,
TAS3004_REG_DRC = 0x02,
TAS3004_REG_VOLUME = 0x04,
TAS3004_REG_TREBLE = 0x05,
TAS3004_REG_BASS = 0x06,
TAS3004_REG_LEFT_MIXER = 0x07,
TAS3004_REG_RIGHT_MIXER = 0x08,
TAS3004_REG_LEFT_BIQUAD0 = 0x0a,
TAS3004_REG_LEFT_BIQUAD1 = 0x0b,
TAS3004_REG_LEFT_BIQUAD2 = 0x0c,
TAS3004_REG_LEFT_BIQUAD3 = 0x0d,
TAS3004_REG_LEFT_BIQUAD4 = 0x0e,
TAS3004_REG_LEFT_BIQUAD5 = 0x0f,
TAS3004_REG_LEFT_BIQUAD6 = 0x10,
TAS3004_REG_RIGHT_BIQUAD0 = 0x13,
TAS3004_REG_RIGHT_BIQUAD1 = 0x14,
TAS3004_REG_RIGHT_BIQUAD2 = 0x15,
TAS3004_REG_RIGHT_BIQUAD3 = 0x16,
TAS3004_REG_RIGHT_BIQUAD4 = 0x17,
TAS3004_REG_RIGHT_BIQUAD5 = 0x18,
TAS3004_REG_RIGHT_BIQUAD6 = 0x19,
TAS3004_REG_LEFT_LOUD_BIQUAD = 0x21,
TAS3004_REG_RIGHT_LOUD_BIQUAD = 0x22,
TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN = 0x23,
TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN = 0x24,
TAS3004_REG_TEST = 0x29,
TAS3004_REG_ANALOG_CTRL = 0x40,
TAS3004_REG_TEST1 = 0x41,
TAS3004_REG_TEST2 = 0x42,
TAS3004_REG_MCR2 = 0x43,
TAS3004_REG_MAX = 0x44
};
#endif /* _TAS3004_H_ */
#include "tas3004.h"
#include "tas_eq_prefs.h"
static struct tas_drce_t eqp_17_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -19.12 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_17_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0fd0d4, 0xe05e56, 0x0fd0d4, 0xe05ee1, 0x0fa234 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x0910d7, 0x088e1a, 0x030651, 0x01dcb1, 0x02c892 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0ff895, 0xe0970b, 0x0f7f00, 0xe0970b, 0x0f7795 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0fd1c4, 0xe1ac22, 0x0ec8cf, 0xe1ac22, 0x0e9a94 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0f7c1c, 0xe3cc03, 0x0df786, 0xe3cc03, 0x0d73a2 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x11fb92, 0xf5a1a0, 0x073cd2, 0xf5a1a0, 0x093865 } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x0e17a9, 0x068b6c, 0x08a0e5, 0x068b6c, 0x06b88e } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0fd0d4, 0xe05e56, 0x0fd0d4, 0xe05ee1, 0x0fa234 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x0910d7, 0x088e1a, 0x030651, 0x01dcb1, 0x02c892 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0ff895, 0xe0970b, 0x0f7f00, 0xe0970b, 0x0f7795 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0fd1c4, 0xe1ac22, 0x0ec8cf, 0xe1ac22, 0x0e9a94 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0f7c1c, 0xe3cc03, 0x0df786, 0xe3cc03, 0x0d73a2 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x11fb92, 0xf5a1a0, 0x073cd2, 0xf5a1a0, 0x093865 } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x0e17a9, 0x068b6c, 0x08a0e5, 0x068b6c, 0x06b88e } } }
};
static struct tas_eq_pref_t eqp_17_1_0 = {
.sample_rate = 44100,
.device_id = 0x17,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_17_1_0_drce,
.filter_count = 14,
.biquads = eqp_17_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_18_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -13.14 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_18_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0f5514, 0xe155d7, 0x0f5514, 0xe15cfa, 0x0eb14b } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x06ec33, 0x02abe3, 0x015eef, 0xf764d9, 0x03922d } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0ef5f2, 0xe67d1f, 0x0bcf37, 0xe67d1f, 0x0ac529 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0db050, 0xe5be4d, 0x0d0c78, 0xe5be4d, 0x0abcc8 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0f1298, 0xe64ec6, 0x0cc03e, 0xe64ec6, 0x0bd2d7 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0c641a, 0x06537a, 0x08d155, 0x06537a, 0x053570 } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0f5514, 0xe155d7, 0x0f5514, 0xe15cfa, 0x0eb14b } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x06ec33, 0x02abe3, 0x015eef, 0xf764d9, 0x03922d } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0ef5f2, 0xe67d1f, 0x0bcf37, 0xe67d1f, 0x0ac529 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0db050, 0xe5be4d, 0x0d0c78, 0xe5be4d, 0x0abcc8 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0f1298, 0xe64ec6, 0x0cc03e, 0xe64ec6, 0x0bd2d7 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0c641a, 0x06537a, 0x08d155, 0x06537a, 0x053570 } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } }
};
static struct tas_eq_pref_t eqp_18_1_0 = {
.sample_rate = 44100,
.device_id = 0x18,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_18_1_0_drce,
.filter_count = 14,
.biquads = eqp_18_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_1a_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -10.75 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_1a_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0fb8fd, 0xe08e04, 0x0fb8fd, 0xe08f40, 0x0f7336 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x06371d, 0x0c6e3a, 0x06371d, 0x05bfd3, 0x031ca2 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0fa1c0, 0xe18692, 0x0f030e, 0xe18692, 0x0ea4ce } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0fe495, 0xe17eff, 0x0f0452, 0xe17eff, 0x0ee8e7 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x100857, 0xe7e71c, 0x0e9599, 0xe7e71c, 0x0e9df1 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0fb26e, 0x06a82c, 0x0db2b4, 0x06a82c, 0x0d6522 } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x11419d, 0xf06cbf, 0x0a4f6e, 0xf06cbf, 0x0b910c } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0fb8fd, 0xe08e04, 0x0fb8fd, 0xe08f40, 0x0f7336 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x06371d, 0x0c6e3a, 0x06371d, 0x05bfd3, 0x031ca2 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0fa1c0, 0xe18692, 0x0f030e, 0xe18692, 0x0ea4ce } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0fe495, 0xe17eff, 0x0f0452, 0xe17eff, 0x0ee8e7 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x100857, 0xe7e71c, 0x0e9599, 0xe7e71c, 0x0e9df1 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0fb26e, 0x06a82c, 0x0db2b4, 0x06a82c, 0x0d6522 } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x11419d, 0xf06cbf, 0x0a4f6e, 0xf06cbf, 0x0b910c } } }
};
static struct tas_eq_pref_t eqp_1a_1_0 = {
.sample_rate = 44100,
.device_id = 0x1a,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_1a_1_0_drce,
.filter_count = 14,
.biquads = eqp_1a_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_1c_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -14.34 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_1c_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0f4f95, 0xe160d4, 0x0f4f95, 0xe1686e, 0x0ea6c5 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x066b92, 0x0290d4, 0x0148a0, 0xf6853f, 0x03bfc7 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0f57dc, 0xe51c91, 0x0dd1cb, 0xe51c91, 0x0d29a8 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0df1cb, 0xe4fa84, 0x0d7cdc, 0xe4fa84, 0x0b6ea7 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0eba36, 0xe6aa48, 0x0b9f52, 0xe6aa48, 0x0a5989 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0caf02, 0x05ef9d, 0x084beb, 0x05ef9d, 0x04faee } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x0fc686, 0xe22947, 0x0e4b5d, 0xe22947, 0x0e11e4 } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0f4f95, 0xe160d4, 0x0f4f95, 0xe1686e, 0x0ea6c5 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x066b92, 0x0290d4, 0x0148a0, 0xf6853f, 0x03bfc7 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0f57dc, 0xe51c91, 0x0dd1cb, 0xe51c91, 0x0d29a8 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0df1cb, 0xe4fa84, 0x0d7cdc, 0xe4fa84, 0x0b6ea7 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0eba36, 0xe6aa48, 0x0b9f52, 0xe6aa48, 0x0a5989 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0caf02, 0x05ef9d, 0x084beb, 0x05ef9d, 0x04faee } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x0fc686, 0xe22947, 0x0e4b5d, 0xe22947, 0x0e11e4 } } }
};
static struct tas_eq_pref_t eqp_1c_1_0 = {
.sample_rate = 44100,
.device_id = 0x1c,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_1c_1_0_drce,
.filter_count = 14,
.biquads = eqp_1c_1_0_biquads
};
/* ======================================================================== */
static uint tas3004_master_tab[]={
0x0, 0x75, 0x9c, 0xbb,
0xdb, 0xfb, 0x11e, 0x143,
0x16b, 0x196, 0x1c3, 0x1f5,
0x229, 0x263, 0x29f, 0x2e1,
0x328, 0x373, 0x3c5, 0x41b,
0x478, 0x4dc, 0x547, 0x5b8,
0x633, 0x6b5, 0x740, 0x7d5,
0x873, 0x91c, 0x9d2, 0xa92,
0xb5e, 0xc39, 0xd22, 0xe19,
0xf20, 0x1037, 0x1161, 0x129e,
0x13ed, 0x1551, 0x16ca, 0x185d,
0x1a08, 0x1bcc, 0x1dac, 0x1fa7,
0x21c1, 0x23fa, 0x2655, 0x28d6,
0x2b7c, 0x2e4a, 0x3141, 0x3464,
0x37b4, 0x3b35, 0x3ee9, 0x42d3,
0x46f6, 0x4b53, 0x4ff0, 0x54ce,
0x59f2, 0x5f5f, 0x6519, 0x6b24,
0x7183, 0x783c, 0x7f53, 0x86cc,
0x8ead, 0x96fa, 0x9fba, 0xa8f2,
0xb2a7, 0xbce1, 0xc7a5, 0xd2fa,
0xdee8, 0xeb75, 0xf8aa, 0x1068e,
0x1152a, 0x12487, 0x134ad, 0x145a5,
0x1577b, 0x16a37, 0x17df5, 0x192bd,
0x1a890, 0x1bf7b, 0x1d78d, 0x1f0d1,
0x20b55, 0x22727, 0x24456, 0x262f2,
0x2830b
};
static uint tas3004_mixer_tab[]={
0x0, 0x748, 0x9be, 0xbaf,
0xda4, 0xfb1, 0x11de, 0x1431,
0x16ad, 0x1959, 0x1c37, 0x1f4b,
0x2298, 0x2628, 0x29fb, 0x2e12,
0x327d, 0x3734, 0x3c47, 0x41b4,
0x4787, 0x4dbe, 0x546d, 0x5b86,
0x632e, 0x6b52, 0x7400, 0x7d54,
0x873b, 0x91c6, 0x9d1a, 0xa920,
0xb5e5, 0xc38c, 0xd21b, 0xe18f,
0xf1f5, 0x1036a, 0x1160f, 0x129d6,
0x13ed0, 0x1550c, 0x16ca0, 0x185c9,
0x1a07b, 0x1bcc3, 0x1dab9, 0x1fa75,
0x21c0f, 0x23fa3, 0x26552, 0x28d64,
0x2b7c9, 0x2e4a2, 0x31411, 0x3463b,
0x37b44, 0x3b353, 0x3ee94, 0x42d30,
0x46f55, 0x4b533, 0x4fefc, 0x54ce5,
0x59f25, 0x5f5f6, 0x65193, 0x6b23c,
0x71835, 0x783c3, 0x7f52c, 0x86cc0,
0x8eacc, 0x96fa5, 0x9fba0, 0xa8f1a,
0xb2a71, 0xbce0a, 0xc7a4a, 0xd2fa0,
0xdee7b, 0xeb752, 0xf8a9f, 0x1068e4,
0x1152a3, 0x12486a, 0x134ac8, 0x145a55,
0x1577ac, 0x16a370, 0x17df51, 0x192bc2,
0x1a88f8, 0x1bf7b7, 0x1d78c9, 0x1f0d04,
0x20b542, 0x227268, 0x244564, 0x262f26,
0x2830af
};
static uint tas3004_treble_tab[]={
0x96, 0x95, 0x95, 0x94,
0x93, 0x92, 0x92, 0x91,
0x90, 0x90, 0x8f, 0x8e,
0x8d, 0x8d, 0x8c, 0x8b,
0x8a, 0x8a, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85,
0x85, 0x84, 0x83, 0x83,
0x82, 0x81, 0x80, 0x80,
0x7f, 0x7e, 0x7e, 0x7d,
0x7c, 0x7b, 0x7b, 0x7a,
0x79, 0x78, 0x78, 0x77,
0x76, 0x76, 0x75, 0x74,
0x73, 0x73, 0x72, 0x71,
0x71, 0x68, 0x45, 0x5b,
0x6d, 0x6c, 0x6b, 0x6a,
0x69, 0x68, 0x67, 0x66,
0x65, 0x63, 0x62, 0x62,
0x60, 0x5e, 0x5c, 0x5b,
0x59, 0x57, 0x55, 0x53,
0x52, 0x4f, 0x4d, 0x4a,
0x48, 0x46, 0x43, 0x40,
0x3d, 0x3a, 0x36, 0x33,
0x2f, 0x2c, 0x27, 0x23,
0x1f, 0x1a, 0x15, 0xf,
0x8, 0x5, 0x2, 0x1,
0x1
};
static uint tas3004_bass_tab[]={
0x96, 0x95, 0x95, 0x94,
0x93, 0x92, 0x92, 0x91,
0x90, 0x90, 0x8f, 0x8e,
0x8d, 0x8d, 0x8c, 0x8b,
0x8a, 0x8a, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85,
0x85, 0x84, 0x83, 0x83,
0x82, 0x81, 0x80, 0x80,
0x7f, 0x7e, 0x7e, 0x7d,
0x7c, 0x7b, 0x7b, 0x7a,
0x79, 0x78, 0x78, 0x77,
0x76, 0x76, 0x75, 0x74,
0x73, 0x73, 0x72, 0x71,
0x70, 0x6f, 0x6e, 0x6d,
0x6c, 0x6b, 0x6a, 0x6a,
0x69, 0x67, 0x66, 0x66,
0x65, 0x63, 0x62, 0x62,
0x61, 0x60, 0x5e, 0x5d,
0x5b, 0x59, 0x57, 0x55,
0x53, 0x51, 0x4f, 0x4c,
0x4a, 0x48, 0x46, 0x44,
0x41, 0x3e, 0x3b, 0x38,
0x36, 0x33, 0x2f, 0x2b,
0x28, 0x24, 0x20, 0x1c,
0x17, 0x12, 0xd, 0x7,
0x1
};
struct tas_gain_t tas3004_gain={
.master = tas3004_master_tab,
.treble = tas3004_treble_tab,
.bass = tas3004_bass_tab,
.mixer = tas3004_mixer_tab
};
struct tas_eq_pref_t *tas3004_eq_prefs[]={
&eqp_17_1_0,
&eqp_18_1_0,
&eqp_1a_1_0,
&eqp_1c_1_0,
NULL
};
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/prom.h>
#include "tas_common.h"
#define CALL0(proc) \
do { \
struct tas_data_t *self; \
if (!tas_client || driver_hooks == NULL) \
return -1; \
self = dev_get_drvdata(&tas_client->dev); \
if (driver_hooks->proc) \
return driver_hooks->proc(self); \
else \
return -EINVAL; \
} while (0)
#define CALL(proc,arg...) \
do { \
struct tas_data_t *self; \
if (!tas_client || driver_hooks == NULL) \
return -1; \
self = dev_get_drvdata(&tas_client->dev); \
if (driver_hooks->proc) \
return driver_hooks->proc(self, ## arg); \
else \
return -EINVAL; \
} while (0)
static u8 tas_i2c_address = 0x34;
static struct i2c_client *tas_client;
static int tas_attach_adapter(struct i2c_adapter *);
static int tas_detach_client(struct i2c_client *);
struct i2c_driver tas_driver = {
.driver = {
.name = "tas",
},
.attach_adapter = tas_attach_adapter,
.detach_client = tas_detach_client,
};
struct tas_driver_hooks_t *driver_hooks;
int
tas_register_driver(struct tas_driver_hooks_t *hooks)
{
driver_hooks = hooks;
return 0;
}
int
tas_get_mixer_level(int mixer, uint *level)
{
CALL(get_mixer_level,mixer,level);
}
int
tas_set_mixer_level(int mixer,uint level)
{
CALL(set_mixer_level,mixer,level);
}
int
tas_enter_sleep(void)
{
CALL0(enter_sleep);
}
int
tas_leave_sleep(void)
{
CALL0(leave_sleep);
}
int
tas_supported_mixers(void)
{
CALL0(supported_mixers);
}
int
tas_mixer_is_stereo(int mixer)
{
CALL(mixer_is_stereo,mixer);
}
int
tas_stereo_mixers(void)
{
CALL0(stereo_mixers);
}
int
tas_output_device_change(int device_id,int layout_id,int speaker_id)
{
CALL(output_device_change,device_id,layout_id,speaker_id);
}
int
tas_device_ioctl(u_int cmd, u_long arg)
{
CALL(device_ioctl,cmd,arg);
}
int
tas_post_init(void)
{
CALL0(post_init);
}
static int
tas_detect_client(struct i2c_adapter *adapter, int address)
{
static const char *client_name = "tas Digital Equalizer";
struct i2c_client *new_client;
int rc = -ENODEV;
if (!driver_hooks) {
printk(KERN_ERR "tas_detect_client called with no hooks !\n");
return -ENODEV;
}
new_client = kzalloc(sizeof(*new_client), GFP_KERNEL);
if (!new_client)
return -ENOMEM;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &tas_driver;
strlcpy(new_client->name, client_name, DEVICE_NAME_SIZE);
if (driver_hooks->init(new_client))
goto bail;
/* Tell the i2c layer a new client has arrived */
if (i2c_attach_client(new_client)) {
driver_hooks->uninit(dev_get_drvdata(&new_client->dev));
goto bail;
}
tas_client = new_client;
return 0;
bail:
tas_client = NULL;
kfree(new_client);
return rc;
}
static int
tas_attach_adapter(struct i2c_adapter *adapter)
{
if (!strncmp(adapter->name, "mac-io", 6))
return tas_detect_client(adapter, tas_i2c_address);
return 0;
}
static int
tas_detach_client(struct i2c_client *client)
{
if (client == tas_client) {
driver_hooks->uninit(dev_get_drvdata(&client->dev));
i2c_detach_client(client);
kfree(client);
}
return 0;
}
void
tas_cleanup(void)
{
i2c_del_driver(&tas_driver);
}
int __init
tas_init(int driver_id, const char *driver_name)
{
const u32* paddr;
struct device_node *tas_node;
printk(KERN_INFO "tas driver [%s])\n", driver_name);
#ifndef CONFIG_I2C_POWERMAC
request_module("i2c-powermac");
#endif
tas_node = of_find_node_by_name("deq");
if (tas_node == NULL)
return -ENODEV;
paddr = of_get_property(tas_node, "i2c-address", NULL);
if (paddr) {
tas_i2c_address = (*paddr) >> 1;
printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
tas_i2c_address);
} else
printk(KERN_INFO "using i2c address: 0x%x (default)\n",
tas_i2c_address);
of_node_put(tas_node);
return i2c_add_driver(&tas_driver);
}
#ifndef _TAS_COMMON_H_
#define _TAS_COMMON_H_
#include <linux/i2c.h>
#include <linux/soundcard.h>
#include <asm/string.h>
#define I2C_DRIVERID_TAS_BASE (0xFEBA)
#define SET_4_20(shadow, offset, val) \
do { \
(shadow)[(offset)+0] = ((val) >> 16) & 0xff; \
(shadow)[(offset)+1] = ((val) >> 8) & 0xff; \
(shadow)[(offset)+2] = ((val) >> 0) & 0xff; \
} while (0)
#define GET_4_20(shadow, offset) \
(((u_int)((shadow)[(offset)+0]) << 16) | \
((u_int)((shadow)[(offset)+1]) << 8) | \
((u_int)((shadow)[(offset)+2]) << 0))
#define TAS_BIQUAD_FAST_LOAD 0x01
#define TAS_DRCE_ENABLE 0x01
#define TAS_DRCE_ABOVE_RATIO 0x02
#define TAS_DRCE_BELOW_RATIO 0x04
#define TAS_DRCE_THRESHOLD 0x08
#define TAS_DRCE_ENERGY 0x10
#define TAS_DRCE_ATTACK 0x20
#define TAS_DRCE_DECAY 0x40
#define TAS_DRCE_ALL 0x7f
#define TAS_OUTPUT_HEADPHONES 0x00
#define TAS_OUTPUT_INTERNAL_SPKR 0x01
#define TAS_OUTPUT_EXTERNAL_SPKR 0x02
union tas_biquad_t {
struct {
int b0,b1,b2,a1,a2;
} coeff;
int buf[5];
};
struct tas_biquad_ctrl_t {
u_int channel:4;
u_int filter:4;
union tas_biquad_t data;
};
struct tas_biquad_ctrl_list_t {
int flags;
int filter_count;
struct tas_biquad_ctrl_t biquads[0];
};
struct tas_ratio_t {
unsigned short val; /* 8.8 */
unsigned short expand; /* 0 = compress, !0 = expand. */
};
struct tas_drce_t {
unsigned short enable;
struct tas_ratio_t above;
struct tas_ratio_t below;
short threshold; /* dB, 8.8 signed */
unsigned short energy; /* seconds, 4.12 unsigned */
unsigned short attack; /* seconds, 4.12 unsigned */
unsigned short decay; /* seconds, 4.12 unsigned */
};
struct tas_drce_ctrl_t {
uint flags;
struct tas_drce_t data;
};
struct tas_gain_t
{
unsigned int *master;
unsigned int *treble;
unsigned int *bass;
unsigned int *mixer;
};
typedef char tas_shadow_t[0x45];
struct tas_data_t
{
struct i2c_client *client;
tas_shadow_t *shadow;
uint mixer[SOUND_MIXER_NRDEVICES];
};
typedef int (*tas_hook_init_t)(struct i2c_client *);
typedef int (*tas_hook_post_init_t)(struct tas_data_t *);
typedef void (*tas_hook_uninit_t)(struct tas_data_t *);
typedef int (*tas_hook_get_mixer_level_t)(struct tas_data_t *,int,uint *);
typedef int (*tas_hook_set_mixer_level_t)(struct tas_data_t *,int,uint);
typedef int (*tas_hook_enter_sleep_t)(struct tas_data_t *);
typedef int (*tas_hook_leave_sleep_t)(struct tas_data_t *);
typedef int (*tas_hook_supported_mixers_t)(struct tas_data_t *);
typedef int (*tas_hook_mixer_is_stereo_t)(struct tas_data_t *,int);
typedef int (*tas_hook_stereo_mixers_t)(struct tas_data_t *);
typedef int (*tas_hook_output_device_change_t)(struct tas_data_t *,int,int,int);
typedef int (*tas_hook_device_ioctl_t)(struct tas_data_t *,u_int,u_long);
struct tas_driver_hooks_t {
/*
* All hardware initialisation must be performed in
* post_init(), as tas_dmasound_init() does a hardware reset.
*
* init() is called before tas_dmasound_init() so that
* ouput_device_change() is always called after i2c driver
* initialisation. The implication is that
* output_device_change() must cope with the fact that it
* may be called before post_init().
*/
tas_hook_init_t init;
tas_hook_post_init_t post_init;
tas_hook_uninit_t uninit;
tas_hook_get_mixer_level_t get_mixer_level;
tas_hook_set_mixer_level_t set_mixer_level;
tas_hook_enter_sleep_t enter_sleep;
tas_hook_leave_sleep_t leave_sleep;
tas_hook_supported_mixers_t supported_mixers;
tas_hook_mixer_is_stereo_t mixer_is_stereo;
tas_hook_stereo_mixers_t stereo_mixers;
tas_hook_output_device_change_t output_device_change;
tas_hook_device_ioctl_t device_ioctl;
};
enum tas_write_mode_t {
WRITE_HW = 0x01,
WRITE_SHADOW = 0x02,
WRITE_NORMAL = 0x03,
FORCE_WRITE = 0x04
};
static inline uint
tas_mono_to_stereo(uint mono)
{
mono &=0xff;
return mono | (mono<<8);
}
/*
* Todo: make these functions a bit more efficient !
*/
static inline int
tas_write_register( struct tas_data_t *self,
uint reg_num,
uint reg_width,
char *data,
uint write_mode)
{
int rc;
if (reg_width==0 || data==NULL || self==NULL)
return -EINVAL;
if (!(write_mode & FORCE_WRITE) &&
!memcmp(data,self->shadow[reg_num],reg_width))
return 0;
if (write_mode & WRITE_SHADOW)
memcpy(self->shadow[reg_num],data,reg_width);
if (write_mode & WRITE_HW) {
rc=i2c_smbus_write_i2c_block_data(self->client,
reg_num,
reg_width,
data);
if (rc < 0) {
printk("tas: I2C block write failed \n");
return rc;
}
}
return 0;
}
static inline int
tas_sync_register( struct tas_data_t *self,
uint reg_num,
uint reg_width)
{
int rc;
if (reg_width==0 || self==NULL)
return -EINVAL;
rc=i2c_smbus_write_i2c_block_data(self->client,
reg_num,
reg_width,
self->shadow[reg_num]);
if (rc < 0) {
printk("tas: I2C block write failed \n");
return rc;
}
return 0;
}
static inline int
tas_write_byte_register( struct tas_data_t *self,
uint reg_num,
char data,
uint write_mode)
{
if (self==NULL)
return -1;
if (!(write_mode & FORCE_WRITE) && data != self->shadow[reg_num][0])
return 0;
if (write_mode & WRITE_SHADOW)
self->shadow[reg_num][0]=data;
if (write_mode & WRITE_HW) {
if (i2c_smbus_write_byte_data(self->client, reg_num, data) < 0) {
printk("tas: I2C byte write failed \n");
return -1;
}
}
return 0;
}
static inline int
tas_sync_byte_register( struct tas_data_t *self,
uint reg_num,
uint reg_width)
{
if (reg_width==0 || self==NULL)
return -1;
if (i2c_smbus_write_byte_data(
self->client, reg_num, self->shadow[reg_num][0]) < 0) {
printk("tas: I2C byte write failed \n");
return -1;
}
return 0;
}
static inline int
tas_read_register( struct tas_data_t *self,
uint reg_num,
uint reg_width,
char *data)
{
if (reg_width==0 || data==NULL || self==NULL)
return -1;
memcpy(data,self->shadow[reg_num],reg_width);
return 0;
}
extern int tas_register_driver(struct tas_driver_hooks_t *hooks);
extern int tas_get_mixer_level(int mixer,uint *level);
extern int tas_set_mixer_level(int mixer,uint level);
extern int tas_enter_sleep(void);
extern int tas_leave_sleep(void);
extern int tas_supported_mixers(void);
extern int tas_mixer_is_stereo(int mixer);
extern int tas_stereo_mixers(void);
extern int tas_output_device_change(int,int,int);
extern int tas_device_ioctl(u_int, u_long);
extern void tas_cleanup(void);
extern int tas_init(int driver_id,const char *driver_name);
extern int tas_post_init(void);
#endif /* _TAS_COMMON_H_ */
/*
* Local Variables:
* tab-width: 8
* indent-tabs-mode: t
* c-basic-offset: 8
* End:
*/
#ifndef _TAS_EQ_PREFS_H_
#define _TAS_EQ_PREFS_H_
struct tas_eq_pref_t {
u_int sample_rate;
u_int device_id;
u_int output_id;
u_int speaker_id;
struct tas_drce_t *drce;
u_int filter_count;
struct tas_biquad_ctrl_t *biquads;
};
#endif /* _TAS_EQ_PREFS_H_ */
/*
* Local Variables:
* tab-width: 8
* indent-tabs-mode: t
* c-basic-offset: 8
* End:
*/
#ifndef _TAS_IOCTL_H_
#define _TAS_IOCTL_H_
#include <linux/soundcard.h>
#define TAS_READ_EQ _SIOR('t',0,struct tas_biquad_ctrl_t)
#define TAS_WRITE_EQ _SIOW('t',0,struct tas_biquad_ctrl_t)
#define TAS_READ_EQ_LIST _SIOR('t',1,struct tas_biquad_ctrl_t)
#define TAS_WRITE_EQ_LIST _SIOW('t',1,struct tas_biquad_ctrl_t)
#define TAS_READ_EQ_FILTER_COUNT _SIOR('t',2,int)
#define TAS_READ_EQ_CHANNEL_COUNT _SIOR('t',3,int)
#define TAS_READ_DRCE _SIOR('t',4,struct tas_drce_ctrl_t)
#define TAS_WRITE_DRCE _SIOW('t',4,struct tas_drce_ctrl_t)
#define TAS_READ_DRCE_CAPS _SIOR('t',5,int)
#define TAS_READ_DRCE_MIN _SIOR('t',6,int)
#define TAS_READ_DRCE_MAX _SIOR('t',7,int)
#endif
/*
* linux/sound/oss/dmasound/trans_16.c
*
* 16 bit translation routines. Only used by Power mac at present.
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and
* history prior to 08/02/2001.
*
* 08/02/2001 Iain Sandoe
* split from dmasound_awacs.c
* 11/29/2003 Renzo Davoli (King Enzo)
* - input resampling (for soft rate < hard rate)
* - software line in gain control
*/
#include <linux/soundcard.h>
#include <asm/uaccess.h>
#include "dmasound.h"
extern int expand_bal; /* Balance factor for expanding (not volume!) */
static short dmasound_alaw2dma16[] ;
static short dmasound_ulaw2dma16[] ;
static ssize_t pmac_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
/*** Translations ************************************************************/
static int expand_data; /* Data for expanding */
static ssize_t pmac_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *table = dmasound.soft.format == AFMT_MU_LAW
? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = table[data];
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = table[data];
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = data << 8;
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = data << 8;
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = (data ^ 0x80) << 8;
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = (data ^ 0x80) << 8;
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
if (!stereo) {
short __user *up = (short __user *) userPtr;
while (count > 0) {
short data;
if (get_user(data, up++))
return -EFAULT;
*fp++ = data;
*fp++ = data;
count--;
}
} else {
if (copy_from_user(fp, userPtr, count * 4))
return -EFAULT;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ct_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
short data;
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
*fp++ = data;
if (stereo) {
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
}
*fp++ = data;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned short *table = (unsigned short *)
(dmasound.soft.format == AFMT_MU_LAW
? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
unsigned int data = expand_data;
unsigned int *p = (unsigned int *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
int stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = table[c];
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + table[c];
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c << 8;
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + (c << 8);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = (c ^ 0x80) << 8;
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + ((c ^ 0x80) << 8);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
unsigned short __user *up = (unsigned short __user *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
unsigned short c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(data, up++))
return -EFAULT;
if (stereo) {
if (get_user(c, up++))
return -EFAULT;
data = (data << 16) + c;
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
static ssize_t pmac_ctx_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
unsigned short __user *up = (unsigned short __user *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
unsigned short c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
if (stereo) {
if (get_user(c, up++))
return -EFAULT;
data = (data << 16) + (c ^ mask);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
/* data in routines... */
static ssize_t pmac_ct_s8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
val = *p++;
val = (val * software_input_volume) >> 7;
data = val >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
val = (val * software_input_volume) >> 7;
data = val >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
p++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_u8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
val = *p++;
val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
p++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
short data;
data = *fp++;
data = (data * software_input_volume) >> 7;
if (put_user(data, up++))
return -EFAULT;
if (stereo) {
data = *fp;
data = (data * software_input_volume) >> 7;
if (put_user(data, up++))
return -EFAULT;
}
fp++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ct_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
int data;
data = *fp++;
data = (data * software_input_volume) >> 7;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
if (stereo) {
data = *fp;
data = (data * software_input_volume) >> 7;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
}
fp++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
/* data in routines (reducing speed)... */
static ssize_t pmac_ctx_s8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *p = (short *) &frame[*frameUsed];
int bal = expand_read_bal;
int vall,valr, stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char data;
if (bal<0 && userCount == 0)
break;
vall = *p++;
vall = (vall * software_input_volume) >> 7;
if (stereo) {
valr = *p;
valr = (valr * software_input_volume) >> 7;
}
p++;
if (bal < 0) {
data = vall >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
data = valr >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_u8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *p = (short *) &frame[*frameUsed];
int bal = expand_read_bal;
int vall,valr, stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char data;
if (bal<0 && userCount == 0)
break;
vall = *p++;
vall = (vall * software_input_volume) >> 7;
if (stereo) {
valr = *p;
valr = (valr * software_input_volume) >> 7;
}
p++;
if (bal < 0) {
data = (vall >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
data = (valr >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int bal = expand_read_bal;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
int stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
int datal,datar;
if (bal<0 && userCount == 0)
break;
datal = *fp++;
datal = (datal * software_input_volume) >> 7;
if (stereo) {
datar = *fp;
datar = (datar * software_input_volume) >> 7;
}
fp++;
if (bal < 0) {
if (put_user(datal, up++))
return -EFAULT;
if (stereo) {
if (put_user(datar, up++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
static ssize_t pmac_ctx_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int bal = expand_read_bal;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
int stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
int datal,datar;
if (bal<0 && userCount == 0)
break;
datal = *fp++;
datal = (datal * software_input_volume) >> 7;
datal ^= mask;
if (stereo) {
datar = *fp;
datar = (datar * software_input_volume) >> 7;
datar ^= mask;
}
fp++;
if (bal < 0) {
if (put_user(datal, up++))
return -EFAULT;
if (stereo) {
if (put_user(datar, up++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
TRANS transAwacsNormal = {
.ct_ulaw= pmac_ct_law,
.ct_alaw= pmac_ct_law,
.ct_s8= pmac_ct_s8,
.ct_u8= pmac_ct_u8,
.ct_s16be= pmac_ct_s16,
.ct_u16be= pmac_ct_u16,
.ct_s16le= pmac_ct_s16,
.ct_u16le= pmac_ct_u16,
};
TRANS transAwacsExpand = {
.ct_ulaw= pmac_ctx_law,
.ct_alaw= pmac_ctx_law,
.ct_s8= pmac_ctx_s8,
.ct_u8= pmac_ctx_u8,
.ct_s16be= pmac_ctx_s16,
.ct_u16be= pmac_ctx_u16,
.ct_s16le= pmac_ctx_s16,
.ct_u16le= pmac_ctx_u16,
};
TRANS transAwacsNormalRead = {
.ct_s8= pmac_ct_s8_read,
.ct_u8= pmac_ct_u8_read,
.ct_s16be= pmac_ct_s16_read,
.ct_u16be= pmac_ct_u16_read,
.ct_s16le= pmac_ct_s16_read,
.ct_u16le= pmac_ct_u16_read,
};
TRANS transAwacsExpandRead = {
.ct_s8= pmac_ctx_s8_read,
.ct_u8= pmac_ctx_u8_read,
.ct_s16be= pmac_ctx_s16_read,
.ct_u16be= pmac_ctx_u16_read,
.ct_s16le= pmac_ctx_s16_read,
.ct_u16le= pmac_ctx_u16_read,
};
/* translation tables */
/* 16 bit mu-law */
static short dmasound_ulaw2dma16[] = {
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
-11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
-876, -844, -812, -780, -748, -716, -684, -652,
-620, -588, -556, -524, -492, -460, -428, -396,
-372, -356, -340, -324, -308, -292, -276, -260,
-244, -228, -212, -196, -180, -164, -148, -132,
-120, -112, -104, -96, -88, -80, -72, -64,
-56, -48, -40, -32, -24, -16, -8, 0,
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
876, 844, 812, 780, 748, 716, 684, 652,
620, 588, 556, 524, 492, 460, 428, 396,
372, 356, 340, 324, 308, 292, 276, 260,
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0,
};
/* 16 bit A-law */
static short dmasound_alaw2dma16[] = {
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
-3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
-22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
-30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
-11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
-15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
-344, -328, -376, -360, -280, -264, -312, -296,
-472, -456, -504, -488, -408, -392, -440, -424,
-88, -72, -120, -104, -24, -8, -56, -40,
-216, -200, -248, -232, -152, -136, -184, -168,
-1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
-1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
-688, -656, -752, -720, -560, -528, -624, -592,
-944, -912, -1008, -976, -816, -784, -880, -848,
5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
344, 328, 376, 360, 280, 264, 312, 296,
472, 456, 504, 488, 408, 392, 440, 424,
88, 72, 120, 104, 24, 8, 56, 40,
216, 200, 248, 232, 152, 136, 184, 168,
1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
688, 656, 752, 720, 560, 528, 624, 592,
944, 912, 1008, 976, 816, 784, 880, 848,
};
/*****************************************************************************/
/*
* es1371.c -- Creative Ensoniq ES1371.
*
* Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Special thanks to Ensoniq
*
* Supported devices:
* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible
* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible
* /dev/dsp1 additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting
* /dev/midi simple MIDI UART interface, no ioctl
*
* NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed
* to be done in software. That is what /dev/dac is for. By now (Q2 1998)
* there are several MIDI to PCM (WAV) packages, one of them is timidity.
*
* Revision history
* 04.06.1998 0.1 Initial release
* Mixer stuff should be overhauled; especially optional AC97 mixer bits
* should be detected. This results in strange behaviour of some mixer
* settings, like master volume and mic.
* 08.06.1998 0.2 First release using Alan Cox' soundcore instead of miscdevice
* 03.08.1998 0.3 Do not include modversions.h
* Now mixer behaviour can basically be selected between
* "OSS documented" and "OSS actual" behaviour
* 31.08.1998 0.4 Fix realplayer problems - dac.count issues
* 27.10.1998 0.5 Fix joystick support
* -- Oliver Neukum (c188@org.chemie.uni-muenchen.de)
* 10.12.1998 0.6 Fix drain_dac trying to wait on not yet initialized DMA
* 23.12.1998 0.7 Fix a few f_file & FMODE_ bugs
* Don't wake up app until there are fragsize bytes to read/write
* 06.01.1999 0.8 remove the silly SA_INTERRUPT flag.
* hopefully killed the egcs section type conflict
* 12.03.1999 0.9 cinfo.blocks should be reset after GETxPTR ioctl.
* reported by Johan Maes <joma@telindus.be>
* 22.03.1999 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK
* read/write cannot be executed
* 07.04.1999 0.11 implemented the following ioctl's: SOUND_PCM_READ_RATE,
* SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS;
* Alpha fixes reported by Peter Jones <pjones@redhat.com>
* Another Alpha fix (wait_src_ready in init routine)
* reported by "Ivan N. Kokshaysky" <ink@jurassic.park.msu.ru>
* Note: joystick address handling might still be wrong on archs
* other than i386
* 15.06.1999 0.12 Fix bad allocation bug.
* Thanks to Deti Fliegl <fliegl@in.tum.de>
* 28.06.1999 0.13 Add pci_set_master
* 03.08.1999 0.14 adapt to Linus' new __setup/__initcall
* added kernel command line option "es1371=joystickaddr"
* removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge
* 10.08.1999 0.15 (Re)added S/PDIF module option for cards revision >= 4.
* Initial version by Dave Platt <dplatt@snulbug.mtview.ca.us>.
* module_init/__setup fixes
* 08.16.1999 0.16 Joe Cotellese <joec@ensoniq.com>
* Added detection for ES1371 revision ID so that we can
* detect the ES1373 and later parts.
* added AC97 #defines for readability
* added a /proc file system for dumping hardware state
* updated SRC and CODEC w/r functions to accommodate bugs
* in some versions of the ES137x chips.
* 31.08.1999 0.17 add spin_lock_init
* replaced current->state = x with set_current_state(x)
* 03.09.1999 0.18 change read semantics for MIDI to match
* OSS more closely; remove possible wakeup race
* 21.10.1999 0.19 Round sampling rates, requested by
* Kasamatsu Kenichi <t29w0267@ip.media.kyoto-u.ac.jp>
* 27.10.1999 0.20 Added SigmaTel 3D enhancement string
* Codec ID printing changes
* 28.10.1999 0.21 More waitqueue races fixed
* Joe Cotellese <joec@ensoniq.com>
* Changed PCI detection routine so we can more easily
* detect ES137x chip and derivatives.
* 05.01.2000 0.22 Should now work with rev7 boards; patch by
* Eric Lemar, elemar@cs.washington.edu
* 08.01.2000 0.23 Prevent some ioctl's from returning bad count values on underrun/overrun;
* Tim Janik's BSE (Bedevilled Sound Engine) found this
* 07.02.2000 0.24 Use pci_alloc_consistent and pci_register_driver
* 07.02.2000 0.25 Use ac97_codec
* 01.03.2000 0.26 SPDIF patch by Mikael Bouillot <mikael.bouillot@bigfoot.com>
* Use pci_module_init
* 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask
* 12.12.2000 0.28 More dma buffer initializations, patch from
* Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com>
* 05.01.2001 0.29 Hopefully updates will not be required anymore when Creative bumps
* the CT5880 revision.
* suggested by Stephan Müller <smueller@chronox.de>
* 31.01.2001 0.30 Register/Unregister gameport
* Fix SETTRIGGER non OSS API conformity
* 14.07.2001 0.31 Add list of laptops needing amplifier control
* 03.01.2003 0.32 open_mode fixes from Georg Acher <acher@in.tum.de>
*/
/*****************************************************************************/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/sound.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/bitops.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/ac97_codec.h>
#include <linux/gameport.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
#define SUPPORT_JOYSTICK
#endif
/* --------------------------------------------------------------------- */
#undef OSS_DOCUMENTED_MIXER_SEMANTICS
#define ES1371_DEBUG
#define DBG(x) {}
/*#define DBG(x) {x}*/
/* --------------------------------------------------------------------- */
#ifndef PCI_VENDOR_ID_ENSONIQ
#define PCI_VENDOR_ID_ENSONIQ 0x1274
#endif
#ifndef PCI_VENDOR_ID_ECTIVA
#define PCI_VENDOR_ID_ECTIVA 0x1102
#endif
#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371
#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
#endif
#ifndef PCI_DEVICE_ID_ENSONIQ_CT5880
#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880
#endif
#ifndef PCI_DEVICE_ID_ECTIVA_EV1938
#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938
#endif
/* ES1371 chip ID */
/* This is a little confusing because all ES1371 compatible chips have the
same DEVICE_ID, the only thing differentiating them is the REV_ID field.
This is only significant if you want to enable features on the later parts.
Yes, I know it's stupid and why didn't we use the sub IDs?
*/
#define ES1371REV_ES1373_A 0x04
#define ES1371REV_ES1373_B 0x06
#define ES1371REV_CT5880_A 0x07
#define CT5880REV_CT5880_C 0x02
#define CT5880REV_CT5880_D 0x03
#define ES1371REV_ES1371_B 0x09
#define EV1938REV_EV1938_A 0x00
#define ES1371REV_ES1373_8 0x08
#define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371)
#define ES1371_EXTENT 0x40
#define JOY_EXTENT 8
#define ES1371_REG_CONTROL 0x00
#define ES1371_REG_STATUS 0x04 /* on the 5880 it is control/status */
#define ES1371_REG_UART_DATA 0x08
#define ES1371_REG_UART_STATUS 0x09
#define ES1371_REG_UART_CONTROL 0x09
#define ES1371_REG_UART_TEST 0x0a
#define ES1371_REG_MEMPAGE 0x0c
#define ES1371_REG_SRCONV 0x10
#define ES1371_REG_CODEC 0x14
#define ES1371_REG_LEGACY 0x18
#define ES1371_REG_SERIAL_CONTROL 0x20
#define ES1371_REG_DAC1_SCOUNT 0x24
#define ES1371_REG_DAC2_SCOUNT 0x28
#define ES1371_REG_ADC_SCOUNT 0x2c
#define ES1371_REG_DAC1_FRAMEADR 0xc30
#define ES1371_REG_DAC1_FRAMECNT 0xc34
#define ES1371_REG_DAC2_FRAMEADR 0xc38
#define ES1371_REG_DAC2_FRAMECNT 0xc3c
#define ES1371_REG_ADC_FRAMEADR 0xd30
#define ES1371_REG_ADC_FRAMECNT 0xd34
#define ES1371_FMT_U8_MONO 0
#define ES1371_FMT_U8_STEREO 1
#define ES1371_FMT_S16_MONO 2
#define ES1371_FMT_S16_STEREO 3
#define ES1371_FMT_STEREO 1
#define ES1371_FMT_S16 2
#define ES1371_FMT_MASK 3
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
#define CTRL_RECEN_B 0x08000000 /* 1 = don't mix analog in to digital out */
#define CTRL_SPDIFEN_B 0x04000000
#define CTRL_JOY_SHIFT 24
#define CTRL_JOY_MASK 3
#define CTRL_JOY_200 0x00000000 /* joystick base address */
#define CTRL_JOY_208 0x01000000
#define CTRL_JOY_210 0x02000000
#define CTRL_JOY_218 0x03000000
#define CTRL_GPIO_IN0 0x00100000 /* general purpose inputs/outputs */
#define CTRL_GPIO_IN1 0x00200000
#define CTRL_GPIO_IN2 0x00400000
#define CTRL_GPIO_IN3 0x00800000
#define CTRL_GPIO_OUT0 0x00010000
#define CTRL_GPIO_OUT1 0x00020000
#define CTRL_GPIO_OUT2 0x00040000
#define CTRL_GPIO_OUT3 0x00080000
#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
#define CTRL_SYNCRES 0x00004000 /* AC97 warm reset */
#define CTRL_ADCSTOP 0x00002000 /* stop ADC transfers */
#define CTRL_PWR_INTRM 0x00001000 /* 1 = power level ints enabled */
#define CTRL_M_CB 0x00000800 /* recording source: 0 = ADC, 1 = MPEG */
#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
#define CTRL_PDLEV0 0x00000000 /* power down level */
#define CTRL_PDLEV1 0x00000100
#define CTRL_PDLEV2 0x00000200
#define CTRL_PDLEV3 0x00000300
#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
#define CTRL_ADC_EN 0x00000010 /* enable ADC */
#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port */
#define CTRL_XTALCLKDIS 0x00000002 /* 1 = disable crystal clock input */
#define CTRL_PCICLKDIS 0x00000001 /* 1 = disable PCI clock distribution */
#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
#define CSTAT_5880_AC97_RST 0x20000000 /* CT5880 Reset bit */
#define STAT_EN_SPDIF 0x00040000 /* enable S/PDIF circuitry */
#define STAT_TS_SPDIF 0x00020000 /* test S/PDIF circuitry */
#define STAT_TESTMODE 0x00010000 /* test ASIC */
#define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */
#define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
#define STAT_SH_VC 6
#define STAT_MPWR 0x00000020 /* power level interrupt */
#define STAT_MCCB 0x00000010 /* CCB int pending */
#define STAT_UART 0x00000008 /* UART int pending */
#define STAT_DAC1 0x00000004 /* DAC1 int pending */
#define STAT_DAC2 0x00000002 /* DAC2 int pending */
#define STAT_ADC 0x00000001 /* ADC int pending */
#define USTAT_RXINT 0x80 /* UART rx int pending */
#define USTAT_TXINT 0x04 /* UART tx int pending */
#define USTAT_TXRDY 0x02 /* UART tx ready */
#define USTAT_RXRDY 0x01 /* UART rx ready */
#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */
#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */
#define UCTRL_ENA_TXINT 0x20 /* enable TX int */
#define UCTRL_CNTRL 0x03 /* control field */
#define UCTRL_CNTRL_SWR 0x03 /* software reset command */
/* sample rate converter */
#define SRC_OKSTATE 1
#define SRC_RAMADDR_MASK 0xfe000000
#define SRC_RAMADDR_SHIFT 25
#define SRC_DAC1FREEZE (1UL << 21)
#define SRC_DAC2FREEZE (1UL << 20)
#define SRC_ADCFREEZE (1UL << 19)
#define SRC_WE 0x01000000 /* read/write control for SRC RAM */
#define SRC_BUSY 0x00800000 /* SRC busy */
#define SRC_DIS 0x00400000 /* 1 = disable SRC */
#define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */
#define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */
#define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */
#define SRC_CTLMASK 0x00780000
#define SRC_RAMDATA_MASK 0x0000ffff
#define SRC_RAMDATA_SHIFT 0
#define SRCREG_ADC 0x78
#define SRCREG_DAC1 0x70
#define SRCREG_DAC2 0x74
#define SRCREG_VOL_ADC 0x6c
#define SRCREG_VOL_DAC1 0x7c
#define SRCREG_VOL_DAC2 0x7e
#define SRCREG_TRUNC_N 0x00
#define SRCREG_INT_REGS 0x01
#define SRCREG_ACCUM_FRAC 0x02
#define SRCREG_VFREQ_FRAC 0x03
#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */
#define CODEC_PIADD_MASK 0x007f0000
#define CODEC_PIADD_SHIFT 16
#define CODEC_PIDAT_MASK 0x0000ffff
#define CODEC_PIDAT_SHIFT 0
#define CODEC_RDY 0x80000000 /* AC97 read data valid */
#define CODEC_WIP 0x40000000 /* AC97 write in progress */
#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */
#define CODEC_POADD_MASK 0x007f0000
#define CODEC_POADD_SHIFT 16
#define CODEC_PODAT_MASK 0x0000ffff
#define CODEC_PODAT_SHIFT 0
#define LEGACY_JFAST 0x80000000 /* fast joystick timing */
#define LEGACY_FIRQ 0x01000000 /* force IRQ */
#define SCTRL_DACTEST 0x00400000 /* 1 = DAC test, test vector generation purposes */
#define SCTRL_P2ENDINC 0x00380000 /* */
#define SCTRL_SH_P2ENDINC 19
#define SCTRL_P2STINC 0x00070000 /* */
#define SCTRL_SH_P2STINC 16
#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */
#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */
#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
#define SCTRL_R1FMT 0x00000030 /* format mask */
#define SCTRL_SH_R1FMT 4
#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
#define SCTRL_P2FMT 0x0000000c /* format mask */
#define SCTRL_SH_P2FMT 2
#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
#define SCTRL_P1FMT 0x00000003 /* format mask */
#define SCTRL_SH_P1FMT 0
/* misc stuff */
#define POLL_COUNT 0x1000
#define FMODE_DAC 4 /* slight misuse of mode_t */
/* MIDI buffer sizes */
#define MIDIINBUF 256
#define MIDIOUTBUF 256
#define FMODE_MIDI_SHIFT 3
#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT)
#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
#define ES1371_MODULE_NAME "es1371"
#define PFX ES1371_MODULE_NAME ": "
/* --------------------------------------------------------------------- */
struct es1371_state {
/* magic */
unsigned int magic;
/* list of es1371 devices */
struct list_head devs;
/* the corresponding pci_dev structure */
struct pci_dev *dev;
/* soundcore stuff */
int dev_audio;
int dev_dac;
int dev_midi;
/* hardware resources */
unsigned long io; /* long for SPARC */
unsigned int irq;
/* PCI ID's */
u16 vendor;
u16 device;
u8 rev; /* the chip revision */
/* options */
int spdif_volume; /* S/PDIF output is enabled if != -1 */
#ifdef ES1371_DEBUG
/* debug /proc entry */
struct proc_dir_entry *ps;
#endif /* ES1371_DEBUG */
struct ac97_codec *codec;
/* wave stuff */
unsigned ctrl;
unsigned sctrl;
unsigned dac1rate, dac2rate, adcrate;
spinlock_t lock;
struct mutex open_mutex;
mode_t open_mode;
wait_queue_head_t open_wait;
struct dmabuf {
void *rawbuf;
dma_addr_t dmaaddr;
unsigned buforder;
unsigned numfrag;
unsigned fragshift;
unsigned hwptr, swptr;
unsigned total_bytes;
int count;
unsigned error; /* over/underrun */
wait_queue_head_t wait;
/* redundant, but makes calculations easier */
unsigned fragsize;
unsigned dmasize;
unsigned fragsamples;
/* OSS stuff */
unsigned mapped:1;
unsigned ready:1;
unsigned endcleared:1;
unsigned enabled:1;
unsigned ossfragshift;
int ossmaxfrags;
unsigned subdivision;
} dma_dac1, dma_dac2, dma_adc;
/* midi stuff */
struct {
unsigned ird, iwr, icnt;
unsigned ord, owr, ocnt;
wait_queue_head_t iwait;
wait_queue_head_t owait;
unsigned char ibuf[MIDIINBUF];
unsigned char obuf[MIDIOUTBUF];
} midi;
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
struct mutex sem;
};
/* --------------------------------------------------------------------- */
static LIST_HEAD(devs);
/* --------------------------------------------------------------------- */
static inline unsigned ld2(unsigned int x)
{
unsigned r = 0;
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 4) {
x >>= 2;
r += 2;
}
if (x >= 2)
r++;
return r;
}
/* --------------------------------------------------------------------- */
static unsigned wait_src_ready(struct es1371_state *s)
{
unsigned int t, r;
for (t = 0; t < POLL_COUNT; t++) {
if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY))
return r;
udelay(1);
}
printk(KERN_DEBUG PFX "sample rate converter timeout r = 0x%08x\n", r);
return r;
}
static unsigned src_read(struct es1371_state *s, unsigned reg)
{
unsigned int temp,i,orig;
/* wait for ready */
temp = wait_src_ready (s);
/* we can only access the SRC at certain times, make sure
we're allowed to before we read */
orig = temp;
/* expose the SRC state bits */
outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL,
s->io + ES1371_REG_SRCONV);
/* now, wait for busy and the correct time to read */
temp = wait_src_ready (s);
if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){
/* wait for the right state */
for (i=0; i<POLL_COUNT; i++){
temp = inl (s->io + ES1371_REG_SRCONV);
if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 ))
break;
}
}
/* hide the state bits */
outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV);
return temp;
}
static void src_write(struct es1371_state *s, unsigned reg, unsigned data)
{
unsigned int r;
r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC);
r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK;
r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK;
outl(r | SRC_WE, s->io + ES1371_REG_SRCONV);
}
/* --------------------------------------------------------------------- */
/* most of the following here is black magic */
static void set_adc_rate(struct es1371_state *s, unsigned rate)
{
unsigned long flags;
unsigned int n, truncm, freq;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
n = rate / 3000;
if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
n--;
truncm = (21 * n - 1) | 1;
freq = ((48000UL << 15) / rate) * n;
s->adcrate = (48000UL << 15) / (freq / n);
spin_lock_irqsave(&s->lock, flags);
if (rate >= 24000) {
if (truncm > 239)
truncm = 239;
src_write(s, SRCREG_ADC+SRCREG_TRUNC_N,
(((239 - truncm) >> 1) << 9) | (n << 4));
} else {
if (truncm > 119)
truncm = 119;
src_write(s, SRCREG_ADC+SRCREG_TRUNC_N,
0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
}
src_write(s, SRCREG_ADC+SRCREG_INT_REGS,
(src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) |
((freq >> 5) & 0xfc00));
src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff);
src_write(s, SRCREG_VOL_ADC, n << 8);
src_write(s, SRCREG_VOL_ADC+1, n << 8);
spin_unlock_irqrestore(&s->lock, flags);
}
static void set_dac1_rate(struct es1371_state *s, unsigned rate)
{
unsigned long flags;
unsigned int freq, r;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
freq = ((rate << 15) + 1500) / 3000;
s->dac1rate = (freq * 3000 + 16384) >> 15;
spin_lock_irqsave(&s->lock, flags);
r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1;
outl(r, s->io + ES1371_REG_SRCONV);
src_write(s, SRCREG_DAC1+SRCREG_INT_REGS,
(src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) |
((freq >> 5) & 0xfc00));
src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff);
r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC));
outl(r, s->io + ES1371_REG_SRCONV);
spin_unlock_irqrestore(&s->lock, flags);
}
static void set_dac2_rate(struct es1371_state *s, unsigned rate)
{
unsigned long flags;
unsigned int freq, r;
if (rate > 48000)
rate = 48000;
if (rate < 4000)
rate = 4000;
freq = ((rate << 15) + 1500) / 3000;
s->dac2rate = (freq * 3000 + 16384) >> 15;
spin_lock_irqsave(&s->lock, flags);
r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2;
outl(r, s->io + ES1371_REG_SRCONV);
src_write(s, SRCREG_DAC2+SRCREG_INT_REGS,
(src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) |
((freq >> 5) & 0xfc00));
src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff);
r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC));
outl(r, s->io + ES1371_REG_SRCONV);
spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
static void __devinit src_init(struct es1371_state *s)
{
unsigned int i;
/* before we enable or disable the SRC we need
to wait for it to become ready */
wait_src_ready(s);
outl(SRC_DIS, s->io + ES1371_REG_SRCONV);
for (i = 0; i < 0x80; i++)
src_write(s, i, 0);
src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4);
src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10);
src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4);
src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10);
src_write(s, SRCREG_VOL_ADC, 1 << 12);
src_write(s, SRCREG_VOL_ADC+1, 1 << 12);
src_write(s, SRCREG_VOL_DAC1, 1 << 12);
src_write(s, SRCREG_VOL_DAC1+1, 1 << 12);
src_write(s, SRCREG_VOL_DAC2, 1 << 12);
src_write(s, SRCREG_VOL_DAC2+1, 1 << 12);
set_adc_rate(s, 22050);
set_dac1_rate(s, 22050);
set_dac2_rate(s, 22050);
/* WARNING:
* enabling the sample rate converter without properly programming
* its parameters causes the chip to lock up (the SRC busy bit will
* be stuck high, and I've found no way to rectify this other than
* power cycle)
*/
wait_src_ready(s);
outl(0, s->io+ES1371_REG_SRCONV);
}
/* --------------------------------------------------------------------- */
static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
{
struct es1371_state *s = (struct es1371_state *)codec->private_data;
unsigned long flags;
unsigned t, x;
spin_lock_irqsave(&s->lock, flags);
for (t = 0; t < POLL_COUNT; t++)
if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
break;
/* save the current state for later */
x = wait_src_ready(s);
/* enable SRC state data in SRC mux */
outl((x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
s->io+ES1371_REG_SRCONV);
/* wait for not busy (state 0) first to avoid
transition states */
for (t=0; t<POLL_COUNT; t++){
if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0 )
break;
udelay(1);
}
/* wait for a SAFE time to write addr/data and then do it, dammit */
for (t=0; t<POLL_COUNT; t++){
if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000)
break;
udelay(1);
}
outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC);
/* restore SRC reg */
wait_src_ready(s);
outl(x, s->io+ES1371_REG_SRCONV);
spin_unlock_irqrestore(&s->lock, flags);
}
static u16 rdcodec(struct ac97_codec *codec, u8 addr)
{
struct es1371_state *s = (struct es1371_state *)codec->private_data;
unsigned long flags;
unsigned t, x;
spin_lock_irqsave(&s->lock, flags);
/* wait for WIP to go away */
for (t = 0; t < 0x1000; t++)
if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
break;
/* save the current state for later */
x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC));
/* enable SRC state data in SRC mux */
outl( x | 0x00010000,
s->io+ES1371_REG_SRCONV);
/* wait for not busy (state 0) first to avoid
transition states */
for (t=0; t<POLL_COUNT; t++){
if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0 )
break;
udelay(1);
}
/* wait for a SAFE time to write addr/data and then do it, dammit */
for (t=0; t<POLL_COUNT; t++){
if((inl(s->io+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000)
break;
udelay(1);
}
outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC);
/* restore SRC reg */
wait_src_ready(s);
outl(x, s->io+ES1371_REG_SRCONV);
/* wait for WIP again */
for (t = 0; t < 0x1000; t++)
if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
break;
/* now wait for the stinkin' data (RDY) */
for (t = 0; t < POLL_COUNT; t++)
if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY)
break;
spin_unlock_irqrestore(&s->lock, flags);
return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
}
/* --------------------------------------------------------------------- */
static inline void stop_adc(struct es1371_state *s)
{
unsigned long flags;
spin_lock_irqsave(&s->lock, flags);
s->ctrl &= ~CTRL_ADC_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
static inline void stop_dac1(struct es1371_state *s)
{
unsigned long flags;
spin_lock_irqsave(&s->lock, flags);
s->ctrl &= ~CTRL_DAC1_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
static inline void stop_dac2(struct es1371_state *s)
{
unsigned long flags;
spin_lock_irqsave(&s->lock, flags);
s->ctrl &= ~CTRL_DAC2_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
static void start_dac1(struct es1371_state *s)
{
unsigned long flags;
unsigned fragremain, fshift;
spin_lock_irqsave(&s->lock, flags);
if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0)
&& s->dma_dac1.ready) {
s->ctrl |= CTRL_DAC1_EN;
s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1));
fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
if (fragremain < 2*fshift)
fragremain = s->dma_dac1.fragsize;
outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT);
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT);
}
spin_unlock_irqrestore(&s->lock, flags);
}
static void start_dac2(struct es1371_state *s)
{
unsigned long flags;
unsigned fragremain, fshift;
spin_lock_irqsave(&s->lock, flags);
if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0)
&& s->dma_dac2.ready) {
s->ctrl |= CTRL_DAC2_EN;
s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN |
SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN |
(((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) |
(0 << SCTRL_SH_P2STINC);
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1));
fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
if (fragremain < 2*fshift)
fragremain = s->dma_dac2.fragsize;
outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT);
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT);
}
spin_unlock_irqrestore(&s->lock, flags);
}
static void start_adc(struct es1371_state *s)
{
unsigned long flags;
unsigned fragremain, fshift;
spin_lock_irqsave(&s->lock, flags);
if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
&& s->dma_adc.ready) {
s->ctrl |= CTRL_ADC_EN;
s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1));
fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT];
if (fragremain < 2*fshift)
fragremain = s->dma_adc.fragsize;
outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT);
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT);
}
spin_unlock_irqrestore(&s->lock, flags);
}
/* --------------------------------------------------------------------- */
#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
#define DMABUF_MINORDER 1
static inline void dealloc_dmabuf(struct es1371_state *s, struct dmabuf *db)
{
struct page *page, *pend;
if (db->rawbuf) {
/* undo marking the pages as reserved */
pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
for (page = virt_to_page(db->rawbuf); page <= pend; page++)
ClearPageReserved(page);
pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);
}
db->rawbuf = NULL;
db->mapped = db->ready = 0;
}
static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg)
{
int order;
unsigned bytepersec;
unsigned bufs;
struct page *page, *pend;
db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
if (!db->rawbuf) {
db->ready = db->mapped = 0;
for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
break;
if (!db->rawbuf)
return -ENOMEM;
db->buforder = order;
/* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */
pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
for (page = virt_to_page(db->rawbuf); page <= pend; page++)
SetPageReserved(page);
}
fmt &= ES1371_FMT_MASK;
bytepersec = rate << sample_shift[fmt];
bufs = PAGE_SIZE << db->buforder;
if (db->ossfragshift) {
if ((1000 << db->ossfragshift) < bytepersec)
db->fragshift = ld2(bytepersec/1000);
else
db->fragshift = db->ossfragshift;
} else {
db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
if (db->fragshift < 3)
db->fragshift = 3;
}
db->numfrag = bufs >> db->fragshift;
while (db->numfrag < 4 && db->fragshift > 3) {
db->fragshift--;
db->numfrag = bufs >> db->fragshift;
}
db->fragsize = 1 << db->fragshift;
if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
db->numfrag = db->ossmaxfrags;
db->fragsamples = db->fragsize >> sample_shift[fmt];
db->dmasize = db->numfrag << db->fragshift;
memset(db->rawbuf, (fmt & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize);
outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE);
outl(db->dmaaddr, s->io+(reg & 0xff));
outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff));
db->enabled = 1;
db->ready = 1;
return 0;
}
static inline int prog_dmabuf_adc(struct es1371_state *s)
{
stop_adc(s);
return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK,
ES1371_REG_ADC_FRAMEADR);
}
static inline int prog_dmabuf_dac2(struct es1371_state *s)
{
stop_dac2(s);
return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK,
ES1371_REG_DAC2_FRAMEADR);
}
static inline int prog_dmabuf_dac1(struct es1371_state *s)
{
stop_dac1(s);
return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK,
ES1371_REG_DAC1_FRAMEADR);
}
static inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg)
{
unsigned hwptr, diff;
outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE);
hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc;
diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize;
db->hwptr = hwptr;
return diff;
}
static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c)
{
if (bptr + len > bsize) {
unsigned x = bsize - bptr;
memset(((char *)buf) + bptr, c, x);
bptr = 0;
len -= x;
}
memset(((char *)buf) + bptr, c, len);
}
/* call with spinlock held! */
static void es1371_update_ptr(struct es1371_state *s)
{
int diff;
/* update ADC pointer */
if (s->ctrl & CTRL_ADC_EN) {
diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT);
s->dma_adc.total_bytes += diff;
s->dma_adc.count += diff;
if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
wake_up(&s->dma_adc.wait);
if (!s->dma_adc.mapped) {
if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
s->ctrl &= ~CTRL_ADC_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
s->dma_adc.error++;
}
}
}
/* update DAC1 pointer */
if (s->ctrl & CTRL_DAC1_EN) {
diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT);
s->dma_dac1.total_bytes += diff;
if (s->dma_dac1.mapped) {
s->dma_dac1.count += diff;
if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
wake_up(&s->dma_dac1.wait);
} else {
s->dma_dac1.count -= diff;
if (s->dma_dac1.count <= 0) {
s->ctrl &= ~CTRL_DAC1_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
s->dma_dac1.error++;
} else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr,
s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
s->dma_dac1.endcleared = 1;
}
if (s->dma_dac1.count + (signed)s->dma_dac1.fragsize <= (signed)s->dma_dac1.dmasize)
wake_up(&s->dma_dac1.wait);
}
}
/* update DAC2 pointer */
if (s->ctrl & CTRL_DAC2_EN) {
diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT);
s->dma_dac2.total_bytes += diff;
if (s->dma_dac2.mapped) {
s->dma_dac2.count += diff;
if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize)
wake_up(&s->dma_dac2.wait);
} else {
s->dma_dac2.count -= diff;
if (s->dma_dac2.count <= 0) {
s->ctrl &= ~CTRL_DAC2_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
s->dma_dac2.error++;
} else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr,
s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
s->dma_dac2.endcleared = 1;
}
if (s->dma_dac2.count + (signed)s->dma_dac2.fragsize <= (signed)s->dma_dac2.dmasize)
wake_up(&s->dma_dac2.wait);
}
}
}
/* hold spinlock for the following! */
static void es1371_handle_midi(struct es1371_state *s)
{
unsigned char ch;
int wake;
if (!(s->ctrl & CTRL_UART_EN))
return;
wake = 0;
while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) {
ch = inb(s->io+ES1371_REG_UART_DATA);
if (s->midi.icnt < MIDIINBUF) {
s->midi.ibuf[s->midi.iwr] = ch;
s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
s->midi.icnt++;
}
wake = 1;
}
if (wake)
wake_up(&s->midi.iwait);
wake = 0;
while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) {
outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA);
s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
s->midi.ocnt--;
if (s->midi.ocnt < MIDIOUTBUF-16)
wake = 1;
}
if (wake)
wake_up(&s->midi.owait);
outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL);
}
static irqreturn_t es1371_interrupt(int irq, void *dev_id)
{
struct es1371_state *s = dev_id;
unsigned int intsrc, sctl;
/* fastpath out, to ease interrupt sharing */
intsrc = inl(s->io+ES1371_REG_STATUS);
if (!(intsrc & 0x80000000))
return IRQ_NONE;
spin_lock(&s->lock);
/* clear audio interrupts first */
sctl = s->sctrl;
if (intsrc & STAT_ADC)
sctl &= ~SCTRL_R1INTEN;
if (intsrc & STAT_DAC1)
sctl &= ~SCTRL_P1INTEN;
if (intsrc & STAT_DAC2)
sctl &= ~SCTRL_P2INTEN;
outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL);
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
es1371_update_ptr(s);
es1371_handle_midi(s);
spin_unlock(&s->lock);
return IRQ_HANDLED;
}
/* --------------------------------------------------------------------- */
static const char invalid_magic[] = KERN_CRIT PFX "invalid magic value\n";
#define VALIDATE_STATE(s) \
({ \
if (!(s) || (s)->magic != ES1371_MAGIC) { \
printk(invalid_magic); \
return -ENXIO; \
} \
})
/* --------------------------------------------------------------------- */
/* Conversion table for S/PDIF PCM volume emulation through the SRC */
/* dB-linear table of DAC vol values; -0dB to -46.5dB with mute */
static const unsigned short DACVolTable[101] =
{
0x1000, 0x0f2a, 0x0e60, 0x0da0, 0x0cea, 0x0c3e, 0x0b9a, 0x0aff,
0x0a6d, 0x09e1, 0x095e, 0x08e1, 0x086a, 0x07fa, 0x078f, 0x072a,
0x06cb, 0x0670, 0x061a, 0x05c9, 0x057b, 0x0532, 0x04ed, 0x04ab,
0x046d, 0x0432, 0x03fa, 0x03c5, 0x0392, 0x0363, 0x0335, 0x030b,
0x02e2, 0x02bc, 0x0297, 0x0275, 0x0254, 0x0235, 0x0217, 0x01fb,
0x01e1, 0x01c8, 0x01b0, 0x0199, 0x0184, 0x0170, 0x015d, 0x014b,
0x0139, 0x0129, 0x0119, 0x010b, 0x00fd, 0x00f0, 0x00e3, 0x00d7,
0x00cc, 0x00c1, 0x00b7, 0x00ae, 0x00a5, 0x009c, 0x0094, 0x008c,
0x0085, 0x007e, 0x0077, 0x0071, 0x006b, 0x0066, 0x0060, 0x005b,
0x0057, 0x0052, 0x004e, 0x004a, 0x0046, 0x0042, 0x003f, 0x003c,
0x0038, 0x0036, 0x0033, 0x0030, 0x002e, 0x002b, 0x0029, 0x0027,
0x0025, 0x0023, 0x0021, 0x001f, 0x001e, 0x001c, 0x001b, 0x0019,
0x0018, 0x0017, 0x0016, 0x0014, 0x0000
};
/*
* when we are in S/PDIF mode, we want to disable any analog output so
* we filter the mixer ioctls
*/
static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
{
struct es1371_state *s = (struct es1371_state *)codec->private_data;
int val;
unsigned long flags;
unsigned int left, right;
VALIDATE_STATE(s);
/* filter mixer ioctls to catch PCM and MASTER volume when in S/PDIF mode */
if (s->spdif_volume == -1)
return codec->mixer_ioctl(codec, cmd, arg);
switch (cmd) {
case SOUND_MIXER_WRITE_VOLUME:
return 0;
case SOUND_MIXER_WRITE_PCM: /* use SRC for PCM volume */
if (get_user(val, (int __user *)arg))
return -EFAULT;
right = ((val >> 8) & 0xff);
left = (val & 0xff);
if (right > 100)
right = 100;
if (left > 100)
left = 100;
s->spdif_volume = (right << 8) | left;
spin_lock_irqsave(&s->lock, flags);
src_write(s, SRCREG_VOL_DAC2, DACVolTable[100 - left]);
src_write(s, SRCREG_VOL_DAC2+1, DACVolTable[100 - right]);
spin_unlock_irqrestore(&s->lock, flags);
return 0;
case SOUND_MIXER_READ_PCM:
return put_user(s->spdif_volume, (int __user *)arg);
}
return codec->mixer_ioctl(codec, cmd, arg);
}
/* --------------------------------------------------------------------- */
/*
* AC97 Mixer Register to Connections mapping of the Concert 97 board
*
* AC97_MASTER_VOL_STEREO Line Out
* AC97_MASTER_VOL_MONO TAD Output
* AC97_PCBEEP_VOL none
* AC97_PHONE_VOL TAD Input (mono)
* AC97_MIC_VOL MIC Input (mono)
* AC97_LINEIN_VOL Line Input (stereo)
* AC97_CD_VOL CD Input (stereo)
* AC97_VIDEO_VOL none
* AC97_AUX_VOL Aux Input (stereo)
* AC97_PCMOUT_VOL Wave Output (stereo)
*/
static int es1371_open_mixdev(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct list_head *list;
struct es1371_state *s;
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
s = list_entry(list, struct es1371_state, devs);
if (s->codec->dev_mixer == minor)
break;
}
VALIDATE_STATE(s);
file->private_data = s;
return nonseekable_open(inode, file);
}
static int es1371_release_mixdev(struct inode *inode, struct file *file)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
VALIDATE_STATE(s);
return 0;
}
static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
struct ac97_codec *codec = s->codec;
return mixdev_ioctl(codec, cmd, arg);
}
static /*const*/ struct file_operations es1371_mixer_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = es1371_ioctl_mixdev,
.open = es1371_open_mixdev,
.release = es1371_release_mixdev,
};
/* --------------------------------------------------------------------- */
static int drain_dac1(struct es1371_state *s, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int count, tmo;
if (s->dma_dac1.mapped || !s->dma_dac1.ready)
return 0;
add_wait_queue(&s->dma_dac1.wait, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&s->lock, flags);
count = s->dma_dac1.count;
spin_unlock_irqrestore(&s->lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (nonblock) {
remove_wait_queue(&s->dma_dac1.wait, &wait);
set_current_state(TASK_RUNNING);
return -EBUSY;
}
tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate;
tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
if (!schedule_timeout(tmo + 1))
DBG(printk(KERN_DEBUG PFX "dac1 dma timed out??\n");)
}
remove_wait_queue(&s->dma_dac1.wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
static int drain_dac2(struct es1371_state *s, int nonblock)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int count, tmo;
if (s->dma_dac2.mapped || !s->dma_dac2.ready)
return 0;
add_wait_queue(&s->dma_dac2.wait, &wait);
for (;;) {
__set_current_state(TASK_UNINTERRUPTIBLE);
spin_lock_irqsave(&s->lock, flags);
count = s->dma_dac2.count;
spin_unlock_irqrestore(&s->lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (nonblock) {
remove_wait_queue(&s->dma_dac2.wait, &wait);
set_current_state(TASK_RUNNING);
return -EBUSY;
}
tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate;
tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
if (!schedule_timeout(tmo + 1))
DBG(printk(KERN_DEBUG PFX "dac2 dma timed out??\n");)
}
remove_wait_queue(&s->dma_dac2.wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
/* --------------------------------------------------------------------- */
static ssize_t es1371_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
int cnt;
VALIDATE_STATE(s);
if (s->dma_adc.mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
mutex_lock(&s->sem);
if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
goto out2;
add_wait_queue(&s->dma_adc.wait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
swptr = s->dma_adc.swptr;
cnt = s->dma_adc.dmasize-swptr;
if (s->dma_adc.count < cnt)
cnt = s->dma_adc.count;
if (cnt <= 0)
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&s->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (s->dma_adc.enabled)
start_adc(s);
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
goto out;
}
mutex_unlock(&s->sem);
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&s->sem);
if (s->dma_adc.mapped)
{
ret = -ENXIO;
goto out;
}
continue;
}
if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) {
if (!ret)
ret = -EFAULT;
goto out;
}
swptr = (swptr + cnt) % s->dma_adc.dmasize;
spin_lock_irqsave(&s->lock, flags);
s->dma_adc.swptr = swptr;
s->dma_adc.count -= cnt;
spin_unlock_irqrestore(&s->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
if (s->dma_adc.enabled)
start_adc(s);
}
out:
mutex_unlock(&s->sem);
out2:
remove_wait_queue(&s->dma_adc.wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
static ssize_t es1371_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
unsigned long flags;
unsigned swptr;
int cnt;
VALIDATE_STATE(s);
if (s->dma_dac2.mapped)
return -ENXIO;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
mutex_lock(&s->sem);
if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
goto out3;
ret = 0;
add_wait_queue(&s->dma_dac2.wait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
if (s->dma_dac2.count < 0) {
s->dma_dac2.count = 0;
s->dma_dac2.swptr = s->dma_dac2.hwptr;
}
swptr = s->dma_dac2.swptr;
cnt = s->dma_dac2.dmasize-swptr;
if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
if (cnt <= 0)
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&s->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (s->dma_dac2.enabled)
start_dac2(s);
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
goto out;
}
mutex_unlock(&s->sem);
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
goto out2;
}
mutex_lock(&s->sem);
if (s->dma_dac2.mapped)
{
ret = -ENXIO;
goto out;
}
continue;
}
if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) {
if (!ret)
ret = -EFAULT;
goto out;
}
swptr = (swptr + cnt) % s->dma_dac2.dmasize;
spin_lock_irqsave(&s->lock, flags);
s->dma_dac2.swptr = swptr;
s->dma_dac2.count += cnt;
s->dma_dac2.endcleared = 0;
spin_unlock_irqrestore(&s->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
if (s->dma_dac2.enabled)
start_dac2(s);
}
out:
mutex_unlock(&s->sem);
out2:
remove_wait_queue(&s->dma_dac2.wait, &wait);
out3:
set_current_state(TASK_RUNNING);
return ret;
}
/* No kernel lock - we have our own spinlock */
static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
unsigned long flags;
unsigned int mask = 0;
VALIDATE_STATE(s);
if (file->f_mode & FMODE_WRITE) {
if (!s->dma_dac2.ready && prog_dmabuf_dac2(s))
return 0;
poll_wait(file, &s->dma_dac2.wait, wait);
}
if (file->f_mode & FMODE_READ) {
if (!s->dma_adc.ready && prog_dmabuf_adc(s))
return 0;
poll_wait(file, &s->dma_adc.wait, wait);
}
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
if (file->f_mode & FMODE_READ) {
if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
if (s->dma_dac2.mapped) {
if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize)
mask |= POLLOUT | POLLWRNORM;
} else {
if ((signed)s->dma_dac2.dmasize >= s->dma_dac2.count + (signed)s->dma_dac2.fragsize)
mask |= POLLOUT | POLLWRNORM;
}
}
spin_unlock_irqrestore(&s->lock, flags);
return mask;
}
static int es1371_mmap(struct file *file, struct vm_area_struct *vma)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
struct dmabuf *db;
int ret = 0;
unsigned long size;
VALIDATE_STATE(s);
lock_kernel();
mutex_lock(&s->sem);
if (vma->vm_flags & VM_WRITE) {
if ((ret = prog_dmabuf_dac2(s)) != 0) {
goto out;
}
db = &s->dma_dac2;
} else if (vma->vm_flags & VM_READ) {
if ((ret = prog_dmabuf_adc(s)) != 0) {
goto out;
}
db = &s->dma_adc;
} else {
ret = -EINVAL;
goto out;
}
if (vma->vm_pgoff != 0) {
ret = -EINVAL;
goto out;
}
size = vma->vm_end - vma->vm_start;
if (size > (PAGE_SIZE << db->buforder)) {
ret = -EINVAL;
goto out;
}
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(db->rawbuf) >> PAGE_SHIFT,
size, vma->vm_page_prot)) {
ret = -EAGAIN;
goto out;
}
db->mapped = 1;
out:
mutex_unlock(&s->sem);
unlock_kernel();
return ret;
}
static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int count;
int val, mapped, ret;
void __user *argp = (void __user *)arg;
int __user *p = argp;
VALIDATE_STATE(s);
mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) ||
((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
switch (cmd) {
case OSS_GETVERSION:
return put_user(SOUND_VERSION, p);
case SNDCTL_DSP_SYNC:
if (file->f_mode & FMODE_WRITE)
return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);
return 0;
case SNDCTL_DSP_SETDUPLEX:
return 0;
case SNDCTL_DSP_GETCAPS:
return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p);
case SNDCTL_DSP_RESET:
if (file->f_mode & FMODE_WRITE) {
stop_dac2(s);
synchronize_irq(s->irq);
s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0;
}
if (file->f_mode & FMODE_READ) {
stop_adc(s);
synchronize_irq(s->irq);
s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
}
return 0;
case SNDCTL_DSP_SPEED:
if (get_user(val, p))
return -EFAULT;
if (val >= 0) {
if (file->f_mode & FMODE_READ) {
stop_adc(s);
s->dma_adc.ready = 0;
set_adc_rate(s, val);
}
if (file->f_mode & FMODE_WRITE) {
stop_dac2(s);
s->dma_dac2.ready = 0;
set_dac2_rate(s, val);
}
}
return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, p);
case SNDCTL_DSP_STEREO:
if (get_user(val, p))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
stop_adc(s);
s->dma_adc.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val)
s->sctrl |= SCTRL_R1SMB;
else
s->sctrl &= ~SCTRL_R1SMB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
if (file->f_mode & FMODE_WRITE) {
stop_dac2(s);
s->dma_dac2.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val)
s->sctrl |= SCTRL_P2SMB;
else
s->sctrl &= ~SCTRL_P2SMB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
return 0;
case SNDCTL_DSP_CHANNELS:
if (get_user(val, p))
return -EFAULT;
if (val != 0) {
if (file->f_mode & FMODE_READ) {
stop_adc(s);
s->dma_adc.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val >= 2)
s->sctrl |= SCTRL_R1SMB;
else
s->sctrl &= ~SCTRL_R1SMB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
if (file->f_mode & FMODE_WRITE) {
stop_dac2(s);
s->dma_dac2.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val >= 2)
s->sctrl |= SCTRL_P2SMB;
else
s->sctrl &= ~SCTRL_P2SMB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
}
return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, p);
case SNDCTL_DSP_GETFMTS: /* Returns a mask */
return put_user(AFMT_S16_LE|AFMT_U8, p);
case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
if (get_user(val, p))
return -EFAULT;
if (val != AFMT_QUERY) {
if (file->f_mode & FMODE_READ) {
stop_adc(s);
s->dma_adc.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val == AFMT_S16_LE)
s->sctrl |= SCTRL_R1SEB;
else
s->sctrl &= ~SCTRL_R1SEB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
if (file->f_mode & FMODE_WRITE) {
stop_dac2(s);
s->dma_dac2.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val == AFMT_S16_LE)
s->sctrl |= SCTRL_P2SEB;
else
s->sctrl &= ~SCTRL_P2SEB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
}
return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ?
AFMT_S16_LE : AFMT_U8, p);
case SNDCTL_DSP_POST:
return 0;
case SNDCTL_DSP_GETTRIGGER:
val = 0;
if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN)
val |= PCM_ENABLE_INPUT;
if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN)
val |= PCM_ENABLE_OUTPUT;
return put_user(val, p);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, p))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
if (val & PCM_ENABLE_INPUT) {
if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
return ret;
s->dma_adc.enabled = 1;
start_adc(s);
} else {
s->dma_adc.enabled = 0;
stop_adc(s);
}
}
if (file->f_mode & FMODE_WRITE) {
if (val & PCM_ENABLE_OUTPUT) {
if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
return ret;
s->dma_dac2.enabled = 1;
start_dac2(s);
} else {
s->dma_dac2.enabled = 0;
stop_dac2(s);
}
}
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
abinfo.fragsize = s->dma_dac2.fragsize;
count = s->dma_dac2.count;
if (count < 0)
count = 0;
abinfo.bytes = s->dma_dac2.dmasize - count;
abinfo.fragstotal = s->dma_dac2.numfrag;
abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;
spin_unlock_irqrestore(&s->lock, flags);
return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_GETISPACE:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
abinfo.fragsize = s->dma_adc.fragsize;
count = s->dma_adc.count;
if (count < 0)
count = 0;
abinfo.bytes = count;
abinfo.fragstotal = s->dma_adc.numfrag;
abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
spin_unlock_irqrestore(&s->lock, flags);
return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETODELAY:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
count = s->dma_dac2.count;
spin_unlock_irqrestore(&s->lock, flags);
if (count < 0)
count = 0;
return put_user(count, p);
case SNDCTL_DSP_GETIPTR:
if (!(file->f_mode & FMODE_READ))
return -EINVAL;
if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
cinfo.bytes = s->dma_adc.total_bytes;
count = s->dma_adc.count;
if (count < 0)
count = 0;
cinfo.blocks = count >> s->dma_adc.fragshift;
cinfo.ptr = s->dma_adc.hwptr;
if (s->dma_adc.mapped)
s->dma_adc.count &= s->dma_adc.fragsize-1;
spin_unlock_irqrestore(&s->lock, flags);
if (copy_to_user(argp, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETOPTR:
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
if (!s->dma_dac2.ready && (val = prog_dmabuf_dac2(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
cinfo.bytes = s->dma_dac2.total_bytes;
count = s->dma_dac2.count;
if (count < 0)
count = 0;
cinfo.blocks = count >> s->dma_dac2.fragshift;
cinfo.ptr = s->dma_dac2.hwptr;
if (s->dma_dac2.mapped)
s->dma_dac2.count &= s->dma_dac2.fragsize-1;
spin_unlock_irqrestore(&s->lock, flags);
if (copy_to_user(argp, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETBLKSIZE:
if (file->f_mode & FMODE_WRITE) {
if ((val = prog_dmabuf_dac2(s)))
return val;
return put_user(s->dma_dac2.fragsize, p);
}
if ((val = prog_dmabuf_adc(s)))
return val;
return put_user(s->dma_adc.fragsize, p);
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, p))
return -EFAULT;
if (file->f_mode & FMODE_READ) {
s->dma_adc.ossfragshift = val & 0xffff;
s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
if (s->dma_adc.ossfragshift < 4)
s->dma_adc.ossfragshift = 4;
if (s->dma_adc.ossfragshift > 15)
s->dma_adc.ossfragshift = 15;
if (s->dma_adc.ossmaxfrags < 4)
s->dma_adc.ossmaxfrags = 4;
}
if (file->f_mode & FMODE_WRITE) {
s->dma_dac2.ossfragshift = val & 0xffff;
s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff;
if (s->dma_dac2.ossfragshift < 4)
s->dma_dac2.ossfragshift = 4;
if (s->dma_dac2.ossfragshift > 15)
s->dma_dac2.ossfragshift = 15;
if (s->dma_dac2.ossmaxfrags < 4)
s->dma_dac2.ossmaxfrags = 4;
}
return 0;
case SNDCTL_DSP_SUBDIVIDE:
if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
(file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
return -EINVAL;
if (get_user(val, p))
return -EFAULT;
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
if (file->f_mode & FMODE_READ)
s->dma_adc.subdivision = val;
if (file->f_mode & FMODE_WRITE)
s->dma_dac2.subdivision = val;
return 0;
case SOUND_PCM_READ_RATE:
return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, p);
case SOUND_PCM_READ_CHANNELS:
return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, p);
case SOUND_PCM_READ_BITS:
return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 16 : 8, p);
case SOUND_PCM_WRITE_FILTER:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_READ_FILTER:
return -EINVAL;
}
return mixdev_ioctl(s->codec, cmd, arg);
}
static int es1371_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
struct es1371_state *s;
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
s = list_entry(list, struct es1371_state, devs);
if (!((s->dev_audio ^ minor) & ~0xf))
break;
}
VALIDATE_STATE(s);
file->private_data = s;
/* wait for device to become free */
mutex_lock(&s->open_mutex);
while (s->open_mode & file->f_mode) {
if (file->f_flags & O_NONBLOCK) {
mutex_unlock(&s->open_mutex);
return -EBUSY;
}
add_wait_queue(&s->open_wait, &wait);
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&s->open_mutex);
schedule();
remove_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
mutex_lock(&s->open_mutex);
}
if (file->f_mode & FMODE_READ) {
s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
s->dma_adc.enabled = 1;
set_adc_rate(s, 8000);
}
if (file->f_mode & FMODE_WRITE) {
s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0;
s->dma_dac2.enabled = 1;
set_dac2_rate(s, 8000);
}
spin_lock_irqsave(&s->lock, flags);
if (file->f_mode & FMODE_READ) {
s->sctrl &= ~SCTRL_R1FMT;
if ((minor & 0xf) == SND_DEV_DSP16)
s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT;
else
s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT;
}
if (file->f_mode & FMODE_WRITE) {
s->sctrl &= ~SCTRL_P2FMT;
if ((minor & 0xf) == SND_DEV_DSP16)
s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT;
else
s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT;
}
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
mutex_unlock(&s->open_mutex);
mutex_init(&s->sem);
return nonseekable_open(inode, file);
}
static int es1371_release(struct inode *inode, struct file *file)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
VALIDATE_STATE(s);
lock_kernel();
if (file->f_mode & FMODE_WRITE)
drain_dac2(s, file->f_flags & O_NONBLOCK);
mutex_lock(&s->open_mutex);
if (file->f_mode & FMODE_WRITE) {
stop_dac2(s);
dealloc_dmabuf(s, &s->dma_dac2);
}
if (file->f_mode & FMODE_READ) {
stop_adc(s);
dealloc_dmabuf(s, &s->dma_adc);
}
s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE));
mutex_unlock(&s->open_mutex);
wake_up(&s->open_wait);
unlock_kernel();
return 0;
}
static /*const*/ struct file_operations es1371_audio_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = es1371_read,
.write = es1371_write,
.poll = es1371_poll,
.ioctl = es1371_ioctl,
.mmap = es1371_mmap,
.open = es1371_open,
.release = es1371_release,
};
/* --------------------------------------------------------------------- */
static ssize_t es1371_write_dac(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret = 0;
unsigned long flags;
unsigned swptr;
int cnt;
VALIDATE_STATE(s);
if (s->dma_dac1.mapped)
return -ENXIO;
if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
return ret;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
add_wait_queue(&s->dma_dac1.wait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
if (s->dma_dac1.count < 0) {
s->dma_dac1.count = 0;
s->dma_dac1.swptr = s->dma_dac1.hwptr;
}
swptr = s->dma_dac1.swptr;
cnt = s->dma_dac1.dmasize-swptr;
if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
if (cnt <= 0)
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&s->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (s->dma_dac1.enabled)
start_dac1(s);
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
break;
}
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
break;
}
continue;
}
if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) {
if (!ret)
ret = -EFAULT;
break;
}
swptr = (swptr + cnt) % s->dma_dac1.dmasize;
spin_lock_irqsave(&s->lock, flags);
s->dma_dac1.swptr = swptr;
s->dma_dac1.count += cnt;
s->dma_dac1.endcleared = 0;
spin_unlock_irqrestore(&s->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
if (s->dma_dac1.enabled)
start_dac1(s);
}
remove_wait_queue(&s->dma_dac1.wait, &wait);
set_current_state(TASK_RUNNING);
return ret;
}
/* No kernel lock - we have our own spinlock */
static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
unsigned long flags;
unsigned int mask = 0;
VALIDATE_STATE(s);
if (!s->dma_dac1.ready && prog_dmabuf_dac1(s))
return 0;
poll_wait(file, &s->dma_dac1.wait, wait);
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
if (s->dma_dac1.mapped) {
if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
mask |= POLLOUT | POLLWRNORM;
} else {
if ((signed)s->dma_dac1.dmasize >= s->dma_dac1.count + (signed)s->dma_dac1.fragsize)
mask |= POLLOUT | POLLWRNORM;
}
spin_unlock_irqrestore(&s->lock, flags);
return mask;
}
static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
int ret;
unsigned long size;
VALIDATE_STATE(s);
if (!(vma->vm_flags & VM_WRITE))
return -EINVAL;
lock_kernel();
if ((ret = prog_dmabuf_dac1(s)) != 0)
goto out;
ret = -EINVAL;
if (vma->vm_pgoff != 0)
goto out;
size = vma->vm_end - vma->vm_start;
if (size > (PAGE_SIZE << s->dma_dac1.buforder))
goto out;
ret = -EAGAIN;
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(s->dma_dac1.rawbuf) >> PAGE_SHIFT,
size, vma->vm_page_prot))
goto out;
s->dma_dac1.mapped = 1;
ret = 0;
out:
unlock_kernel();
return ret;
}
static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int count;
int val, ret;
int __user *p = (int __user *)arg;
VALIDATE_STATE(s);
switch (cmd) {
case OSS_GETVERSION:
return put_user(SOUND_VERSION, p);
case SNDCTL_DSP_SYNC:
return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/);
case SNDCTL_DSP_SETDUPLEX:
return -EINVAL;
case SNDCTL_DSP_GETCAPS:
return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, p);
case SNDCTL_DSP_RESET:
stop_dac1(s);
synchronize_irq(s->irq);
s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0;
return 0;
case SNDCTL_DSP_SPEED:
if (get_user(val, p))
return -EFAULT;
if (val >= 0) {
stop_dac1(s);
s->dma_dac1.ready = 0;
set_dac1_rate(s, val);
}
return put_user(s->dac1rate, p);
case SNDCTL_DSP_STEREO:
if (get_user(val, p))
return -EFAULT;
stop_dac1(s);
s->dma_dac1.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val)
s->sctrl |= SCTRL_P1SMB;
else
s->sctrl &= ~SCTRL_P1SMB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
return 0;
case SNDCTL_DSP_CHANNELS:
if (get_user(val, p))
return -EFAULT;
if (val != 0) {
stop_dac1(s);
s->dma_dac1.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val >= 2)
s->sctrl |= SCTRL_P1SMB;
else
s->sctrl &= ~SCTRL_P1SMB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, p);
case SNDCTL_DSP_GETFMTS: /* Returns a mask */
return put_user(AFMT_S16_LE|AFMT_U8, p);
case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
if (get_user(val, p))
return -EFAULT;
if (val != AFMT_QUERY) {
stop_dac1(s);
s->dma_dac1.ready = 0;
spin_lock_irqsave(&s->lock, flags);
if (val == AFMT_S16_LE)
s->sctrl |= SCTRL_P1SEB;
else
s->sctrl &= ~SCTRL_P1SEB;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
}
return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, p);
case SNDCTL_DSP_POST:
return 0;
case SNDCTL_DSP_GETTRIGGER:
return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, p);
case SNDCTL_DSP_SETTRIGGER:
if (get_user(val, p))
return -EFAULT;
if (val & PCM_ENABLE_OUTPUT) {
if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
return ret;
s->dma_dac1.enabled = 1;
start_dac1(s);
} else {
s->dma_dac1.enabled = 0;
stop_dac1(s);
}
return 0;
case SNDCTL_DSP_GETOSPACE:
if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
abinfo.fragsize = s->dma_dac1.fragsize;
count = s->dma_dac1.count;
if (count < 0)
count = 0;
abinfo.bytes = s->dma_dac1.dmasize - count;
abinfo.fragstotal = s->dma_dac1.numfrag;
abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;
spin_unlock_irqrestore(&s->lock, flags);
return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETODELAY:
if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
count = s->dma_dac1.count;
spin_unlock_irqrestore(&s->lock, flags);
if (count < 0)
count = 0;
return put_user(count, p);
case SNDCTL_DSP_GETOPTR:
if (!s->dma_dac1.ready && (val = prog_dmabuf_dac1(s)) != 0)
return val;
spin_lock_irqsave(&s->lock, flags);
es1371_update_ptr(s);
cinfo.bytes = s->dma_dac1.total_bytes;
count = s->dma_dac1.count;
if (count < 0)
count = 0;
cinfo.blocks = count >> s->dma_dac1.fragshift;
cinfo.ptr = s->dma_dac1.hwptr;
if (s->dma_dac1.mapped)
s->dma_dac1.count &= s->dma_dac1.fragsize-1;
spin_unlock_irqrestore(&s->lock, flags);
if (copy_to_user((void __user *)arg, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETBLKSIZE:
if ((val = prog_dmabuf_dac1(s)))
return val;
return put_user(s->dma_dac1.fragsize, p);
case SNDCTL_DSP_SETFRAGMENT:
if (get_user(val, p))
return -EFAULT;
s->dma_dac1.ossfragshift = val & 0xffff;
s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
if (s->dma_dac1.ossfragshift < 4)
s->dma_dac1.ossfragshift = 4;
if (s->dma_dac1.ossfragshift > 15)
s->dma_dac1.ossfragshift = 15;
if (s->dma_dac1.ossmaxfrags < 4)
s->dma_dac1.ossmaxfrags = 4;
return 0;
case SNDCTL_DSP_SUBDIVIDE:
if (s->dma_dac1.subdivision)
return -EINVAL;
if (get_user(val, p))
return -EFAULT;
if (val != 1 && val != 2 && val != 4)
return -EINVAL;
s->dma_dac1.subdivision = val;
return 0;
case SOUND_PCM_READ_RATE:
return put_user(s->dac1rate, p);
case SOUND_PCM_READ_CHANNELS:
return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, p);
case SOUND_PCM_READ_BITS:
return put_user((s->sctrl & SCTRL_P1SEB) ? 16 : 8, p);
case SOUND_PCM_WRITE_FILTER:
case SNDCTL_DSP_SETSYNCRO:
case SOUND_PCM_READ_FILTER:
return -EINVAL;
}
return mixdev_ioctl(s->codec, cmd, arg);
}
static int es1371_open_dac(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
struct es1371_state *s;
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
s = list_entry(list, struct es1371_state, devs);
if (!((s->dev_dac ^ minor) & ~0xf))
break;
}
VALIDATE_STATE(s);
/* we allow opening with O_RDWR, most programs do it although they will only write */
#if 0
if (file->f_mode & FMODE_READ)
return -EPERM;
#endif
if (!(file->f_mode & FMODE_WRITE))
return -EINVAL;
file->private_data = s;
/* wait for device to become free */
mutex_lock(&s->open_mutex);
while (s->open_mode & FMODE_DAC) {
if (file->f_flags & O_NONBLOCK) {
mutex_unlock(&s->open_mutex);
return -EBUSY;
}
add_wait_queue(&s->open_wait, &wait);
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&s->open_mutex);
schedule();
remove_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
mutex_lock(&s->open_mutex);
}
s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0;
s->dma_dac1.enabled = 1;
set_dac1_rate(s, 8000);
spin_lock_irqsave(&s->lock, flags);
s->sctrl &= ~SCTRL_P1FMT;
if ((minor & 0xf) == SND_DEV_DSP16)
s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT;
else
s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT;
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= FMODE_DAC;
mutex_unlock(&s->open_mutex);
return nonseekable_open(inode, file);
}
static int es1371_release_dac(struct inode *inode, struct file *file)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
VALIDATE_STATE(s);
lock_kernel();
drain_dac1(s, file->f_flags & O_NONBLOCK);
mutex_lock(&s->open_mutex);
stop_dac1(s);
dealloc_dmabuf(s, &s->dma_dac1);
s->open_mode &= ~FMODE_DAC;
mutex_unlock(&s->open_mutex);
wake_up(&s->open_wait);
unlock_kernel();
return 0;
}
static /*const*/ struct file_operations es1371_dac_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = es1371_write_dac,
.poll = es1371_poll_dac,
.ioctl = es1371_ioctl_dac,
.mmap = es1371_mmap_dac,
.open = es1371_open_dac,
.release = es1371_release_dac,
};
/* --------------------------------------------------------------------- */
static ssize_t es1371_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
unsigned long flags;
unsigned ptr;
int cnt;
VALIDATE_STATE(s);
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
if (count == 0)
return 0;
ret = 0;
add_wait_queue(&s->midi.iwait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
ptr = s->midi.ird;
cnt = MIDIINBUF - ptr;
if (s->midi.icnt < cnt)
cnt = s->midi.icnt;
if (cnt <= 0)
__set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&s->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
break;
}
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
break;
}
continue;
}
if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) {
if (!ret)
ret = -EFAULT;
break;
}
ptr = (ptr + cnt) % MIDIINBUF;
spin_lock_irqsave(&s->lock, flags);
s->midi.ird = ptr;
s->midi.icnt -= cnt;
spin_unlock_irqrestore(&s->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&s->midi.iwait, &wait);
return ret;
}
static ssize_t es1371_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
unsigned long flags;
unsigned ptr;
int cnt;
VALIDATE_STATE(s);
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if (count == 0)
return 0;
ret = 0;
add_wait_queue(&s->midi.owait, &wait);
while (count > 0) {
spin_lock_irqsave(&s->lock, flags);
ptr = s->midi.owr;
cnt = MIDIOUTBUF - ptr;
if (s->midi.ocnt + cnt > MIDIOUTBUF)
cnt = MIDIOUTBUF - s->midi.ocnt;
if (cnt <= 0) {
__set_current_state(TASK_INTERRUPTIBLE);
es1371_handle_midi(s);
}
spin_unlock_irqrestore(&s->lock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (file->f_flags & O_NONBLOCK) {
if (!ret)
ret = -EAGAIN;
break;
}
schedule();
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
break;
}
continue;
}
if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) {
if (!ret)
ret = -EFAULT;
break;
}
ptr = (ptr + cnt) % MIDIOUTBUF;
spin_lock_irqsave(&s->lock, flags);
s->midi.owr = ptr;
s->midi.ocnt += cnt;
spin_unlock_irqrestore(&s->lock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
spin_lock_irqsave(&s->lock, flags);
es1371_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&s->midi.owait, &wait);
return ret;
}
/* No kernel lock - we have our own spinlock */
static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
unsigned long flags;
unsigned int mask = 0;
VALIDATE_STATE(s);
if (file->f_mode & FMODE_WRITE)
poll_wait(file, &s->midi.owait, wait);
if (file->f_mode & FMODE_READ)
poll_wait(file, &s->midi.iwait, wait);
spin_lock_irqsave(&s->lock, flags);
if (file->f_mode & FMODE_READ) {
if (s->midi.icnt > 0)
mask |= POLLIN | POLLRDNORM;
}
if (file->f_mode & FMODE_WRITE) {
if (s->midi.ocnt < MIDIOUTBUF)
mask |= POLLOUT | POLLWRNORM;
}
spin_unlock_irqrestore(&s->lock, flags);
return mask;
}
static int es1371_midi_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
struct list_head *list;
struct es1371_state *s;
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
s = list_entry(list, struct es1371_state, devs);
if (s->dev_midi == minor)
break;
}
VALIDATE_STATE(s);
file->private_data = s;
/* wait for device to become free */
mutex_lock(&s->open_mutex);
while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
if (file->f_flags & O_NONBLOCK) {
mutex_unlock(&s->open_mutex);
return -EBUSY;
}
add_wait_queue(&s->open_wait, &wait);
__set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&s->open_mutex);
schedule();
remove_wait_queue(&s->open_wait, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current))
return -ERESTARTSYS;
mutex_lock(&s->open_mutex);
}
spin_lock_irqsave(&s->lock, flags);
if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL);
outb(0, s->io+ES1371_REG_UART_CONTROL);
outb(0, s->io+ES1371_REG_UART_TEST);
}
if (file->f_mode & FMODE_READ) {
s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
}
if (file->f_mode & FMODE_WRITE) {
s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
}
s->ctrl |= CTRL_UART_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
es1371_handle_midi(s);
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
mutex_unlock(&s->open_mutex);
return nonseekable_open(inode, file);
}
static int es1371_midi_release(struct inode *inode, struct file *file)
{
struct es1371_state *s = (struct es1371_state *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
unsigned count, tmo;
VALIDATE_STATE(s);
lock_kernel();
if (file->f_mode & FMODE_WRITE) {
add_wait_queue(&s->midi.owait, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&s->lock, flags);
count = s->midi.ocnt;
spin_unlock_irqrestore(&s->lock, flags);
if (count <= 0)
break;
if (signal_pending(current))
break;
if (file->f_flags & O_NONBLOCK)
break;
tmo = (count * HZ) / 3100;
if (!schedule_timeout(tmo ? : 1) && tmo)
printk(KERN_DEBUG PFX "midi timed out??\n");
}
remove_wait_queue(&s->midi.owait, &wait);
set_current_state(TASK_RUNNING);
}
mutex_lock(&s->open_mutex);
s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
spin_lock_irqsave(&s->lock, flags);
if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
s->ctrl &= ~CTRL_UART_EN;
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
}
spin_unlock_irqrestore(&s->lock, flags);
mutex_unlock(&s->open_mutex);
wake_up(&s->open_wait);
unlock_kernel();
return 0;
}
static /*const*/ struct file_operations es1371_midi_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = es1371_midi_read,
.write = es1371_midi_write,
.poll = es1371_midi_poll,
.open = es1371_midi_open,
.release = es1371_midi_release,
};
/* --------------------------------------------------------------------- */
/*
* for debugging purposes, we'll create a proc device that dumps the
* CODEC chipstate
*/
#ifdef ES1371_DEBUG
static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data)
{
struct es1371_state *s;
int cnt, len = 0;
if (list_empty(&devs))
return 0;
s = list_entry(devs.next, struct es1371_state, devs);
/* print out header */
len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n");
/* print out CODEC state */
len += sprintf (buf + len, "AC97 CODEC state\n");
for (cnt=0; cnt <= 0x7e; cnt = cnt +2)
len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(s->codec, cnt));
if (fpos >=len){
*start = buf;
*eof =1;
return 0;
}
*start = buf + fpos;
if ((len -= fpos) > length)
return length;
*eof =1;
return len;
}
#endif /* ES1371_DEBUG */
/* --------------------------------------------------------------------- */
/* maximum number of devices; only used for command line params */
#define NR_DEVICE 5
static int spdif[NR_DEVICE];
static int nomix[NR_DEVICE];
static int amplifier[NR_DEVICE];
static unsigned int devindex;
module_param_array(spdif, bool, NULL, 0);
MODULE_PARM_DESC(spdif, "if 1 the output is in S/PDIF digital mode");
module_param_array(nomix, bool, NULL, 0);
MODULE_PARM_DESC(nomix, "if 1 no analog audio is mixed to the digital output");
module_param_array(amplifier, bool, NULL, 0);
MODULE_PARM_DESC(amplifier, "Set to 1 if the machine needs the amp control enabling (many laptops)");
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver");
MODULE_LICENSE("GPL");
/* --------------------------------------------------------------------- */
static struct initvol {
int mixch;
int vol;
} initvol[] __devinitdata = {
{ SOUND_MIXER_WRITE_LINE, 0x4040 },
{ SOUND_MIXER_WRITE_CD, 0x4040 },
{ MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 },
{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
{ SOUND_MIXER_WRITE_PCM, 0x4040 },
{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
{ MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 },
{ SOUND_MIXER_WRITE_OGAIN, 0x4040 },
{ MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 },
{ SOUND_MIXER_WRITE_SPEAKER, 0x4040 },
{ SOUND_MIXER_WRITE_MIC, 0x4040 },
{ SOUND_MIXER_WRITE_RECLEV, 0x4040 },
{ SOUND_MIXER_WRITE_IGAIN, 0x4040 }
};
static struct
{
short svid, sdid;
} amplifier_needed[] =
{
{ 0x107B, 0x2150 }, /* Gateway Solo 2150 */
{ 0x13BD, 0x100C }, /* Mebius PC-MJ100V */
{ 0x1102, 0x5938 }, /* Targa Xtender 300 */
{ 0x1102, 0x8938 }, /* IPC notebook */
{ PCI_ANY_ID, PCI_ANY_ID }
};
#ifdef SUPPORT_JOYSTICK
static int __devinit es1371_register_gameport(struct es1371_state *s)
{
struct gameport *gp;
int gpio;
for (gpio = 0x218; gpio >= 0x200; gpio -= 0x08)
if (request_region(gpio, JOY_EXTENT, "es1371"))
break;
if (gpio < 0x200) {
printk(KERN_ERR PFX "no free joystick address found\n");
return -EBUSY;
}
s->gameport = gp = gameport_allocate_port();
if (!gp) {
printk(KERN_ERR PFX "can not allocate memory for gameport\n");
release_region(gpio, JOY_EXTENT);
return -ENOMEM;
}
gameport_set_name(gp, "ESS1371 Gameport");
gameport_set_phys(gp, "isa%04x/gameport0", gpio);
gp->dev.parent = &s->dev->dev;
gp->io = gpio;
s->ctrl |= CTRL_JYSTK_EN | (((gpio >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT);
outl(s->ctrl, s->io + ES1371_REG_CONTROL);
gameport_register_port(gp);
return 0;
}
static inline void es1371_unregister_gameport(struct es1371_state *s)
{
if (s->gameport) {
int gpio = s->gameport->io;
gameport_unregister_port(s->gameport);
release_region(gpio, JOY_EXTENT);
}
}
#else
static inline int es1371_register_gameport(struct es1371_state *s) { return -ENOSYS; }
static inline void es1371_unregister_gameport(struct es1371_state *s) { }
#endif /* SUPPORT_JOYSTICK */
static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid)
{
struct es1371_state *s;
mm_segment_t fs;
int i, val, res = -1;
int idx;
unsigned long tmo;
signed long tmo2;
unsigned int cssr;
if ((res=pci_enable_device(pcidev)))
return res;
if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_IO))
return -ENODEV;
if (pcidev->irq == 0)
return -ENODEV;
i = pci_set_dma_mask(pcidev, DMA_32BIT_MASK);
if (i) {
printk(KERN_WARNING "es1371: architecture does not support 32bit PCI busmaster DMA\n");
return i;
}
if (!(s = kzalloc(sizeof(struct es1371_state), GFP_KERNEL))) {
printk(KERN_WARNING PFX "out of memory\n");
return -ENOMEM;
}
s->codec = ac97_alloc_codec();
if(s->codec == NULL)
goto err_codec;
init_waitqueue_head(&s->dma_adc.wait);
init_waitqueue_head(&s->dma_dac1.wait);
init_waitqueue_head(&s->dma_dac2.wait);
init_waitqueue_head(&s->open_wait);
init_waitqueue_head(&s->midi.iwait);
init_waitqueue_head(&s->midi.owait);
mutex_init(&s->open_mutex);
spin_lock_init(&s->lock);
s->magic = ES1371_MAGIC;
s->dev = pcidev;
s->io = pci_resource_start(pcidev, 0);
s->irq = pcidev->irq;
s->vendor = pcidev->vendor;
s->device = pcidev->device;
s->rev = pcidev->revision;
s->codec->private_data = s;
s->codec->id = 0;
s->codec->codec_read = rdcodec;
s->codec->codec_write = wrcodec;
printk(KERN_INFO PFX "found chip, vendor id 0x%04x device id 0x%04x revision 0x%02x\n",
s->vendor, s->device, s->rev);
if (!request_region(s->io, ES1371_EXTENT, "es1371")) {
printk(KERN_ERR PFX "io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1);
res = -EBUSY;
goto err_region;
}
if ((res=request_irq(s->irq, es1371_interrupt, IRQF_SHARED, "es1371",s))) {
printk(KERN_ERR PFX "irq %u in use\n", s->irq);
goto err_irq;
}
printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u\n",
s->rev, s->io, s->irq);
/* register devices */
if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1)))<0)
goto err_dev1;
if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0)
goto err_dev2;
if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1))) < 0)
goto err_dev3;
if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)))<0 )
goto err_dev4;
#ifdef ES1371_DEBUG
/* initialize the debug proc device */
s->ps = create_proc_read_entry("es1371",0,NULL,proc_es1371_dump,NULL);
#endif /* ES1371_DEBUG */
/* initialize codec registers */
s->ctrl = 0;
/* Check amplifier requirements */
if (amplifier[devindex])
s->ctrl |= CTRL_GPIO_OUT0;
else for(idx = 0; amplifier_needed[idx].svid != PCI_ANY_ID; idx++)
{
if(pcidev->subsystem_vendor == amplifier_needed[idx].svid &&
pcidev->subsystem_device == amplifier_needed[idx].sdid)
{
s->ctrl |= CTRL_GPIO_OUT0; /* turn internal amplifier on */
printk(KERN_INFO PFX "Enabling internal amplifier.\n");
}
}
s->sctrl = 0;
cssr = 0;
s->spdif_volume = -1;
/* check to see if s/pdif mode is being requested */
if (spdif[devindex]) {
if (s->rev >= 4) {
printk(KERN_INFO PFX "enabling S/PDIF output\n");
s->spdif_volume = 0;
cssr |= STAT_EN_SPDIF;
s->ctrl |= CTRL_SPDIFEN_B;
if (nomix[devindex]) /* don't mix analog inputs to s/pdif output */
s->ctrl |= CTRL_RECEN_B;
} else {
printk(KERN_ERR PFX "revision %d does not support S/PDIF\n", s->rev);
}
}
/* initialize the chips */
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
outl(LEGACY_JFAST, s->io+ES1371_REG_LEGACY);
pci_set_master(pcidev); /* enable bus mastering */
/* if we are a 5880 turn on the AC97 */
if (s->vendor == PCI_VENDOR_ID_ENSONIQ &&
((s->device == PCI_DEVICE_ID_ENSONIQ_CT5880 && s->rev >= CT5880REV_CT5880_C) ||
(s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_CT5880_A) ||
(s->device == PCI_DEVICE_ID_ENSONIQ_ES1371 && s->rev == ES1371REV_ES1373_8))) {
cssr |= CSTAT_5880_AC97_RST;
outl(cssr, s->io+ES1371_REG_STATUS);
/* need to delay around 20ms(bleech) to give
some CODECs enough time to wakeup */
tmo = jiffies + (HZ / 50) + 1;
for (;;) {
tmo2 = tmo - jiffies;
if (tmo2 <= 0)
break;
schedule_timeout(tmo2);
}
}
/* AC97 warm reset to start the bitclk */
outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL);
udelay(2);
outl(s->ctrl, s->io+ES1371_REG_CONTROL);
/* init the sample rate converter */
src_init(s);
/* codec init */
if (!ac97_probe_codec(s->codec)) {
res = -ENODEV;
goto err_gp;
}
/* set default values */
fs = get_fs();
set_fs(KERNEL_DS);
val = SOUND_MASK_LINE;
mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
for (i = 0; i < ARRAY_SIZE(initvol); i++) {
val = initvol[i].vol;
mixdev_ioctl(s->codec, initvol[i].mixch, (unsigned long)&val);
}
/* mute master and PCM when in S/PDIF mode */
if (s->spdif_volume != -1) {
val = 0x0000;
s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_VOLUME, (unsigned long)&val);
s->codec->mixer_ioctl(s->codec, SOUND_MIXER_WRITE_PCM, (unsigned long)&val);
}
set_fs(fs);
/* turn on S/PDIF output driver if requested */
outl(cssr, s->io+ES1371_REG_STATUS);
es1371_register_gameport(s);
/* store it in the driver field */
pci_set_drvdata(pcidev, s);
/* put it into driver list */
list_add_tail(&s->devs, &devs);
/* increment devindex */
if (devindex < NR_DEVICE-1)
devindex++;
return 0;
err_gp:
#ifdef ES1371_DEBUG
if (s->ps)
remove_proc_entry("es1371", NULL);
#endif
unregister_sound_midi(s->dev_midi);
err_dev4:
unregister_sound_dsp(s->dev_dac);
err_dev3:
unregister_sound_mixer(s->codec->dev_mixer);
err_dev2:
unregister_sound_dsp(s->dev_audio);
err_dev1:
printk(KERN_ERR PFX "cannot register misc device\n");
free_irq(s->irq, s);
err_irq:
release_region(s->io, ES1371_EXTENT);
err_region:
err_codec:
ac97_release_codec(s->codec);
kfree(s);
return res;
}
static void __devexit es1371_remove(struct pci_dev *dev)
{
struct es1371_state *s = pci_get_drvdata(dev);
if (!s)
return;
list_del(&s->devs);
#ifdef ES1371_DEBUG
if (s->ps)
remove_proc_entry("es1371", NULL);
#endif /* ES1371_DEBUG */
outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */
outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */
synchronize_irq(s->irq);
free_irq(s->irq, s);
es1371_unregister_gameport(s);
release_region(s->io, ES1371_EXTENT);
unregister_sound_dsp(s->dev_audio);
unregister_sound_mixer(s->codec->dev_mixer);
unregister_sound_dsp(s->dev_dac);
unregister_sound_midi(s->dev_midi);
ac97_release_codec(s->codec);
kfree(s);
pci_set_drvdata(dev, NULL);
}
static struct pci_device_id id_table[] = {
{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, id_table);
static struct pci_driver es1371_driver = {
.name = "es1371",
.id_table = id_table,
.probe = es1371_probe,
.remove = __devexit_p(es1371_remove),
};
static int __init init_es1371(void)
{
printk(KERN_INFO PFX "version v0.32 time " __TIME__ " " __DATE__ "\n");
return pci_register_driver(&es1371_driver);
}
static void __exit cleanup_es1371(void)
{
printk(KERN_INFO PFX "unloading\n");
pci_unregister_driver(&es1371_driver);
}
module_init(init_es1371);
module_exit(cleanup_es1371);
/* --------------------------------------------------------------------- */
#ifndef MODULE
/* format is: es1371=[spdif,[nomix,[amplifier]]] */
static int __init es1371_setup(char *str)
{
static unsigned __initdata nr_dev = 0;
if (nr_dev >= NR_DEVICE)
return 0;
(void)
((get_option(&str, &spdif[nr_dev]) == 2)
&& (get_option(&str, &nomix[nr_dev]) == 2)
&& (get_option(&str, &amplifier[nr_dev])));
nr_dev++;
return 1;
}
__setup("es1371=", es1371_setup);
#endif /* MODULE */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment