Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
3f250722
Commit
3f250722
authored
Sep 10, 2003
by
Benjamin Herrenschmidt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dmasound update from Christoph Hellwig
parent
875aeb35
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
4640 additions
and
305 deletions
+4640
-305
sound/oss/dmasound/Kconfig
sound/oss/dmasound/Kconfig
+2
-2
sound/oss/dmasound/Makefile
sound/oss/dmasound/Makefile
+9
-4
sound/oss/dmasound/awacs_defs.h
sound/oss/dmasound/awacs_defs.h
+28
-6
sound/oss/dmasound/dac3550a.c
sound/oss/dmasound/dac3550a.c
+222
-0
sound/oss/dmasound/dmasound_awacs.c
sound/oss/dmasound/dmasound_awacs.c
+354
-293
sound/oss/dmasound/tas3001c.c
sound/oss/dmasound/tas3001c.c
+861
-0
sound/oss/dmasound/tas3001c.h
sound/oss/dmasound/tas3001c.h
+64
-0
sound/oss/dmasound/tas3001c_tables.c
sound/oss/dmasound/tas3001c_tables.c
+375
-0
sound/oss/dmasound/tas3004.c
sound/oss/dmasound/tas3004.c
+1124
-0
sound/oss/dmasound/tas3004.h
sound/oss/dmasound/tas3004.h
+77
-0
sound/oss/dmasound/tas3004_tables.c
sound/oss/dmasound/tas3004_tables.c
+301
-0
sound/oss/dmasound/tas_common.c
sound/oss/dmasound/tas_common.c
+212
-0
sound/oss/dmasound/tas_common.h
sound/oss/dmasound/tas_common.h
+284
-0
sound/oss/dmasound/tas_eq_prefs.h
sound/oss/dmasound/tas_eq_prefs.h
+24
-0
sound/oss/dmasound/tas_ioctl.h
sound/oss/dmasound/tas_ioctl.h
+24
-0
sound/oss/dmasound/trans_16.c
sound/oss/dmasound/trans_16.c
+679
-0
No files found.
sound/oss/dmasound/Kconfig
View file @
3f250722
...
...
@@ -12,9 +12,9 @@ config DMASOUND_ATARI
want). If you want to compile it as a module, say M here and read
<file:Documentation/modules.txt>.
config DMASOUND_
AWACS
config DMASOUND_
PMAC
tristate "PowerMac DMA sound support"
depends on PPC_PMAC && SOUND
depends on PPC_PMAC && SOUND
&& I2C
help
If you want to use the internal audio of your PowerMac in Linux,
answer Y to this question. This will provide a Sun-like /dev/audio,
...
...
sound/oss/dmasound/Makefile
View file @
3f250722
...
...
@@ -2,7 +2,12 @@
# 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_
AWACS)
+=
dmasound_core.o dmasound_awacs
.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
sound/oss/dmasound/awacs_defs.h
View file @
3f250722
...
...
@@ -71,17 +71,20 @@ struct awacs_regs {
/* ------- - --- ----- - ------ */
#define MASK_GAINRIGHT (0xf)
/* Gain Right Mask */
#define MASK_GAINLEFT (0xf << 4)
/* Gain Left Mask */
#define MASK_GAINLINE (0x1 << 8)
/*
Change Gain for Line???
*/
#define MASK_GAINMIC (0x0 << 8)
/*
Change Gain for Mic???
*/
#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_
AUDIN (0x1 << 10)
/* Select Audio In
in MUX */
#define MASK_MUX_
MIC (0x1 << 11)
/* Select Mic
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 */
...
...
@@ -93,7 +96,10 @@ struct awacs_regs {
#define MASK_ADDR1RES2 (0x1 << 8)
/* Reserved */
#define MASK_AMUTE (0x1 << 9)
/* Output A (Headphone) Mute when 1 */
#define MASK_HDMUTE MASK_AMUTE
#define MASK_PAROUT (0x3 << 10)
/* Parallel Out (???) */
#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 */
...
...
@@ -162,8 +168,9 @@ struct awacs_regs {
#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)
...
...
@@ -226,4 +233,19 @@ struct awacs_regs {
#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_ */
sound/oss/dmasound/dac3550a.c
0 → 100644
View file @
3f250722
/*
* 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/version.h>
#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 <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
);
/* Unique ID allocation */
static
int
daca_id
;
struct
daca_data
{
int
arf
;
/* place holder for furture use */
};
struct
i2c_driver
daca_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"DAC3550A driver V "
DACA_VERSION
,
.
id
=
I2C_DRIVERID_DACA
,
.
flags
=
I2C_DF_NOTIFY
,
.
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
*/
wait_ms
(
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...
*/
wait_ms
(
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
;
struct
daca_data
*
data
;
int
rc
=
-
ENODEV
;
new_client
=
kmalloc
(
sizeof
(
*
new_client
)
+
sizeof
(
*
data
),
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
);
new_client
->
id
=
daca_id
++
;
/* racy... */
data
=
(
struct
daca_data
*
)(
new_client
+
1
);
dev_set_drvdata
(
&
new_client
->
dev
,
data
);
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
);
}
sound/oss/dmasound/dmasound_awacs.c
View file @
3f250722
...
...
@@ -45,7 +45,14 @@
* 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
*/
/* GENERAL FIXME/TODO: check that the assumptions about what is written to
mac-io is valid for DACA & Tumbler.
...
...
@@ -68,6 +75,7 @@
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/kmod.h>
#include <linux/interrupt.h>
#include <asm/semaphore.h>
#ifdef CONFIG_ADB_CUDA
#include <linux/cuda.h>
...
...
@@ -89,10 +97,14 @@
#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) */
...
...
@@ -103,11 +115,13 @@
*/
static
int
awacs_irq
,
awacs_tx_irq
,
awacs_rx_irq
;
static
volatile
struct
awacs_regs
*
awacs
;
static
volatile
u32
*
i2s
;
static
volatile
struct
dbdma_regs
*
awacs_txdma
,
*
awacs_rxdma
;
static
int
awacs_rate_index
;
static
int
awacs_subframe
;
static
int
awacs_spkr_vol
;
static
struct
device_node
*
awacs_node
;
static
struct
device_node
*
i2s_node
;
static
char
awacs_name
[
64
];
static
int
awacs_revision
;
...
...
@@ -164,6 +178,8 @@ 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
...
...
@@ -215,7 +231,6 @@ static int awacs_beep_state;
static
short
*
beep_buf
;
static
void
*
beep_dbdma_cmd_space
;
static
volatile
struct
dbdma_cmd
*
beep_dbdma_cmd
;
static
void
(
*
orig_mksound
)(
unsigned
int
,
unsigned
int
);
/* Burgundy functions */
static
void
awacs_burgundy_wcw
(
unsigned
addr
,
unsigned
newval
);
...
...
@@ -286,25 +301,12 @@ extern TRANS transAwacsExpand ;
extern
TRANS
transAwacsNormalRead
;
extern
int
daca_init
(
void
);
extern
int
daca_cleanup
(
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
);
extern
int
tas_init
(
void
);
extern
int
tas_cleanup
(
void
);
extern
int
tumbler_set_volume
(
uint
left_vol
,
uint
right_vol
);
extern
void
tumbler_get_volume
(
uint
*
left_vol
,
uint
*
right_vol
);
extern
void
tumbler_set_treble
(
int
treble
);
extern
void
tumbler_get_treble
(
int
*
treble
);
extern
void
tumbler_set_bass
(
int
bass
);
extern
void
tumbler_get_bass
(
int
*
bass
);
extern
void
tumbler_set_pcm_lvl
(
int
pcm_lvl
);
extern
void
tumbler_get_pcm_lvl
(
int
*
pcm_lvl
);
extern
int
tumbler_enter_sleep
(
void
);
extern
int
tumbler_leave_sleep
(
void
);
#define TRY_LOCK() \
if ((rc = down_interruptible(&dmasound_sem)) != 0) \
return rc;
...
...
@@ -331,7 +333,7 @@ static inline int ioctl_return2(int *addr, int value)
}
/*** AE - TUMBLER
START *********
************************************************/
/*** AE - TUMBLER
/ SNAPPER START
************************************************/
int
gpio_audio_reset
,
gpio_audio_reset_pol
;
...
...
@@ -393,31 +395,35 @@ read_audio_gpio(int gpio_addr)
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
,
struct
pt_regs
*
regs
)
{
int
handled
=
0
;
spin_lock
(
&
dmasound
.
lock
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
dmasound
.
lock
,
flags
);
if
(
read_audio_gpio
(
gpio_headphone_detect
)
==
gpio_headphone_detect_pol
)
{
handled
=
1
;
printk
(
KERN_INFO
"Audio jack plugged, muting speakers.
\n
"
);
write_audio_gpio
(
gpio_amp_mute
,
gpio_amp_mute_pol
);
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
{
handled
=
1
;
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
(
&
dmasound
.
lock
);
return
IRQ_
RETVAL
(
handled
)
;
spin_unlock
_irqrestore
(
&
dmasound
.
lock
,
flags
);
return
IRQ_
HANDLED
;
}
/* Initialize tumbler */
static
int
awacs_tumbler
_init
(
void
)
tas_dmasound
_init
(
void
)
{
setup_audio_gpio
(
"audio-hw-reset"
,
...
...
@@ -474,15 +480,124 @@ awacs_tumbler_init(void)
static
int
awacs_tumbler
_cleanup
(
void
)
tas_dmasound
_cleanup
(
void
)
{
if
(
gpio_headphone_irq
)
free_irq
(
gpio_headphone_irq
,
0
);
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
data
;
int
rc
;
/*** AE - TUMBLER END *********************************************************/
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
,
(
int
*
)(
arg
));
if
(
rc
<
0
)
return
rc
;
tas_set_mixer_level
(
cmd
&
0xff
,
data
);
tas_get_mixer_level
(
cmd
&
0xff
,
&
data
);
return
ioctl_return2
((
int
*
)(
arg
),
data
);
}
if
((
cmd
&
~
0xff
)
==
MIXER_READ
(
0
)
&&
tas_supported_mixers
()
&
(
1
<<
(
cmd
&
0xff
)))
{
tas_get_mixer_level
(
cmd
&
0xff
,
&
data
);
return
ioctl_return2
((
int
*
)(
arg
),
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
:
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_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
(
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 ************************************************/
...
...
@@ -503,8 +618,10 @@ static void PMacFree(void *ptr, unsigned int size)
static
int
__init
PMacIrqInit
(
void
)
{
if
(
request_irq
(
awacs_irq
,
pmac_awacs_intr
,
0
,
"Built-in Sound misc"
,
0
)
||
request_irq
(
awacs_tx_irq
,
pmac_awacs_tx_intr
,
0
,
"Built-in Sound out"
,
0
)
if
(
awacs
)
if
(
request_irq
(
awacs_irq
,
pmac_awacs_intr
,
0
,
"Built-in Sound misc"
,
0
))
return
0
;
if
(
request_irq
(
awacs_tx_irq
,
pmac_awacs_tx_intr
,
0
,
"Built-in Sound out"
,
0
)
||
request_irq
(
awacs_rx_irq
,
pmac_awacs_rx_intr
,
0
,
"Built-in Sound in"
,
0
))
return
0
;
return
1
;
...
...
@@ -517,23 +634,28 @@ static void PMacIrqCleanup(void)
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"
))
{
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
]);
wait_ms
(
200
);
}
if
(
awacs
)
free_irq
(
awacs_irq
,
0
);
free_irq
(
awacs_tx_irq
,
0
);
free_irq
(
awacs_rx_irq
,
0
);
/* all OF versions I've seen use this value */
if
(
awacs
)
iounmap
((
void
*
)
awacs
);
if
(
i2s
)
iounmap
((
void
*
)
i2s
);
iounmap
((
void
*
)
awacs_txdma
);
iounmap
((
void
*
)
awacs_rxdma
);
...
...
@@ -547,10 +669,8 @@ static void PMacIrqCleanup(void)
kfree
(
awacs_rx_cmd_space
);
if
(
beep_dbdma_cmd_space
)
kfree
(
beep_dbdma_cmd_space
);
if
(
beep_buf
)
{
if
(
beep_buf
)
kfree
(
beep_buf
);
kd_mksound
=
orig_mksound
;
}
#ifdef CONFIG_PMAC_PBOOK
pmu_unregister_sleep_notifier
(
&
awacs_sleep_notifier
);
#endif
...
...
@@ -563,26 +683,16 @@ static void PMacSilence(void)
DBDMA_DO_STOP
(
awacs_txdma
);
}
static
int
tumbler_freqs
[
2
]
=
{
48000
,
44100
}
;
static
int
tumbler_freqs_ok
[
2
]
=
{
1
,
1
}
;
/* don't know what to do really - just have to leave it where
* OF left things
*/
static
int
tumbler_set_frame_rate
(
void
)
{
dmasound
.
hard
.
speed
=
44100
;
awacs_rate_index
=
0
;
return
44100
;
}
/* 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
;
...
...
@@ -593,7 +703,8 @@ static int awacs_freqs[8] = {
};
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
)
static
int
awacs_set_frame_rate
(
int
desired
,
int
catch_r
)
{
int
tolerance
,
i
=
8
;
/*
...
...
@@ -617,13 +728,9 @@ static int awacs_set_frame_rate(int desired, int catch_r)
return
dmasound
.
hard
.
speed
;
}
static
int
burgundy_frame_rates
=
1
;
static
int
burgundy_set_frame_rate
(
void
)
static
int
burgundy_set_frame_rate
(
void
)
{
#ifdef DEBUG_DMASOUND
if
(
burgundy_frame_rates
>
1
)
printk
(
"dmasound_pmac: warning Burgundy had more than one frame rate
\n
"
);
#endif
awacs_rate_index
=
0
;
awacs_reg
[
1
]
=
(
awacs_reg
[
1
]
&
~
MASK_SAMPLERATE
)
;
/* XXX disable error interrupt on burgundy for now */
...
...
@@ -631,24 +738,24 @@ if (burgundy_frame_rates > 1)
return
44100
;
}
static
int
set_frame_rate
(
int
desired
,
int
catch_r
)
static
int
set_frame_rate
(
int
desired
,
int
catch_r
)
{
switch
(
awacs_revision
)
{
case
AWACS_BURGUNDY
:
dmasound
.
hard
.
speed
=
burgundy_set_frame_rate
();
dmasound
.
hard
.
speed
=
burgundy_set_frame_rate
();
break
;
case
AWACS_TUMBLER
:
dmasound
.
hard
.
speed
=
tumbler
_set_frame_rate
();
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
);
dmasound
.
hard
.
speed
=
awacs_set_frame_rate
(
desired
,
catch_r
);
break
;
}
return
dmasound
.
hard
.
speed
;
...
...
@@ -698,10 +805,12 @@ static void PMacInit(void)
dmasound
.
trans_write
=
&
transAwacsExpand
;
dmasound
.
trans_read
=
&
transAwacsNormalRead
;
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
;
}
...
...
@@ -787,18 +896,28 @@ static int awacs_volume_setter(int volume, int n, int mute, int lshift)
static
int
PMacSetVolume
(
int
volume
)
{
return
awacs_volume_setter
(
volume
,
2
,
MASK_AMUTE
,
6
);
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
;
unsigned
long
flags
;
/* CHECK: how much of this *really* needs IRQs masked? */
spin_lock_irqsave
(
&
dmasound
.
lock
,
flags
);
count
=
300
;
/* > two cycles at the lowest sample rate */
/* what we want to send next */
...
...
@@ -810,15 +929,8 @@ static void __PMacPlay(void)
out_le32
(
&
awacs_txdma
->
control
,
(
RUN
|
PAUSE
|
FLUSH
|
WAKE
)
<<
16
);
while
(
(
in_le32
(
&
awacs_txdma
->
status
)
&
RUN
)
&&
count
--
)
udelay
(
1
);
/* FIXME: check that this is OK for other chip sets */
out_le32
(
&
awacs
->
control
,
(
in_le32
(
&
awacs
->
control
)
&
~
0x1f00
)
|
(
awacs_rate_index
<<
8
));
if
(
hw_can_byteswap
&&
(
dmasound
.
hard
.
format
==
AFMT_S16_LE
))
out_le32
(
&
awacs
->
byteswap
,
BS_VAL
);
else
out_le32
(
&
awacs
->
byteswap
,
0
);
if
(
awacs
)
awacs_setup_for_beep
(
-
1
);
out_le32
(
&
awacs_txdma
->
cmdptr
,
virt_to_bus
(
&
(
awacs_tx_cmds
[
next_frg
])));
...
...
@@ -865,14 +977,18 @@ static void __PMacPlay(void)
out_le32
(
&
awacs_txdma
->
control
,
((
RUN
|
WAKE
)
<<
16
)
+
(
RUN
|
WAKE
));
++
write_sq
.
active
;
}
spin_unlock_irqrestore
(
&
dmasound
.
lock
,
flags
);
}
static
void
PMacPlay
(
void
)
{
LOCK
();
if
(
!
awacs_sleeping
)
if
(
!
awacs_sleeping
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
dmasound
.
lock
,
flags
);
__PMacPlay
();
spin_unlock_irqrestore
(
&
dmasound
.
lock
,
flags
);
}
UNLOCK
();
}
...
...
@@ -919,6 +1035,7 @@ pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
{
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
;
...
...
@@ -976,6 +1093,7 @@ printk("dmasound_pmac: tx-irq: xfer died - patching it up...\n") ;
emergency_in_use
=
0
;
/* done that */
--
write_sq
.
count
;
--
write_sq
.
active
;
i_nowrap
++
;
if
(
++
i
>=
write_sq
.
max_count
)
i
=
0
;
}
...
...
@@ -988,7 +1106,7 @@ printk("dmasound_pmac: tx-irq: xfer died - patching it up...\n") ;
}
/* if we used some data up then wake the writer to supply some more*/
if
(
i
!=
write_sq
.
front
)
if
(
i
_nowrap
!=
write_sq
.
front
)
WAKE_UP
(
write_sq
.
action_queue
);
write_sq
.
front
=
i
;
...
...
@@ -1091,11 +1209,30 @@ static irqreturn_t
pmac_awacs_intr
(
int
irq
,
void
*
devid
,
struct
pt_regs
*
regs
)
{
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
)
{
/* do something when headphone is plugged/unplugged? */
/* 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
;
...
...
@@ -1113,7 +1250,7 @@ static void
awacs_write
(
int
val
)
{
int
count
=
300
;
if
(
awacs_revision
>=
AWACS_DACA
)
if
(
awacs_revision
>=
AWACS_DACA
||
!
awacs
)
return
;
while
((
in_le32
(
&
awacs
->
codec_ctrl
)
&
MASK_NEWECMD
)
&&
count
--
)
...
...
@@ -1136,22 +1273,16 @@ static void awacs_nosound(unsigned long xx)
out_le32
(
&
awacs_txdma
->
control
,
(
RUN
|
PAUSE
|
FLUSH
|
WAKE
)
<<
16
);
while
((
in_le32
(
&
awacs_txdma
->
status
)
&
RUN
)
&&
count
--
)
udelay
(
1
);
/* FIXME: check this is OK for DACA, Tumbler */
out_le32
(
&
awacs
->
control
,
(
in_le32
(
&
awacs
->
control
)
&
~
0x1f00
)
|
(
awacs_rate_index
<<
8
));
if
(
hw_can_byteswap
&&
(
dmasound
.
hard
.
format
==
AFMT_S16_LE
))
out_le32
(
&
awacs
->
byteswap
,
BS_VAL
);
else
out_le32
(
&
awacs
->
byteswap
,
0
);
if
(
awacs
)
awacs_setup_for_beep
(
-
1
);
beep_playing
=
0
;
}
spin_unlock_irqrestore
(
&
dmasound
.
lock
,
flags
);
}
static
struct
timer_list
beep_timer
=
TIMER_INITIALIZER
(
awacs_nosound
,
0
,
0
);
}
;
#if 0 /* would need to go through the input layer in 2.6, later.. --hch */
/* 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.
...
...
@@ -1237,17 +1368,15 @@ static void awacs_mksound(unsigned int hz, unsigned int ticks)
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*/
/* FIXME: check this is OK on DACA, Tumbler */
out_le32
(
&
awacs
->
control
,
(
in_le32
(
&
awacs
->
control
)
&
~
0x1f00
)
|
(
beep_speed
<<
8
));
out_le32
(
&
awacs
->
byteswap
,
0
);
/* force BE */
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
);
}
#endif
/* used in init and for wake-up */
...
...
@@ -1267,10 +1396,12 @@ load_awacs(void)
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_PMAC_PBOOK
...
...
@@ -1280,6 +1411,8 @@ load_awacs(void)
/* FIXME: sort out disabling/re-enabling of read stuff as well */
static
int
awacs_sleep_notify
(
struct
pmu_sleep_notifier
*
self
,
int
when
)
{
unsigned
long
flags
;
switch
(
when
)
{
case
PBOOK_SLEEP_NOW
:
LOCK
();
...
...
@@ -1297,9 +1430,18 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
/* 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
:
tumbler_enter_sleep
();
/* Stub for now */
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
();
...
...
@@ -1312,17 +1454,14 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
out_le32
(
&
awacs
->
control
,
0x11
)
;
break
;
}
disable_irq
(
awacs_irq
);
disable_irq
(
awacs_tx_irq
);
disable_irq
(
awacs_rx_irq
);
/* 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"
))
{
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
]);
wait_ms
(
200
);
...
...
@@ -1331,8 +1470,8 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
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"
))
{
if
(
(
machine_is_compatible
(
"PowerBook3,1"
)
||
machine_is_compatible
(
"PowerBook3,2"
))
&&
awacs
)
{
wait_ms
(
100
);
awacs_reg
[
1
]
&=
~
(
MASK_PAROUT0
|
MASK_PAROUT1
);
awacs_write
(
MASK_ADDR1
|
awacs_reg
[
1
]);
...
...
@@ -1342,13 +1481,20 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
/* 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
);
wait_ms
(
100
);
write_audio_gpio
(
gpio_audio_reset
,
!
gpio_audio_reset_pol
);
wait_ms
(
150
);
tas_leave_sleep
();
/* Stub for now */
headphone_intr
(
0
,
0
,
0
);
tumbler_leave_sleep
();
/* Stub for now */
break
;
case
AWACS_DACA
:
wait_ms
(
10
);
/* Check this !!! */
daca_leave_sleep
();
break
;
/* don
'
t know how yet */
break
;
/* dont know how yet */
case
AWACS_BURGUNDY
:
break
;
case
AWACS_SCREAMER
:
...
...
@@ -1358,17 +1504,20 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
break
;
}
/* Recalibrate chip */
if
(
awacs_revision
==
AWACS_SCREAMER
)
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
);
...
...
@@ -1384,7 +1533,9 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
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
();
}
return
PBOOK_SLEEP_OK
;
...
...
@@ -1956,7 +2107,7 @@ static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
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
);
rc
=
IOCTL_OUT
(
arg
,
(
~
data
)
&
0x0000ffff
);
break
;
case
SOUND_MIXER_WRITE_ALTPCM
:
/* really bell volume */
IOCTL_IN
(
arg
,
data
);
...
...
@@ -2011,89 +2162,6 @@ static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
return
rc
;
}
static
int
tumbler_mixer_ioctl
(
u_int
cmd
,
u_long
arg
)
{
int
data
;
int
rc
;
/* We are, we are, we are... Tumbler (and very dumb) */
/* Ok, we're not THAT dumb anymore, but still pretty dumb :-) */
switch
(
cmd
)
{
case
SOUND_MIXER_READ_DEVMASK
:
data
=
SOUND_MASK_VOLUME
|
SOUND_MASK_ALTPCM
|
SOUND_MASK_BASS
|
SOUND_MASK_TREBLE
|
SOUND_MASK_PCM
;
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
|
SOUND_MASK_PCM
;
rc
=
IOCTL_OUT
(
arg
,
data
);
break
;
case
SOUND_MIXER_READ_CAPS
:
rc
=
IOCTL_OUT
(
arg
,
0
);
break
;
case
SOUND_MIXER_WRITE_BASS
:
IOCTL_IN
(
arg
,
data
);
tumbler_set_bass
(
data
);
/* Fall through */
case
SOUND_MIXER_READ_BASS
:
tumbler_get_bass
(
&
data
);
rc
=
IOCTL_OUT
(
arg
,
data
);
break
;
case
SOUND_MIXER_WRITE_TREBLE
:
IOCTL_IN
(
arg
,
data
);
tumbler_set_treble
(
data
);
/* Fall through */
case
SOUND_MIXER_READ_TREBLE
:
tumbler_get_treble
(
&
data
);
rc
=
IOCTL_OUT
(
arg
,
data
);
break
;
case
SOUND_MIXER_WRITE_PCM
:
IOCTL_IN
(
arg
,
data
);
tumbler_set_pcm_lvl
(
data
);
/* Fall through */
case
SOUND_MIXER_READ_PCM
:
tumbler_get_pcm_lvl
(
&
data
);
IOCTL_OUT
(
arg
,
data
);
break
;
case
SOUND_MIXER_WRITE_VOLUME
:
IOCTL_IN
(
arg
,
data
);
tumbler_set_volume
(
data
,
data
);
/* Fall through */
case
SOUND_MIXER_READ_VOLUME
:
tumbler_get_volume
(
&
data
,
&
data
);
rc
=
IOCTL_OUT
(
arg
,
data
);
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_OUTMASK
:
case
SOUND_MIXER_OUTSRC
:
default:
rc
=
-
EINVAL
;
}
return
rc
;
}
static
int
daca_mixer_ioctl
(
u_int
cmd
,
u_long
arg
)
{
int
data
;
...
...
@@ -2158,7 +2226,8 @@ static int PMacMixerIoctl(u_int cmd, u_long arg)
rc
=
daca_mixer_ioctl
(
cmd
,
arg
);
break
;
case
AWACS_TUMBLER
:
rc
=
tumbler_mixer_ioctl
(
cmd
,
arg
);
case
AWACS_SNAPPER
:
rc
=
tas_mixer_ioctl
(
cmd
,
arg
);
break
;
default:
/* ;-)) */
rc
=
awacs_mixer_ioctl
(
cmd
,
arg
);
...
...
@@ -2175,7 +2244,9 @@ static void PMacMixerInit(void)
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 */
...
...
@@ -2366,12 +2437,12 @@ static int PMacStateInfo(char *b, size_t sp)
len
+=
sprintf
(
b
,
"44100 "
)
;
break
;
case
AWACS_TUMBLER
:
for
(
i
=
0
;
i
<
2
;
i
++
){
if
(
tumbler_freqs_ok
[
i
])
len
+=
sprintf
(
b
+
len
,
"%d "
,
tumbler_freqs
[
i
])
;
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:
...
...
@@ -2472,8 +2543,8 @@ set_model(void)
code that looks for chip properties knows how to go about it.
*/
static
struct
device_node
__init
*
get_snd_io_node
(
void
)
static
struct
device_node
*
__init
get_snd_io_node
(
void
)
{
struct
device_node
*
np
=
NULL
;
...
...
@@ -2494,7 +2565,7 @@ __init *get_snd_io_node(void)
* this seems to be what iBooks (& Tumbler) have.
*/
if
(
np
==
NULL
)
np
=
find_devices
(
"i2s-a"
);
np
=
i2s_node
=
find_devices
(
"i2s-a"
);
/* if we didn't find this - perhaps we are on an early model
* which _only_ has an 'awacs' node
...
...
@@ -2514,23 +2585,22 @@ __init *get_snd_io_node(void)
we have to deduce the info other ways for these.
*/
static
struct
device_node
__init
*
get_snd_info_node
(
struct
device_node
*
io
)
static
struct
device_node
*
__init
get_snd_info_node
(
struct
device_node
*
io
)
{
struct
device_node
*
info
;
info
=
find_devices
(
"sound"
);
while
(
info
!=
0
&&
info
->
parent
!=
io
)
while
(
info
&&
info
->
parent
!=
io
)
info
=
info
->
next
;
return
info
;
return
info
;
}
/* Find out what type of codec we have.
*/
static
int
__init
get_codec_type
(
struct
device_node
*
info
)
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
;
...
...
@@ -2547,14 +2617,16 @@ __init get_codec_type(struct device_node *info)
codec
=
AWACS_DACA
;
if
(
device_is_compatible
(
info
,
"tumbler"
))
codec
=
AWACS_TUMBLER
;
if
(
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
)
static
void
__init
get_expansion_type
(
void
)
{
if
(
find_devices
(
"perch"
)
!=
NULL
)
has_perch
=
1
;
...
...
@@ -2572,8 +2644,8 @@ __init get_expansion_type(void)
* Set dmasound.mach.max_dsp_rate on the basis of these routines.
*/
static
void
__init
init_awacs
_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
static
void
__init
awacs_init
_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
{
int
i
;
if
(
prop
)
{
...
...
@@ -2595,31 +2667,8 @@ __init init_awacs_frame_rates(unsigned int *prop, unsigned int l)
/* else we assume that all the rates are available */
}
static
void
__init
init_tumbler_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
{
int
i
;
if
(
prop
)
{
for
(
i
=
0
;
i
<
2
;
i
++
)
tumbler_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
<
2
;
++
i
)
{
if
(
r
==
tumbler_freqs
[
i
])
{
tumbler_freqs_ok
[
i
]
=
1
;
break
;
}
}
}
}
/* else we assume that all the rates are available */
}
static
void
__init
init_burgundy_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
static
void
__init
burgundy_init_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
{
int
temp
[
9
]
;
int
i
=
0
;
...
...
@@ -2644,8 +2693,8 @@ if (i > 1){
#endif
}
static
void
__init
init_daca
_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
static
void
__init
daca_init
_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
{
int
temp
[
9
]
;
int
i
=
0
;
...
...
@@ -2671,21 +2720,22 @@ if (i > 1){
#endif
}
static
void
__init
init_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
static
void
__init
init_frame_rates
(
unsigned
int
*
prop
,
unsigned
int
l
)
{
switch
(
awacs_revision
){
switch
(
awacs_revision
)
{
case
AWACS_TUMBLER
:
init_tumbler_frame_rates
(
prop
,
l
);
case
AWACS_SNAPPER
:
tas_init_frame_rates
(
prop
,
l
);
break
;
case
AWACS_DACA
:
init_daca
_frame_rates
(
prop
,
l
);
daca_init
_frame_rates
(
prop
,
l
);
break
;
case
AWACS_BURGUNDY
:
init_burgundy
_frame_rates
(
prop
,
l
);
burgundy_init
_frame_rates
(
prop
,
l
);
break
;
default:
/* ;-))) */
init_awacs
_frame_rates
(
prop
,
l
);
default:
awacs_init
_frame_rates
(
prop
,
l
);
break
;
}
}
...
...
@@ -2693,11 +2743,11 @@ __init init_frame_rates(unsigned int *prop, unsigned int l)
/* find things/machines that can't do mac-io byteswap
*/
static
void
__init
set_hw_byteswap
(
struct
device_node
*
io
)
static
void
__init
set_hw_byteswap
(
struct
device_node
*
io
)
{
struct
device_node
*
mio
;
unsigned
int
*
p
,
kl
=
0
;
unsigned
int
kl
=
0
;
/* if seems that Keylargo can't byte-swap */
...
...
@@ -2744,9 +2794,6 @@ __init setup_beep(void)
if
(
beep_dbdma_cmd_space
)
kfree
(
beep_dbdma_cmd_space
)
;
return
-
ENOMEM
;
}
/* OK, we should be safe to claim the mksound vector now */
orig_mksound
=
kd_mksound
;
kd_mksound
=
awacs_mksound
;
return
0
;
}
...
...
@@ -2842,6 +2889,9 @@ printk("dmasound_pmac: couldn't find a Codec we can handle\n");
}
/* all OF versions I've seen use this value */
if
(
i2s_node
)
i2s
=
(
u32
*
)
ioremap
(
io
->
addrs
[
0
].
address
,
0x1000
);
else
awacs
=
(
volatile
struct
awacs_regs
*
)
ioremap
(
io
->
addrs
[
0
].
address
,
0x1000
);
awacs_txdma
=
(
volatile
struct
dbdma_regs
*
)
...
...
@@ -2849,16 +2899,16 @@ printk("dmasound_pmac: couldn't find a Codec we can handle\n");
awacs_rxdma
=
(
volatile
struct
dbdma_regs
*
)
ioremap
(
io
->
addrs
[
2
].
address
,
0x100
);
#ifdef CONFIG_PMAC_PBOOK
/* 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
)
if
(
awacs_revision
==
AWACS_SCREAMER
&&
awacs
)
awacs_recalibrate
();
#endif
awacs_irq
=
io
->
intrs
[
0
].
line
;
awacs_tx_irq
=
io
->
intrs
[
1
].
line
;
awacs_rx_irq
=
io
->
intrs
[
2
].
line
;
/* Hack for legacy crap that will be killed someday */
awacs_node
=
io
;
/* if we have an awacs or screamer - probe the chip to make
...
...
@@ -2910,6 +2960,7 @@ printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
init_frame_rates
(
prop
,
l
)
;
}
if
(
awacs
)
out_le32
(
&
awacs
->
control
,
0x11
);
/* set everything quiesent */
set_hw_byteswap
(
io
)
;
/* figure out if the h/w can do it */
...
...
@@ -2942,25 +2993,27 @@ printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
switch
(
awacs_revision
)
{
case
AWACS_TUMBLER
:
#ifdef CONFIG_KMOD
request_module
(
"i2c-keywest"
);
#endif
/* CONFIG_KMOD */
awacs_tumbler_init
();
tas_init
();
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
:
#ifdef CONFIG_KMOD
request_module
(
"i2c-keywest"
);
#endif
/* CONFIG_KMOD */
daca_init
();
break
;
/* don't know how yet */
break
;
case
AWACS_BURGUNDY
:
awacs_burgundy_init
();
break
;
case
AWACS_SCREAMER
:
case
AWACS_AWACS
:
default:
load_awacs
()
;
load_awacs
();
break
;
}
...
...
@@ -3031,11 +3084,15 @@ printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
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.
* may need to extend this to machines which have no inputs - even tho'
* they use screamer - IIRC one of the powerbooks is like this.
*
* FIXME: Actually, some TUMBLER and SNAPPER do have inputs...
*/
if
(
awacs_revision
!=
AWACS_TUMBLER
&&
awacs_revision
!=
AWACS_DACA
)
{
if
(
awacs_revision
!=
AWACS_TUMBLER
&&
awacs_revision
!=
AWACS_SNAPPER
&&
awacs_revision
!=
AWACS_DACA
)
{
dmasound
.
mach
.
capabilities
=
DSP_CAP_DUPLEX
;
dmasound
.
mach
.
record
=
PMacRecord
;
}
...
...
@@ -3053,6 +3110,9 @@ printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
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
;
...
...
@@ -3069,7 +3129,8 @@ static void __exit dmasound_awacs_cleanup(void)
{
switch
(
awacs_revision
)
{
case
AWACS_TUMBLER
:
awacs_tumbler_cleanup
();
case
AWACS_SNAPPER
:
tas_dmasound_cleanup
();
tas_cleanup
();
break
;
case
AWACS_DACA
:
...
...
sound/oss/dmasound/tas3001c.c
0 → 100644
View file @
3f250722
/*
* 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/version.h>
#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
;
};
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
;
if
(
copy_from_user
((
void
*
)
&
biquad
,
(
const
void
*
)
arg
,
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
((
void
*
)
arg
,
(
const
void
*
)
&
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
;
memset
(
sync_required
,
0
,
sizeof
(
sync_required
));
if
(
copy_from_user
((
void
*
)
&
filter_count
,
(
const
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
filter_count
),
sizeof
(
int
)))
{
return
-
EFAULT
;
}
if
(
copy_from_user
((
void
*
)
&
flags
,
(
const
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
flags
),
sizeof
(
int
)))
{
return
-
EFAULT
;
}
if
(
cmd
&
SIOC_IN
)
{
}
for
(
i
=
0
;
i
<
filter_count
;
i
++
)
{
if
(
copy_from_user
((
void
*
)
&
biquad
,
(
const
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
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
((
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
biquads
[
i
]),
(
const
void
*
)
&
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
;
if
(
copy_from_user
((
void
*
)
&
drce_ctrl
,
(
const
void
*
)
arg
,
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
((
void
*
)
arg
,
(
const
void
*
)
&
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
(
void
*
self
)
{
if
(
self
)
tas3001c_update_device_parameters
(
self
);
}
static
struct
work_struct
device_change
;
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
(
&
device_change
);
return
0
;
}
static
int
tas3001c_device_ioctl
(
struct
tas3001c_data_t
*
self
,
u_int
cmd
,
u_long
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
,
(
uint
*
)(
arg
));
return
0
;
case
TAS_READ_EQ_CHANNEL_COUNT
:
put_user
(
TAS3001C_BIQUAD_CHANNEL_COUNT
,
(
uint
*
)(
arg
));
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
,
(
uint
*
)(
arg
));
return
0
;
case
TAS_READ_DRCE_MIN
:
case
TAS_READ_DRCE_MAX
:
{
struct
tas_drce_ctrl_t
drce_ctrl
;
if
(
copy_from_user
((
void
*
)
&
drce_ctrl
,
(
const
void
*
)
arg
,
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
((
void
*
)
arg
,
(
const
void
*
)
&
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
=
kmalloc
(
sz
,
GFP_KERNEL
);
if
(
!
self
)
return
-
ENOMEM
;
memset
(
self
,
0
,
sz
);
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
(
&
device_change
,
tas3001c_device_change_handler
,
self
);
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
};
sound/oss/dmasound/tas3001c.h
0 → 100644
View file @
3f250722
/*
* 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_ */
sound/oss/dmasound/tas3001c_tables.c
0 → 100644
View file @
3f250722
#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
};
sound/oss/dmasound/tas3004.c
0 → 100644
View file @
3f250722
/*
* 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)
*
*/
#include <linux/version.h>
#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
;
};
#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
)
{
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
)
{
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
;
}
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
;
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
);
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
)
{
int
rc
;
struct
tas_biquad_ctrl_t
biquad
;
if
(
copy_from_user
((
void
*
)
&
biquad
,
(
const
void
*
)
arg
,
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
((
void
*
)
arg
,
(
const
void
*
)
&
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
;
memset
(
sync_required
,
0
,
sizeof
(
sync_required
));
if
(
copy_from_user
((
void
*
)
&
filter_count
,
(
const
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
filter_count
),
sizeof
(
int
)))
{
return
-
EFAULT
;
}
if
(
copy_from_user
((
void
*
)
&
flags
,
(
const
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
flags
),
sizeof
(
int
)))
{
return
-
EFAULT
;
}
if
(
cmd
&
SIOC_IN
)
{
}
for
(
i
=
0
;
i
<
filter_count
;
i
++
)
{
if
(
copy_from_user
((
void
*
)
&
biquad
,
(
const
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
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
((
void
*
)
arg
+
offsetof
(
struct
tas_biquad_ctrl_list_t
,
biquads
[
i
]),
(
const
void
*
)
&
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
;
if
(
copy_from_user
((
void
*
)
&
drce_ctrl
,
(
const
void
*
)
arg
,
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
((
void
*
)
arg
,
(
const
void
*
)
&
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
(
void
*
self
)
{
if
(
!
self
)
return
;
tas3004_update_device_parameters
((
struct
tas3004_data_t
*
)
self
);
}
static
struct
work_struct
device_change
;
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
(
&
device_change
);
return
0
;
}
static
int
tas3004_device_ioctl
(
struct
tas3004_data_t
*
self
,
u_int
cmd
,
u_long
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
,
(
uint
*
)(
arg
));
return
0
;
case
TAS_READ_EQ_CHANNEL_COUNT
:
put_user
(
TAS3004_BIQUAD_CHANNEL_COUNT
,
(
uint
*
)(
arg
));
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
,
(
uint
*
)(
arg
));
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
((
void
*
)
&
drce_ctrl
,
(
const
void
*
)
arg
,
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
((
void
*
)
arg
,
(
const
void
*
)
&
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
);
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
);
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
=
kmalloc
(
sz
,
GFP_KERNEL
);
if
(
!
self
)
return
-
ENOMEM
;
memset
(
self
,
0
,
sz
);
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
(
&
device_change
,
tas3004_device_change_handler
,
self
);
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
};
sound/oss/dmasound/tas3004.h
0 → 100644
View file @
3f250722
/*
* 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_ */
sound/oss/dmasound/tas3004_tables.c
0 → 100644
View file @
3f250722
#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
};
sound/oss/dmasound/tas_common.c
0 → 100644
View file @
3f250722
#include <linux/version.h>
#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
struct
device_node
*
tas_node
;
static
int
tas_attach_adapter
(
struct
i2c_adapter
*
);
static
int
tas_detach_client
(
struct
i2c_client
*
);
struct
i2c_driver
tas_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
"tas"
,
.
flags
=
I2C_DF_NOTIFY
,
.
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
=
kmalloc
(
sizeof
(
*
new_client
),
GFP_KERNEL
);
if
(
!
new_client
)
return
-
ENOMEM
;
memset
(
new_client
,
0
,
sizeof
(
*
new_client
));
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
)
{
u32
*
paddr
;
printk
(
KERN_INFO
"tas driver [%s])
\n
"
,
driver_name
);
tas_node
=
find_devices
(
"deq"
);
if
(
tas_node
==
NULL
)
return
-
ENODEV
;
paddr
=
(
u32
*
)
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
);
return
i2c_add_driver
(
&
tas_driver
);
}
sound/oss/dmasound/tas_common.h
0 → 100644
View file @
3f250722
#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
[
16
];
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_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_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:
*/
sound/oss/dmasound/tas_eq_prefs.h
0 → 100644
View file @
3f250722
#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:
*/
sound/oss/dmasound/tas_ioctl.h
0 → 100644
View file @
3f250722
#ifndef _TAS_IOCTL_H_
#define _TAS_IOCTL_H_
#include <linux/i2c.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
sound/oss/dmasound/trans_16.c
0 → 100644
View file @
3f250722
/*
* linux/drivers/sound/dmasound/trans_16.c
*
* 16 bit translation routines. Only used by Power mac at present.
*
* See linux/drivers/sound/dmasound/dmasound_core.c for copyright and
* history prior to 08/02/2001.
*
* 08/02/2001 Iain Sandoe
* split from dmasound_awacs.c
*/
#include <linux/soundcard.h>
#include <asm/uaccess.h>
#include "dmasound.h"
static
short
dmasound_alaw2dma16
[]
;
static
short
dmasound_ulaw2dma16
[]
;
static
ssize_t
pmac_ct_law
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ct_s8
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ct_u8
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ct_s16
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ct_u16
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ctx_law
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ctx_s8
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ctx_u8
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ctx_s16
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ctx_u16
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ct_s16_read
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
static
ssize_t
pmac_ct_u16_read
(
const
u_char
*
userPtr
,
size_t
userCount
,
u_char
frame
[],
ssize_t
*
frameUsed
,
ssize_t
frameLeft
);
/*** Translations ************************************************************/
extern
int
expand_bal
;
/* Balance factor for expanding (not volume!) */
static
int
expand_data
;
/* Data for expanding */
static
ssize_t
pmac_ct_law
(
const
u_char
*
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
*
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
*
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
*
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
*
up
=
(
short
*
)
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
*
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
*
up
=
(
short
*
)
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
*
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
*
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
*
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
*
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
*
up
=
(
unsigned
short
*
)
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
*
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
*
up
=
(
unsigned
short
*
)
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
*
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
++
;
data
=
val
>>
8
;
if
(
put_user
(
data
,
(
u_char
*
)
userPtr
++
))
return
-
EFAULT
;
if
(
stereo
)
{
val
=
*
p
;
data
=
val
>>
8
;
if
(
put_user
(
data
,
(
u_char
*
)
userPtr
++
))
return
-
EFAULT
;
}
p
++
;
count
--
;
}
*
frameUsed
+=
used
*
4
;
return
stereo
?
used
*
2
:
used
;
}
static
ssize_t
pmac_ct_u8_read
(
const
u_char
*
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
++
;
data
=
(
val
>>
8
)
^
0x80
;
if
(
put_user
(
data
,
(
u_char
*
)
userPtr
++
))
return
-
EFAULT
;
if
(
stereo
)
{
val
=
*
p
;
data
=
(
val
>>
8
)
^
0x80
;
if
(
put_user
(
data
,
(
u_char
*
)
userPtr
++
))
return
-
EFAULT
;
}
p
++
;
count
--
;
}
*
frameUsed
+=
used
*
4
;
return
stereo
?
used
*
2
:
used
;
}
static
ssize_t
pmac_ct_s16_read
(
const
u_char
*
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
*
up
=
(
short
*
)
userPtr
;
while
(
count
>
0
)
{
short
data
;
data
=
*
fp
;
if
(
put_user
(
data
,
up
++
))
return
-
EFAULT
;
fp
+=
2
;
count
--
;
}
}
else
{
if
(
copy_to_user
((
u_char
*
)
userPtr
,
fp
,
count
*
4
))
return
-
EFAULT
;
}
*
frameUsed
+=
used
*
4
;
return
stereo
?
used
*
4
:
used
*
2
;
}
static
ssize_t
pmac_ct_u16_read
(
const
u_char
*
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
*
up
=
(
short
*
)
userPtr
;
frameLeft
>>=
2
;
userCount
>>=
(
stereo
?
2
:
1
);
used
=
count
=
min_t
(
unsigned
long
,
userCount
,
frameLeft
);
while
(
count
>
0
)
{
int
data
;
data
=
*
fp
++
;
data
^=
mask
;
if
(
put_user
(
data
,
up
++
))
return
-
EFAULT
;
if
(
stereo
)
{
data
=
*
fp
;
data
^=
mask
;
if
(
put_user
(
data
,
up
++
))
return
-
EFAULT
;
}
fp
++
;
count
--
;
}
*
frameUsed
+=
used
*
4
;
return
stereo
?
used
*
4
:
used
*
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
,
};
/* 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
,
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment