Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
Commits
6bc09656
Commit
6bc09656
authored
Mar 23, 2006
by
Juha Yrjola
Browse files
Options
Browse Files
Download
Plain Diff
Merge source.mvista.com:linux-omap
parents
b6a3bbbf
7508d984
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
2654 additions
and
151 deletions
+2654
-151
arch/arm/mach-omap1/board-h2.c
arch/arm/mach-omap1/board-h2.c
+38
-1
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-omap1/board-osk.c
+35
-2
drivers/cbus/tahvo-usb.c
drivers/cbus/tahvo-usb.c
+2
-1
include/asm-arm/arch-omap/omap-alsa.h
include/asm-arm/arch-omap/omap-alsa.h
+59
-45
sound/arm/Kconfig
sound/arm/Kconfig
+14
-0
sound/arm/Makefile
sound/arm/Makefile
+1
-2
sound/arm/omap/Makefile
sound/arm/omap/Makefile
+9
-0
sound/arm/omap/omap-alsa-aic23-mixer.c
sound/arm/omap/omap-alsa-aic23-mixer.c
+30
-41
sound/arm/omap/omap-alsa-aic23.c
sound/arm/omap/omap-alsa-aic23.c
+321
-0
sound/arm/omap/omap-alsa-aic23.h
sound/arm/omap/omap-alsa-aic23.h
+83
-0
sound/arm/omap/omap-alsa-dma.c
sound/arm/omap/omap-alsa-dma.c
+31
-44
sound/arm/omap/omap-alsa-dma.h
sound/arm/omap/omap-alsa-dma.h
+9
-15
sound/arm/omap/omap-alsa-tsc2101-mixer.c
sound/arm/omap/omap-alsa-tsc2101-mixer.c
+864
-0
sound/arm/omap/omap-alsa-tsc2101-mixer.h
sound/arm/omap/omap-alsa-tsc2101-mixer.h
+79
-0
sound/arm/omap/omap-alsa-tsc2101.c
sound/arm/omap/omap-alsa-tsc2101.c
+439
-0
sound/arm/omap/omap-alsa-tsc2101.h
sound/arm/omap/omap-alsa-tsc2101.h
+61
-0
sound/arm/omap/omap-alsa.c
sound/arm/omap/omap-alsa.c
+579
-0
No files found.
arch/arm/mach-omap1/board-h2.c
View file @
6bc09656
...
...
@@ -40,8 +40,9 @@
#include <asm/arch/irda.h>
#include <asm/arch/usb.h>
#include <asm/arch/keypad.h>
#include <asm/arch/dma.h>
#include <asm/arch/common.h>
#include <asm/arch/mcbsp.h>
#include <asm/arch/omap-alsa.h>
extern
int
omap_gpio_init
(
void
);
...
...
@@ -285,6 +286,41 @@ static struct platform_device h2_lcd_device = {
.
id
=
-
1
,
};
static
struct
omap_mcbsp_reg_cfg
mcbsp_regs
=
{
.
spcr2
=
FREE
|
FRST
|
GRST
|
XRST
|
XINTM
(
3
),
.
spcr1
=
RINTM
(
3
)
|
RRST
,
.
rcr2
=
RPHASE
|
RFRLEN2
(
OMAP_MCBSP_WORD_8
)
|
RWDLEN2
(
OMAP_MCBSP_WORD_16
)
|
RDATDLY
(
1
),
.
rcr1
=
RFRLEN1
(
OMAP_MCBSP_WORD_8
)
|
RWDLEN1
(
OMAP_MCBSP_WORD_16
),
.
xcr2
=
XPHASE
|
XFRLEN2
(
OMAP_MCBSP_WORD_8
)
|
XWDLEN2
(
OMAP_MCBSP_WORD_16
)
|
XDATDLY
(
1
)
|
XFIG
,
.
xcr1
=
XFRLEN1
(
OMAP_MCBSP_WORD_8
)
|
XWDLEN1
(
OMAP_MCBSP_WORD_16
),
.
srgr1
=
FWID
(
15
),
.
srgr2
=
GSYNC
|
CLKSP
|
FSGM
|
FPER
(
31
),
.
pcr0
=
CLKXM
|
CLKRM
|
FSXP
|
FSRP
|
CLKXP
|
CLKRP
,
//.pcr0 = CLKXP | CLKRP, /* mcbsp: slave */
};
static
struct
omap_alsa_codec_config
alsa_config
=
{
.
name
=
"H2 TSC2101"
,
.
mcbsp_regs_alsa
=
&
mcbsp_regs
,
.
codec_configure_dev
=
NULL
,
// tsc2101_configure,
.
codec_set_samplerate
=
NULL
,
// tsc2101_set_samplerate,
.
codec_clock_setup
=
NULL
,
// tsc2101_clock_setup,
.
codec_clock_on
=
NULL
,
// tsc2101_clock_on,
.
codec_clock_off
=
NULL
,
// tsc2101_clock_off,
.
get_default_samplerate
=
NULL
,
// tsc2101_get_default_samplerate,
};
static
struct
platform_device
h2_mcbsp1_device
=
{
.
name
=
"omap_alsa_mcbsp"
,
.
id
=
1
,
.
dev
=
{
.
platform_data
=
&
alsa_config
,
},
};
static
struct
platform_device
*
h2_devices
[]
__initdata
=
{
&
h2_nor_device
,
&
h2_nand_device
,
...
...
@@ -292,6 +328,7 @@ static struct platform_device *h2_devices[] __initdata = {
&
h2_irda_device
,
&
h2_kp_device
,
&
h2_lcd_device
,
&
h2_mcbsp1_device
,
};
static
void
__init
h2_init_smc91x
(
void
)
...
...
arch/arm/mach-omap1/board-osk.c
View file @
6bc09656
...
...
@@ -47,6 +47,8 @@
#include <asm/arch/tc.h>
#include <asm/arch/keypad.h>
#include <asm/arch/common.h>
#include <asm/arch/mcbsp.h>
#include <asm/arch/omap-alsa.h>
static
int
osk_keymap
[]
=
{
KEY
(
0
,
0
,
KEY_F1
),
...
...
@@ -149,9 +151,40 @@ static struct platform_device osk5912_cf_device = {
.
resource
=
osk5912_cf_resources
,
};
#define DEFAULT_BITPERSAMPLE 16
static
struct
omap_mcbsp_reg_cfg
mcbsp_regs
=
{
.
spcr2
=
FREE
|
FRST
|
GRST
|
XRST
|
XINTM
(
3
),
.
spcr1
=
RINTM
(
3
)
|
RRST
,
.
rcr2
=
RPHASE
|
RFRLEN2
(
OMAP_MCBSP_WORD_8
)
|
RWDLEN2
(
OMAP_MCBSP_WORD_16
)
|
RDATDLY
(
0
),
.
rcr1
=
RFRLEN1
(
OMAP_MCBSP_WORD_8
)
|
RWDLEN1
(
OMAP_MCBSP_WORD_16
),
.
xcr2
=
XPHASE
|
XFRLEN2
(
OMAP_MCBSP_WORD_8
)
|
XWDLEN2
(
OMAP_MCBSP_WORD_16
)
|
XDATDLY
(
0
)
|
XFIG
,
.
xcr1
=
XFRLEN1
(
OMAP_MCBSP_WORD_8
)
|
XWDLEN1
(
OMAP_MCBSP_WORD_16
),
.
srgr1
=
FWID
(
DEFAULT_BITPERSAMPLE
-
1
),
.
srgr2
=
GSYNC
|
CLKSP
|
FSGM
|
FPER
(
DEFAULT_BITPERSAMPLE
*
2
-
1
),
/*.pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,*/
/* mcbsp: master */
.
pcr0
=
CLKXP
|
CLKRP
,
/* mcbsp: slave */
};
static
struct
omap_alsa_codec_config
alsa_config
=
{
.
name
=
"OSK AIC23"
,
.
mcbsp_regs_alsa
=
&
mcbsp_regs
,
.
codec_configure_dev
=
NULL
,
// aic23_configure,
.
codec_set_samplerate
=
NULL
,
// aic23_set_samplerate,
.
codec_clock_setup
=
NULL
,
// aic23_clock_setup,
.
codec_clock_on
=
NULL
,
// aic23_clock_on,
.
codec_clock_off
=
NULL
,
// aic23_clock_off,
.
get_default_samplerate
=
NULL
,
// aic23_get_default_samplerate,
};
static
struct
platform_device
osk5912_mcbsp1_device
=
{
.
name
=
"omap
_mcbsp"
,
.
name
=
"omap_alsa
_mcbsp"
,
.
id
=
1
,
.
dev
=
{
.
platform_data
=
&
alsa_config
,
},
};
static
struct
resource
osk5912_kp_resources
[]
=
{
...
...
drivers/cbus/tahvo-usb.c
View file @
6bc09656
...
...
@@ -193,8 +193,9 @@ static int omap_otg_probe(struct device *dev)
static
int
omap_otg_remove
(
struct
device
*
dev
)
{
tahvo_otg_dev
=
NULL
;
free_irq
(
tahvo_otg_dev
->
resource
[
1
].
start
,
&
tahvo_usb_device
);
tahvo_otg_dev
=
NULL
;
return
0
;
}
...
...
sound/arm/omap-aic23
.h
→
include/asm-arm/arch-omap/omap-alsa
.h
View file @
6bc09656
/*
*
sound/arm/omap-aic23
.h
*
linux/include/asm-arm/arch-omap/omap-alsa
.h
*
* Alsa Driver for AIC23 codec on OSK5912 platform board
* Alsa Driver for AIC23 and TSC2101 codecs on OMAP platform boards.
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
...
...
@@ -33,39 +35,38 @@
* 2005/07/25 INdT-10LE Kernel Team - Alsa driver for omap osk,
* original version based in sa1100 driver
* and omap oss driver.
*
* 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu
*/
#ifndef __OMAP_A
IC23
_H
#define __OMAP_A
IC23
_H
#ifndef __OMAP_A
LSA
_H
#define __OMAP_A
LSA
_H
#include <sound/driver.h>
#include <asm/arch/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <asm/arch/mcbsp.h>
#include <linux/platform_device.h>
/*
* Debug functions
*/
#undef DEBUG
//#define DEBUG
#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
#ifdef DEBUG
#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__)
#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
#else
#define DPRINTK(ARGS...)
/* nop */
#define ADEBUG()
/* nop */
#define FN_IN
/* nop */
#define FN_OUT(n)
/* nop */
#endif
#define DEFAULT_OUTPUT_VOLUME 0x60
#define DEFAULT_INPUT_VOLUME 0x00
/* 0 ==> mute line in */
#define OUTPUT_VOLUME_MIN LHV_MIN
#define OUTPUT_VOLUME_MAX LHV_MAX
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
#define INPUT_VOLUME_MIN LIV_MIN
#define INPUT_VOLUME_MAX LIV_MAX
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
#define SIDETONE_MASK 0x1c0
#define SIDETONE_0 0x100
#define SIDETONE_6 0x000
#define SIDETONE_9 0x040
#define SIDETONE_12 0x080
#define SIDETONE_18 0x0c0
#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB
#define DMA_BUF_SIZE (1024 * 8)
/*
* Buffer management for alsa and dma
...
...
@@ -93,38 +94,51 @@ struct audio_stream {
/*
* Alsa card structure for aic23
*/
struct
snd_card_omap_
aic23
{
struct
snd_card_omap_
codec
{
snd_card_t
*
card
;
snd_pcm_t
*
pcm
;
long
samplerate
;
struct
audio_stream
s
[
2
];
/* playback & capture */
};
/*********** Function Prototypes *************************/
/* Codec specific information and function pointers.
* Codec (omap-alsa-aic23.c and omap-alsa-tsc2101.c)
* are responsible for defining the function pointers.
*/
struct
omap_alsa_codec_config
{
char
*
name
;
struct
omap_mcbsp_reg_cfg
*
mcbsp_regs_alsa
;
snd_pcm_hw_constraint_list_t
*
hw_constraints_rates
;
snd_pcm_hardware_t
*
snd_omap_alsa_playback
;
snd_pcm_hardware_t
*
snd_omap_alsa_capture
;
void
(
*
codec_configure_dev
)(
void
);
void
(
*
codec_set_samplerate
)(
long
);
void
(
*
codec_clock_setup
)(
void
);
int
(
*
codec_clock_on
)(
void
);
int
(
*
codec_clock_off
)(
void
);
int
(
*
get_default_samplerate
)(
void
);
};
void
audio_dma_callback
(
void
*
);
int
snd_omap_mixer
(
struct
snd_card_omap_
aic23
*
);
/*********** Mixer function prototypes *************************/
int
snd_omap_mixer
(
struct
snd_card_omap_
codec
*
);
void
snd_omap_init_mixer
(
void
);
/* Clock functions */
int
omap_aic23_clock_on
(
void
);
int
omap_aic23_clock_off
(
void
);
#ifdef CONFIG_PM
void
snd_omap_suspend_mixer
(
void
);
void
snd_omap_resume_mixer
(
void
);
#endif
/* Codec AIC23 */
#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
extern
int
tlv320aic23_write_value
(
u8
reg
,
u16
value
);
/* TLV320AIC23 is a write only device */
static
__inline__
void
audio_aic23_write
(
u8
address
,
u16
data
)
{
tlv320aic23_write_value
(
address
,
data
);
}
int
snd_omap_alsa_post_probe
(
struct
platform_device
*
pdev
,
struct
omap_alsa_codec_config
*
config
);
int
snd_omap_alsa_remove
(
struct
platform_device
*
pdev
);
#ifdef CONFIG_PM
int
snd_omap_alsa_suspend
(
struct
platform_device
*
pdev
,
pm_message_t
state
);
int
snd_omap_alsa_resume
(
struct
platform_device
*
pdev
);
#else
#define snd_omap_alsa_suspend NULL
#define snd_omap_alsa_resume NULL
#endif
#endif
/* CONFIG_SENSORS_TLV320AIC23 */
/*********** function prototype to function called from the dma interrupt handler ******/
void
callback_omap_alsa_sound_dma
(
void
*
);
#endif
sound/arm/Kconfig
View file @
6bc09656
...
...
@@ -45,4 +45,18 @@ config SND_OMAP_AIC23
To compile this driver as a module, choose M here: the module
will be called snd-omap-aic23.
config SND_OMAP_TSC2101
tristate "OMAP TSC2101 alsa driver"
depends on ARCH_OMAP && SND
select SND_PCM
select OMAP_TSC2101
select OMAP_UWIRE if ARCH_OMAP
help
Say Y here if you have a OMAP platform board
and want to use its TSC2101 audio chip. Driver has
been tested with H2 and iPAQ h6300.
To compile this driver as a module, choose M here: the module
will be called snd-omap-tsc2101.
endmenu
sound/arm/Makefile
View file @
6bc09656
...
...
@@ -14,5 +14,4 @@ snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
obj-$(CONFIG_SND_PXA2XX_AC97)
+=
snd-pxa2xx-ac97.o
snd-pxa2xx-ac97-objs
:=
pxa2xx-ac97.o
obj-$(CONFIG_SND_OMAP_AIC23)
+=
snd-omap-aic23.o
snd-omap-aic23-objs
:=
omap-aic23.o omap-alsa-dma.o omap-alsa-mixer.o
obj-$(CONFIG_SND)
+=
omap/
sound/arm/omap/Makefile
0 → 100644
View file @
6bc09656
#
## Makefile for ALSA OMAP
#
#
obj-$(CONFIG_SND_OMAP_AIC23)
+=
snd-omap-alsa-aic23.o
snd-omap-alsa-aic23-objs
:=
omap-alsa.o omap-alsa-dma.o omap-alsa-aic23.o omap-alsa-aic23-mixer.o
obj-$(CONFIG_SND_OMAP_TSC2101)
+=
snd-omap-alsa-tsc2101.o
snd-omap-alsa-tsc2101-objs
:=
omap-alsa.o omap-alsa-dma.o omap-alsa-tsc2101.o omap-alsa-tsc2101-mixer.o
sound/arm/omap
-alsa
-mixer.c
→
sound/arm/omap
/omap-alsa-aic23
-mixer.c
View file @
6bc09656
/*
* sound/arm/omap
-alsa
-mixer.c
* sound/arm/omap
/omap-alsa-aic23
-mixer.c
*
* Alsa Driver Mixer for generic codecs for omap boards
*
...
...
@@ -39,20 +39,10 @@
#include <linux/config.h>
#include <sound/driver.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/dma.h>
#include <asm/arch/aic23.h>
#include "omap-aic23.h"
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-aic23.h"
#include <sound/initval.h>
#include <sound/control.h>
...
...
@@ -67,7 +57,7 @@ MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA");
/* Codec AIC23 */
#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE)
extern
__inline__
void
audio_aic23_write
(
u8
,
u16
);
extern
void
audio_aic23_write
(
u8
,
u16
);
#define MIXER_NAME "Mixer AIC23"
#define SND_OMAP_WRITE(reg, val) audio_aic23_write(reg, val)
...
...
@@ -411,31 +401,6 @@ static snd_kcontrol_new_t snd_omap_controls[] = {
OMAP_MUX
(
"Capture Source"
,
ANALOG_AUDIO_CONTROL_ADDR
,
AAC_INDEX
,
INSEL_MIC
),
};
void
snd_omap_init_mixer
(
void
)
{
u16
vol_reg
;
/* Line's default values */
omap_regs
[
LINE_INDEX
].
l_reg
=
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
;
omap_regs
[
LINE_INDEX
].
r_reg
=
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
;
omap_regs
[
LINE_INDEX
].
sw
=
0
;
SND_OMAP_WRITE
(
LEFT_LINE_VOLUME_ADDR
,
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
);
SND_OMAP_WRITE
(
RIGHT_LINE_VOLUME_ADDR
,
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
);
/* Analog Audio Control's default values */
omap_regs
[
AAC_INDEX
].
l_reg
=
DEFAULT_ANALOG_AUDIO_CONTROL
;
/* Headphone's default values */
vol_reg
=
LZC_ON
;
vol_reg
&=
~
OUTPUT_VOLUME_MASK
;
vol_reg
|=
DEFAULT_OUTPUT_VOLUME
;
omap_regs
[
PCM_INDEX
].
l_reg
=
DEFAULT_OUTPUT_VOLUME
;
omap_regs
[
PCM_INDEX
].
r_reg
=
DEFAULT_OUTPUT_VOLUME
;
omap_regs
[
PCM_INDEX
].
sw
=
1
;
SND_OMAP_WRITE
(
LEFT_CHANNEL_VOLUME_ADDR
,
vol_reg
);
SND_OMAP_WRITE
(
RIGHT_CHANNEL_VOLUME_ADDR
,
vol_reg
);
}
#ifdef CONFIG_PM
void
snd_omap_suspend_mixer
(
void
)
...
...
@@ -474,7 +439,32 @@ void snd_omap_resume_mixer(void)
}
#endif
int
snd_omap_mixer
(
struct
snd_card_omap_aic23
*
chip
)
void
snd_omap_init_mixer
(
void
)
{
u16
vol_reg
;
/* Line's default values */
omap_regs
[
LINE_INDEX
].
l_reg
=
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
;
omap_regs
[
LINE_INDEX
].
r_reg
=
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
;
omap_regs
[
LINE_INDEX
].
sw
=
0
;
SND_OMAP_WRITE
(
LEFT_LINE_VOLUME_ADDR
,
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
);
SND_OMAP_WRITE
(
RIGHT_LINE_VOLUME_ADDR
,
DEFAULT_INPUT_VOLUME
&
INPUT_VOLUME_MASK
);
/* Analog Audio Control's default values */
omap_regs
[
AAC_INDEX
].
l_reg
=
DEFAULT_ANALOG_AUDIO_CONTROL
;
/* Headphone's default values */
vol_reg
=
LZC_ON
;
vol_reg
&=
~
OUTPUT_VOLUME_MASK
;
vol_reg
|=
DEFAULT_OUTPUT_VOLUME
;
omap_regs
[
PCM_INDEX
].
l_reg
=
DEFAULT_OUTPUT_VOLUME
;
omap_regs
[
PCM_INDEX
].
r_reg
=
DEFAULT_OUTPUT_VOLUME
;
omap_regs
[
PCM_INDEX
].
sw
=
1
;
SND_OMAP_WRITE
(
LEFT_CHANNEL_VOLUME_ADDR
,
vol_reg
);
SND_OMAP_WRITE
(
RIGHT_CHANNEL_VOLUME_ADDR
,
vol_reg
);
}
int
snd_omap_mixer
(
struct
snd_card_omap_codec
*
chip
)
{
snd_card_t
*
card
;
unsigned
int
idx
;
...
...
@@ -493,4 +483,3 @@ int snd_omap_mixer(struct snd_card_omap_aic23 *chip)
return
0
;
}
sound/arm/omap/omap-alsa-aic23.c
0 → 100644
View file @
6bc09656
/*
* arch/arm/mach-omap1/omap-alsa-aic23.c
*
* Alsa codec Driver for AIC23 chip on OSK5912 platform board
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
* {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Based in former alsa driver for osk and oss driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <linux/clk.h>
#include <asm/arch/clock.h>
#include <asm/arch/aic23.h>
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-aic23.h"
static
struct
clk
*
aic23_mclk
=
0
;
/* aic23 related */
static
const
struct
aic23_samplerate_reg_info
rate_reg_info
[
NUMBER_SAMPLE_RATES_SUPPORTED
]
=
{
{
4000
,
0x06
,
1
},
/* 4000 */
{
8000
,
0x06
,
0
},
/* 8000 */
{
16000
,
0x0C
,
1
},
/* 16000 */
{
22050
,
0x11
,
1
},
/* 22050 */
{
24000
,
0x00
,
1
},
/* 24000 */
{
32000
,
0x0C
,
0
},
/* 32000 */
{
44100
,
0x11
,
0
},
/* 44100 */
{
48000
,
0x00
,
0
},
/* 48000 */
{
88200
,
0x1F
,
0
},
/* 88200 */
{
96000
,
0x0E
,
0
},
/* 96000 */
};
/*
* Hardware capabilities
*/
/*
* DAC USB-mode sampling rates (MCLK = 12 MHz)
* The rates and rate_reg_into MUST be in the same order
*/
static
unsigned
int
rates
[]
=
{
4000
,
8000
,
16000
,
22050
,
24000
,
32000
,
44100
,
48000
,
88200
,
96000
,
};
static
snd_pcm_hw_constraint_list_t
aic23_hw_constraints_rates
=
{
.
count
=
ARRAY_SIZE
(
rates
),
.
list
=
rates
,
.
mask
=
0
,
};
static
snd_pcm_hardware_t
aic23_snd_omap_alsa_playback
=
{
.
info
=
(
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
(
SNDRV_PCM_RATE_8000
|
SNDRV_PCM_RATE_16000
|
SNDRV_PCM_RATE_22050
|
SNDRV_PCM_RATE_32000
|
SNDRV_PCM_RATE_44100
|
SNDRV_PCM_RATE_48000
|
SNDRV_PCM_RATE_88200
|
SNDRV_PCM_RATE_96000
|
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
8000
,
.
rate_max
=
96000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
128
*
1024
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8
*
1024
,
.
periods_min
=
16
,
.
periods_max
=
255
,
.
fifo_size
=
0
,
};
static
snd_pcm_hardware_t
aic23_snd_omap_alsa_capture
=
{
.
info
=
(
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
(
SNDRV_PCM_RATE_8000
|
SNDRV_PCM_RATE_16000
|
SNDRV_PCM_RATE_22050
|
SNDRV_PCM_RATE_32000
|
SNDRV_PCM_RATE_44100
|
SNDRV_PCM_RATE_48000
|
SNDRV_PCM_RATE_88200
|
SNDRV_PCM_RATE_96000
|
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
8000
,
.
rate_max
=
96000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
128
*
1024
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8
*
1024
,
.
periods_min
=
16
,
.
periods_max
=
255
,
.
fifo_size
=
0
,
};
/*
* Codec/mcbsp init and configuration section
* codec dependent code.
*/
extern
int
tlv320aic23_write_value
(
u8
reg
,
u16
value
);
/* TLV320AIC23 is a write only device */
void
audio_aic23_write
(
u8
address
,
u16
data
)
{
tlv320aic23_write_value
(
address
,
data
);
}
EXPORT_SYMBOL_GPL
(
audio_aic23_write
);
/*
* Sample rate changing
*/
void
aic23_set_samplerate
(
long
rate
)
{
u8
count
=
0
;
u16
data
=
0
;
/* Fix the rate if it has a wrong value */
if
(
rate
>=
96000
)
rate
=
96000
;
else
if
(
rate
>=
88200
)
rate
=
88200
;
else
if
(
rate
>=
48000
)
rate
=
48000
;
else
if
(
rate
>=
44100
)
rate
=
44100
;
else
if
(
rate
>=
32000
)
rate
=
32000
;
else
if
(
rate
>=
24000
)
rate
=
24000
;
else
if
(
rate
>=
22050
)
rate
=
22050
;
else
if
(
rate
>=
16000
)
rate
=
16000
;
else
if
(
rate
>=
8000
)
rate
=
8000
;
else
rate
=
4000
;
/* Search for the right sample rate */
/* Verify what happens if the rate is not supported
* now it goes to 96Khz */
while
((
rate_reg_info
[
count
].
sample_rate
!=
rate
)
&&
(
count
<
(
NUMBER_SAMPLE_RATES_SUPPORTED
-
1
)))
{
count
++
;
}
data
=
(
rate_reg_info
[
count
].
divider
<<
CLKIN_SHIFT
)
|
(
rate_reg_info
[
count
].
control
<<
BOSR_SHIFT
)
|
USB_CLK_ON
;
audio_aic23_write
(
SAMPLE_RATE_CONTROL_ADDR
,
data
);
}
inline
void
aic23_configure
(
void
)
{
/* Reset codec */
audio_aic23_write
(
RESET_CONTROL_ADDR
,
0
);
/* Initialize the AIC23 internal state */
/* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
audio_aic23_write
(
ANALOG_AUDIO_CONTROL_ADDR
,
DEFAULT_ANALOG_AUDIO_CONTROL
);
/* Digital audio path control, de-emphasis control 44.1kHz */
audio_aic23_write
(
DIGITAL_AUDIO_CONTROL_ADDR
,
DEEMP_44K
);
/* Digital audio interface, master/slave mode, I2S, 16 bit */
#ifdef AIC23_MASTER
audio_aic23_write
(
DIGITAL_AUDIO_FORMAT_ADDR
,
MS_MASTER
|
IWL_16
|
FOR_DSP
);
#else
audio_aic23_write
(
DIGITAL_AUDIO_FORMAT_ADDR
,
IWL_16
|
FOR_DSP
);
#endif
/* Enable digital interface */
audio_aic23_write
(
DIGITAL_INTERFACE_ACT_ADDR
,
ACT_ON
);
}
/*
* Omap MCBSP clock configuration and Power Management
*
* Here we have some functions that allows clock to be enabled and
* disabled only when needed. Besides doing clock configuration
* it allows turn on/turn off audio when necessary.
*/
/*
* Do clock framework mclk search
*/
void
aic23_clock_setup
(
void
)
{
aic23_mclk
=
clk_get
(
0
,
"mclk"
);
}
/*
* Do some sanity check, set clock rate, starts it and
* turn codec audio on
*/
int
aic23_clock_on
(
void
)
{
if
(
clk_get_usecount
(
aic23_mclk
)
>
0
)
{
/* MCLK is already in use */
printk
(
KERN_WARNING
"MCLK in use at %d Hz. We change it to %d Hz
\n
"
,
(
uint
)
clk_get_rate
(
aic23_mclk
),
CODEC_CLOCK
);
}
if
(
clk_set_rate
(
aic23_mclk
,
CODEC_CLOCK
))
{
printk
(
KERN_ERR
"Cannot set MCLK for AIC23 CODEC
\n
"
);
return
-
ECANCELED
;
}
clk_enable
(
aic23_mclk
);
printk
(
KERN_DEBUG
"MCLK = %d [%d], usecount = %d
\n
"
,
(
uint
)
clk_get_rate
(
aic23_mclk
),
CODEC_CLOCK
,
clk_get_usecount
(
aic23_mclk
));
/* Now turn the audio on */
audio_aic23_write
(
POWER_DOWN_CONTROL_ADDR
,
~
DEVICE_POWER_OFF
&
~
OUT_OFF
&
~
DAC_OFF
&
~
ADC_OFF
&
~
MIC_OFF
&
~
LINE_OFF
);
return
0
;
}
/*
* Do some sanity check, turn clock off and then turn
* codec audio off
*/
int
aic23_clock_off
(
void
)
{
if
(
clk_get_usecount
(
aic23_mclk
)
>
0
)
{
if
(
clk_get_rate
(
aic23_mclk
)
!=
CODEC_CLOCK
)
{
printk
(
KERN_WARNING
"MCLK for audio should be %d Hz. But is %d Hz
\n
"
,
(
uint
)
clk_get_rate
(
aic23_mclk
),
CODEC_CLOCK
);
}
clk_disable
(
aic23_mclk
);
}
audio_aic23_write
(
POWER_DOWN_CONTROL_ADDR
,
DEVICE_POWER_OFF
|
OUT_OFF
|
DAC_OFF
|
ADC_OFF
|
MIC_OFF
|
LINE_OFF
);
return
0
;
}
int
aic23_get_default_samplerate
(
void
)
{
return
DEFAULT_SAMPLE_RATE
;
}
static
int
__init
snd_omap_alsa_aic23_probe
(
struct
platform_device
*
pdev
)
{
int
ret
;
struct
omap_alsa_codec_config
*
codec_cfg
;
codec_cfg
=
pdev
->
dev
.
platform_data
;
if
(
codec_cfg
!=
NULL
)
{
codec_cfg
->
hw_constraints_rates
=
&
aic23_hw_constraints_rates
;
codec_cfg
->
snd_omap_alsa_playback
=
&
aic23_snd_omap_alsa_playback
;
codec_cfg
->
snd_omap_alsa_capture
=
&
aic23_snd_omap_alsa_capture
;
codec_cfg
->
codec_configure_dev
=
aic23_configure
;
codec_cfg
->
codec_set_samplerate
=
aic23_set_samplerate
;
codec_cfg
->
codec_clock_setup
=
aic23_clock_setup
;
codec_cfg
->
codec_clock_on
=
aic23_clock_on
;
codec_cfg
->
codec_clock_off
=
aic23_clock_off
;
codec_cfg
->
get_default_samplerate
=
aic23_get_default_samplerate
;
ret
=
snd_omap_alsa_post_probe
(
pdev
,
codec_cfg
);
}
else
ret
=
-
ENODEV
;
return
ret
;
}
static
struct
platform_driver
omap_alsa_driver
=
{
.
probe
=
snd_omap_alsa_aic23_probe
,
.
remove
=
snd_omap_alsa_remove
,
.
suspend
=
snd_omap_alsa_suspend
,
.
resume
=
snd_omap_alsa_resume
,
.
driver
=
{
.
name
=
"omap_alsa_mcbsp"
,
},
};
static
int
__init
omap_alsa_aic23_init
(
void
)
{
int
err
;
ADEBUG
();
err
=
platform_driver_register
(
&
omap_alsa_driver
);
return
err
;
}
static
void
__exit
omap_alsa_aic23_exit
(
void
)
{
ADEBUG
();
platform_driver_unregister
(
&
omap_alsa_driver
);
}
module_init
(
omap_alsa_aic23_init
);
module_exit
(
omap_alsa_aic23_exit
);
sound/arm/omap/omap-alsa-aic23.h
0 → 100644
View file @
6bc09656
/*
* sound/arm/omap-alsa-aic23.h
*
* Alsa Driver for AIC23 codec on OSK5912 platform board
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
* {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __OMAP_ALSA_AIC23_H
#define __OMAP_ALSA_AIC23_H
#include <sound/driver.h>
#include <asm/arch/dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <asm/arch/mcbsp.h>
/* Define to set the AIC23 as the master w.r.t McBSP */
#define AIC23_MASTER
#define NUMBER_SAMPLE_RATES_SUPPORTED 10
/*
* AUDIO related MACROS
*/
#ifndef DEFAULT_BITPERSAMPLE
#define DEFAULT_BITPERSAMPLE 16
#endif
#define DEFAULT_SAMPLE_RATE 44100
#define CODEC_CLOCK 12000000
#define AUDIO_MCBSP OMAP_MCBSP1
#define DEFAULT_OUTPUT_VOLUME 0x60
#define DEFAULT_INPUT_VOLUME 0x00
/* 0 ==> mute line in */
#define OUTPUT_VOLUME_MIN LHV_MIN
#define OUTPUT_VOLUME_MAX LHV_MAX
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX
#define INPUT_VOLUME_MIN LIV_MIN
#define INPUT_VOLUME_MAX LIV_MAX
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX
#define SIDETONE_MASK 0x1c0
#define SIDETONE_0 0x100
#define SIDETONE_6 0x000
#define SIDETONE_9 0x040
#define SIDETONE_12 0x080
#define SIDETONE_18 0x0c0
#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB
struct
aic23_samplerate_reg_info
{
u32
sample_rate
;
u8
control
;
/* SR3, SR2, SR1, SR0 and BOSR */
u8
divider
;
/* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
};
/*
* Defines codec specific functions pointers that can be used from the
* common omap-alse base driver for all omap codecs. (tsc2101 and aic23)
*/
void
define_codec_functions
(
struct
omap_alsa_codec_config
*
codec_config
);
inline
void
aic23_configure
(
void
);
void
aic23_set_samplerate
(
long
rate
);
void
aic23_clock_setup
(
void
);
int
aic23_clock_on
(
void
);
int
aic23_clock_off
(
void
);
int
aic23_get_default_samplerate
(
void
);
#endif
sound/arm/omap-alsa-dma.c
→
sound/arm/omap
/omap
-alsa-dma.c
View file @
6bc09656
/*
* sound/arm/omap-alsa-dma.c
* sound/arm/omap
/omap
-alsa-dma.c
*
* Common audio DMA handling for the OMAP processors
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
*
* Copyright (C) 2004 Texas Instruments, Inc.
...
...
@@ -66,20 +68,9 @@
#include <asm/arch/mcbsp.h>
#include
"omap-aic23.h"
#include
<asm/arch/omap-alsa.h>
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)
#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)
#else
#define DPRINTK( x... )
#define FN_IN
#define FN_OUT(x)
#endif
#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);
...
...
@@ -165,7 +156,7 @@ static void omap_sound_dma_link_lch(void *data)
FN_OUT
(
0
);
}
int
omap_request_sound_dma
(
int
device_id
,
const
char
*
device_name
,
int
omap_request_
alsa_
sound_dma
(
int
device_id
,
const
char
*
device_name
,
void
*
data
,
int
**
channels
)
{
int
i
,
err
=
0
;
...
...
@@ -186,9 +177,10 @@ int omap_request_sound_dma(int device_id, const char *device_name,
}
spin_lock
(
&
dma_list_lock
);
for
(
i
=
0
;
i
<
nr_linked_channels
;
i
++
)
{
err
=
omap_request_dma
(
device_id
,
device_name
,
sound_dma_irq_handler
,
data
,
err
=
omap_request_dma
(
device_id
,
device_name
,
sound_dma_irq_handler
,
data
,
&
chan
[
i
]);
/* Handle Failure condition here */
...
...
@@ -223,7 +215,7 @@ int omap_request_sound_dma(int device_id, const char *device_name,
**************************************************************************************/
static
void
omap_sound_dma_unlink_lch
(
void
*
data
)
{
struct
audio_stream
*
s
=
(
struct
audio_stream
*
)
data
;
struct
audio_stream
*
s
=
(
struct
audio_stream
*
)
data
;
int
*
chan
=
s
->
lch
;
int
i
;
...
...
@@ -243,11 +235,11 @@ static void omap_sound_dma_unlink_lch(void *data)
FN_OUT
(
0
);
}
int
omap_free_sound_dma
(
void
*
data
,
int
**
channels
)
int
omap_free_
alsa_
sound_dma
(
void
*
data
,
int
**
channels
)
{
int
i
;
int
*
chan
=
NULL
;
FN_IN
;
if
(
unlikely
(
NULL
==
channels
))
{
BUG
();
...
...
@@ -277,10 +269,11 @@ int omap_free_sound_dma(void *data, int **channels)
* Stop all the DMA channels of the stream
*
**************************************************************************************/
void
omap_
audio_stop
_dma
(
struct
audio_stream
*
s
)
void
omap_
stop_alsa_sound
_dma
(
struct
audio_stream
*
s
)
{
int
*
chan
=
s
->
lch
;
int
i
;
FN_IN
;
if
(
unlikely
(
NULL
==
chan
))
{
BUG
();
...
...
@@ -299,7 +292,7 @@ void omap_audio_stop_dma(struct audio_stream *s)
* Clear any pending transfers
*
**************************************************************************************/
void
omap_clear_sound_dma
(
struct
audio_stream
*
s
)
void
omap_clear_
alsa_
sound_dma
(
struct
audio_stream
*
s
)
{
FN_IN
;
omap_clear_dma
(
s
->
lch
[
s
->
dma_q_head
]);
...
...
@@ -307,13 +300,6 @@ void omap_clear_sound_dma(struct audio_stream * s)
return
;
}
/*********************************** MODULE FUNCTIONS DEFINTIONS ***********************/
#ifdef OMAP1610_MCBSP1_BASE
#undef OMAP1610_MCBSP1_BASE
#endif
#define OMAP1610_MCBSP1_BASE 0xE1011000
/***************************************************************************************
*
* DMA related functions
...
...
@@ -325,9 +311,10 @@ static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
int
dt
=
0x1
;
/* data type 16 */
int
cen
=
32
;
/* Stereo */
int
cfn
=
dma_size
/
(
2
*
cen
);
FN_IN
;
omap_set_dma_dest_params
(
channel
,
0x05
,
0x00
,
(
OMAP1
610_MCBSP1_BASE
+
0x8
06
),
(
OMAP1
510_MCBSP1_BASE
+
0x
06
),
0
,
0
);
omap_set_dma_src_params
(
channel
,
0x00
,
0x01
,
dma_ptr
,
0
,
0
);
...
...
@@ -341,11 +328,11 @@ static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
{
int
dt
=
0x1
;
/* data type 16 */
int
cen
=
32
;
/* stereo */
int
cfn
=
dma_size
/
(
2
*
cen
);
FN_IN
;
omap_set_dma_src_params
(
channel
,
0x05
,
0x00
,
(
OMAP1
610_MCBSP1_BASE
+
0x8
02
),
(
OMAP1
510_MCBSP1_BASE
+
0x
02
),
0
,
0
);
omap_set_dma_dest_params
(
channel
,
0x00
,
0x01
,
dma_ptr
,
0
,
0
);
omap_set_dma_transfer_params
(
channel
,
dt
,
cen
,
cfn
,
0x00
,
0
,
0
);
...
...
@@ -372,7 +359,8 @@ static int audio_start_dma_chain(struct audio_stream *s)
* Do the initial set of work to initialize all the channels as required.
* We shall then initate a transfer
*/
int
omap_start_sound_dma
(
struct
audio_stream
*
s
,
dma_addr_t
dma_ptr
,
int
omap_start_alsa_sound_dma
(
struct
audio_stream
*
s
,
dma_addr_t
dma_ptr
,
u_int
dma_size
)
{
int
ret
=
-
EPERM
;
...
...
@@ -439,18 +427,17 @@ static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status,
}
if
(
ch_status
&
DCSR_END_BLOCK
)
audio_dma_callback
(
s
);
callback_omap_alsa_sound_dma
(
s
);
FN_OUT
(
0
);
return
;
}
MODULE_AUTHOR
(
"Texas Instruments"
);
MODULE_DESCRIPTION
(
"Common DMA handling for Audio driver on OMAP processors"
);
MODULE_DESCRIPTION
(
"Common DMA handling for Audio driver on OMAP processors"
);
MODULE_LICENSE
(
"GPL"
);
EXPORT_SYMBOL
(
omap_start_sound_dma
);
EXPORT_SYMBOL
(
omap_clear_sound_dma
);
EXPORT_SYMBOL
(
omap_request_sound_dma
);
EXPORT_SYMBOL
(
omap_free_sound_dma
);
EXPORT_SYMBOL
(
omap_
audio_stop
_dma
);
EXPORT_SYMBOL
(
omap_start_
alsa_
sound_dma
);
EXPORT_SYMBOL
(
omap_clear_
alsa_
sound_dma
);
EXPORT_SYMBOL
(
omap_request_
alsa_
sound_dma
);
EXPORT_SYMBOL
(
omap_free_
alsa_
sound_dma
);
EXPORT_SYMBOL
(
omap_
stop_alsa_sound
_dma
);
sound/arm/omap-alsa-dma.h
→
sound/arm/omap
/omap
-alsa-dma.h
View file @
6bc09656
/*
* linux/sound/arm/omap-alsa-dma.h
* linux/sound/arm/omap
/omap
-alsa-dma.h
*
* Common audio DMA handling for the OMAP processors
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
*
* Copyright (C) 2004 Texas Instruments, Inc.
...
...
@@ -30,14 +32,7 @@
/************************** INCLUDES *************************************/
#include "omap-aic23.h"
/************************** GLOBAL MACROS *************************************/
/* Provide the Macro interfaces common across platforms */
#define DMA_REQUEST(e,s, cb) {e=omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);}
#define DMA_FREE(s) omap_free_sound_dma(s, &s->lch)
#define DMA_CLEAR(s) omap_clear_sound_dma(s)
#include <asm/arch/omap-alsa.h>
/************************** GLOBAL DATA STRUCTURES *********************************/
...
...
@@ -45,15 +40,14 @@ typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data);
/**************** ARCH SPECIFIC FUNCIONS *******************************************/
void
omap_clear_sound_dma
(
struct
audio_stream
*
s
);
void
omap_clear_
alsa_
sound_dma
(
struct
audio_stream
*
s
);
int
omap_request_sound_dma
(
int
device_id
,
const
char
*
device_name
,
int
omap_request_
alsa_
sound_dma
(
int
device_id
,
const
char
*
device_name
,
void
*
data
,
int
**
channels
);
int
omap_free_sound_dma
(
void
*
data
,
int
**
channels
);
int
omap_free_
alsa_
sound_dma
(
void
*
data
,
int
**
channels
);
int
omap_start_sound_dma
(
struct
audio_stream
*
s
,
dma_addr_t
dma_ptr
,
u_int
dma_size
);
int
omap_start_alsa_sound_dma
(
struct
audio_stream
*
s
,
dma_addr_t
dma_ptr
,
u_int
dma_size
);
void
omap_
audio_stop
_dma
(
struct
audio_stream
*
s
);
void
omap_
stop_alsa_sound
_dma
(
struct
audio_stream
*
s
);
#endif
sound/arm/omap/omap-alsa-tsc2101-mixer.c
0 → 100644
View file @
6bc09656
/*
* sound/arm/omap/omap-alsa-tsc2101-mixer.c
*
* Alsa Driver for TSC2101 codec for OMAP platform boards.
*
* Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and
* Everett Coleman II <gcc80x86@fuzzyneural.net>
*
* Board initialization code is based on the code in TSC2101 OSS driver.
* Copyright (C) 2004 Texas Instruments, Inc.
* Written by Nishanth Menon and Sriram Kannan
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
*
* 2006-03-01 Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
* Can switch between headset and loudspeaker playback,
* mute and unmute dgc, set dgc volume. Record source switch,
* keyclick, buzzer and headset volume and handset volume control
* are still missing.
*
*/
#include "omap-alsa-tsc2101.h"
#include "omap-alsa-tsc2101-mixer.h"
#include <linux/types.h>
#include <sound/initval.h>
#include <sound/control.h>
//#define M_DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)
#define M_DPRINTK(ARGS...)
/* nop */
#define DGC_DALVL_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
#define DGC_DARVL_EXTRACT(ARG) ((ARG & 0x007f))
#define GET_DGC_DALMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
#define GET_DGC_DARMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(7)) >> 7)
#define IS_DGC_DALMU_UNMUTED(ARG) (((GET_DGC_DALMU_BIT_VALUE(ARG)) == 0))
#define IS_DGC_DARMU_UNMUTED(ARG) (((GET_DGC_DARMU_BIT_VALUE(ARG)) == 0))
#define HGC_ADPGA_HED_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
#define GET_DGC_HGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
#define IS_DGC_HGCMU_UNMUTED(ARG) (((GET_DGC_HGCMU_BIT_VALUE(ARG)) == 0))
#define HNGC_ADPGA_HND_EXTRACT(ARG) ((ARG & 0x7f00) >> 8)
#define GET_DGC_HNGCMU_BIT_VALUE(ARG) (((ARG) & TSC2101_BIT(15)) >> 15)
#define IS_DGC_HNGCMU_UNMUTED(ARG) (((GET_DGC_HNGCMU_BIT_VALUE(ARG)) == 0))
static
int
current_playback_target
=
PLAYBACK_TARGET_LOUDSPEAKER
;
static
int
current_rec_src
=
REC_SRC_SINGLE_ENDED_MICIN_HED
;
/*
* Used for switching between TSC2101 recourd sources.
* Logic is adjusted from the TSC2101 OSS code.
*/
static
int
set_record_source
(
int
val
)
{
u16
data
;
int
maskedVal
;
FN_IN
;
maskedVal
=
0xe0
&
val
;
data
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_MIXER_PGA_CTRL
);
data
&=
~
MPC_MICSEL
(
7
);
/* clear all MICSEL bits */
data
|=
maskedVal
;
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_MIXER_PGA_CTRL
,
data
);
current_rec_src
=
val
;
FN_OUT
(
0
);
return
0
;
}
/*
* Converts the Alsa mixer volume (0 - 100) to real
* Digital Gain Control (DGC) value that can be written
* or read from the TSC2101 registry.
*
* Note that the number "OUTPUT_VOLUME_MAX" is smaller than OUTPUT_VOLUME_MIN
* because DGC works as a volume decreaser. (The more bigger value is put
* to DGC, the more the volume of controlled channel is decreased)
*
* In addition the TCS2101 chip would allow the maximum volume reduction be 63.5 DB
* but according to some tests user can not hear anything with this chip
* when the volume is set to be less than 25 db.
* Therefore this function will return a value that means 38.5 db (63.5 db - 25 db)
* reduction in the channel volume, when mixer is set to 0.
* For mixer value 100, this will return a value that means 0 db volume reduction.
* ([mute_left_bit]0000000[mute_right_bit]0000000)
*/
int
get_mixer_volume_as_dac_gain_control_volume
(
int
vol
)
{
u16
retVal
;
/* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */
retVal
=
((
vol
*
OUTPUT_VOLUME_RANGE
)
/
100
)
+
OUTPUT_VOLUME_MAX
;
/* invert the value for getting the proper range 0 min and 100 max */
retVal
=
OUTPUT_VOLUME_MIN
-
retVal
;
return
retVal
;
}
/*
* Converts the Alsa mixer volume (0 - 100) to TSC2101
* Digital Gain Control (DGC) volume. Alsa mixer volume 0
* is converted to value meaning the volume reduction of -38.5 db
* and Alsa mixer volume 100 is converted to value meaning the
* reduction of 0 db.
*/
int
set_mixer_volume_as_dac_gain_control_volume
(
int
mixerVolL
,
int
mixerVolR
)
{
u16
val
;
int
retVal
;
int
volL
;
int
volR
;
if
((
mixerVolL
<
0
)
||
(
mixerVolL
>
100
)
||
(
mixerVolR
<
0
)
||
(
mixerVolR
>
100
))
{
printk
(
KERN_ERR
"Trying a bad mixer volume as dac gain control volume value, left (%d), right (%d)!
\n
"
,
mixerVolL
,
mixerVolR
);
return
-
EPERM
;
}
M_DPRINTK
(
"mixer volume left = %d, right = %d
\n
"
,
mixerVolL
,
mixerVolR
);
volL
=
get_mixer_volume_as_dac_gain_control_volume
(
mixerVolL
);
volR
=
get_mixer_volume_as_dac_gain_control_volume
(
mixerVolR
);
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_DAC_GAIN_CTRL
);
/* keep the old mute bit settings */
val
&=
~
(
DGC_DALVL
(
OUTPUT_VOLUME_MIN
)
|
DGC_DARVL
(
OUTPUT_VOLUME_MIN
));
val
|=
DGC_DALVL
(
volL
)
|
DGC_DARVL
(
volR
);
retVal
=
2
;
if
(
retVal
)
{
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_DAC_GAIN_CTRL
,
val
);
}
M_DPRINTK
(
"to registry: left = %d, right = %d, total = %d
\n
"
,
DGC_DALVL_EXTRACT
(
val
),
DGC_DARVL_EXTRACT
(
val
),
val
);
return
retVal
;
}
int
dac_gain_control_unmute_control
(
int
muteLeft
,
int
muteRight
)
{
u16
val
;
int
count
;
count
=
0
;
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_DAC_GAIN_CTRL
);
/* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
* so if values are same, it's time to change the registry value.
*/
if
(
muteLeft
==
GET_DGC_DALMU_BIT_VALUE
(
val
))
{
if
(
muteLeft
==
0
)
{
/* mute --> turn bit on */
val
=
val
|
DGC_DALMU
;
}
else
{
/* unmute --> turn bit off */
val
=
val
&
~
DGC_DALMU
;
}
count
++
;
}
/* L */
if
(
muteRight
==
GET_DGC_DARMU_BIT_VALUE
(
val
))
{
if
(
muteRight
==
0
)
{
/* mute --> turn bit on */
val
=
val
|
DGC_DARMU
;
}
else
{
/* unmute --> turn bit off */
val
=
val
&
~
DGC_DARMU
;
}
count
++
;
}
/* R */
if
(
count
)
{
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_DAC_GAIN_CTRL
,
val
);
M_DPRINTK
(
"changed value, is_unmuted left = %d, right = %d
\n
"
,
IS_DGC_DALMU_UNMUTED
(
val
),
IS_DGC_DARMU_UNMUTED
(
val
));
}
return
count
;
}
/*
* Converts the DGC registry value read from the TSC2101 registry to
* Alsa mixer volume format (0 - 100).
*/
int
get_dac_gain_control_volume_as_mixer_volume
(
u16
vol
)
{
u16
retVal
;
retVal
=
OUTPUT_VOLUME_MIN
-
vol
;
retVal
=
((
retVal
-
OUTPUT_VOLUME_MAX
)
*
100
)
/
OUTPUT_VOLUME_RANGE
;
/* fix scaling error */
if
((
retVal
>
0
)
&&
(
retVal
<
100
))
{
retVal
++
;
}
return
retVal
;
}
/*
* Converts the headset gain control volume (0 - 63.5 db)
* to Alsa mixer volume (0 - 100)
*/
int
get_headset_gain_control_volume_as_mixer_volume
(
u16
registerVal
)
{
u16
retVal
;
retVal
=
((
registerVal
*
100
)
/
INPUT_VOLUME_RANGE
);
return
retVal
;
}
/*
* Converts the handset gain control volume (0 - 63.5 db)
* to Alsa mixer volume (0 - 100)
*/
int
get_handset_gain_control_volume_as_mixer_volume
(
u16
registerVal
)
{
return
get_headset_gain_control_volume_as_mixer_volume
(
registerVal
);
}
/*
* Converts the Alsa mixer volume (0 - 100) to
* headset gain control volume (0 - 63.5 db)
*/
int
get_mixer_volume_as_headset_gain_control_volume
(
u16
mixerVal
)
{
u16
retVal
;
retVal
=
((
mixerVal
*
INPUT_VOLUME_RANGE
)
/
100
)
+
INPUT_VOLUME_MIN
;
return
retVal
;
}
/*
* Writes Alsa mixer volume (0 - 100) to TSC2101 headset volume registry in
* a TSC2101 format. (0 - 63.5 db)
* In TSC2101 OSS driver this functionality was controlled with "SET_LINE" parameter.
*/
int
set_mixer_volume_as_headset_gain_control_volume
(
int
mixerVol
)
{
int
volume
;
int
retVal
;
u16
val
;
if
(
mixerVol
<
0
||
mixerVol
>
100
)
{
M_DPRINTK
(
"Trying a bad headset mixer volume value(%d)!
\n
"
,
mixerVol
);
return
-
EPERM
;
}
M_DPRINTK
(
"mixer volume = %d
\n
"
,
mixerVol
);
/* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */
/* NOTE: 0 is minimum volume and not mute */
volume
=
get_mixer_volume_as_headset_gain_control_volume
(
mixerVol
);
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HEADSET_GAIN_CTRL
);
/* preserve the old mute settings */
val
&=
~
(
HGC_ADPGA_HED
(
INPUT_VOLUME_MAX
));
val
|=
HGC_ADPGA_HED
(
volume
);
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HEADSET_GAIN_CTRL
,
val
);
retVal
=
1
;
M_DPRINTK
(
"to registry = %d
\n
"
,
val
);
return
retVal
;
}
/*
* Writes Alsa mixer volume (0 - 100) to TSC2101 handset volume registry in
* a TSC2101 format. (0 - 63.5 db)
* In TSC2101 OSS driver this functionality was controlled with "SET_MIC" parameter.
*/
int
set_mixer_volume_as_handset_gain_control_volume
(
int
mixerVol
)
{
int
volume
;
int
retVal
;
u16
val
;
if
(
mixerVol
<
0
||
mixerVol
>
100
)
{
M_DPRINTK
(
"Trying a bad mic mixer volume value(%d)!
\n
"
,
mixerVol
);
return
-
EPERM
;
}
M_DPRINTK
(
"mixer volume = %d
\n
"
,
mixerVol
);
/* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range
* NOTE: 0 is minimum volume and not mute
*/
volume
=
get_mixer_volume_as_headset_gain_control_volume
(
mixerVol
);
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
);
/* preserve the old mute settigns */
val
&=
~
(
HNGC_ADPGA_HND
(
INPUT_VOLUME_MAX
));
val
|=
HNGC_ADPGA_HND
(
volume
);
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
,
val
);
retVal
=
1
;
M_DPRINTK
(
"to registry = %d
\n
"
,
val
);
return
retVal
;
}
void
init_record_sources
(
void
)
{
/* Mute Analog Sidetone
* analog sidetone gain db?
* Cell Phone In not connected to ADC
* Input selected by MICSEL connected to ADC
*/
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_MIXER_PGA_CTRL
,
MPC_ASTMU
|
MPC_ASTG
(
0x40
)
|
~
MPC_CPADC
|
MPC_MICADC
);
/* Set record source, Select MIC_INHED input for headset */
set_record_source
(
REC_SRC_SINGLE_ENDED_MICIN_HED
);
}
void
set_loudspeaker_to_playback_target
(
void
)
{
u16
val
;
/* power down sp1, sp2 and loudspeaker */
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_CODEC_POWER_CTRL
,
CPC_SP1PWDN
|
CPC_SP2PWDN
|
CPC_LDAPWDF
);
/* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled
* 1dB AGC hysteresis
* MICes bias 2V
*/
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_4
,
AC4_MB_HED
(
0
));
/* DAC left and right routed to SPK1/SPK2
* SPK1/SPK2 unmuted
* keyclicks routed to SPK1/SPK2
*/
val
=
AC5_DIFFIN
|
AC5_DAC2SPK1
(
3
)
|
AC5_AST2SPK1
|
AC5_KCL2SPK1
|
AC5_DAC2SPK2
(
3
)
|
AC5_AST2SPK2
|
AC5_KCL2SPK2
|
AC5_HDSCPTC
;
val
=
val
&
~
AC5_HDSCPTC
;
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_5
,
val
);
/* powerdown spk1/out32n and spk2 */
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_POWERDOWN_STS
);
val
=
val
&
~
(
~
PS_SPK1FL
|
~
PS_HNDFL
|
PS_LSPKFL
);
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_POWERDOWN_STS
,
val
);
/* routing selected to SPK1 goes to OUT8P/OUT84 alsa. (loudspeaker)
* analog sidetone routed to loudspeaker
* buzzer pga routed to loudspeaker
* keyclick routing to loudspeaker
* cellphone input routed to loudspeaker
* mic selection (control register 04h/page2) routed to cell phone output (CP_OUT)
* routing selected for SPK1 goes also to cellphone output (CP_OUT)
* OUT8P/OUT8N (loudspeakers) unmuted (0 = unmuted)
* Cellphone output is not muted (0 = unmuted)
* Enable loudspeaker short protection control (0 = enable protection)
* VGND short protection control (0 = enable protection)
*/
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_6
,
AC6_SPL2LSK
|
AC6_AST2LSK
|
AC6_BUZ2LSK
|
AC6_KCL2LSK
|
AC6_CPI2LSK
|
AC6_MIC2CPO
|
AC6_SPL2CPO
|
~
AC6_MUTLSPK
|
~
AC6_MUTSPK2
|
~
AC6_LDSCPTC
|
~
AC6_VGNDSCPTC
);
current_playback_target
=
PLAYBACK_TARGET_LOUDSPEAKER
;
}
void
set_headphone_to_playback_target
(
void
)
{
/* power down sp1, sp2 and loudspeaker */
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_CODEC_POWER_CTRL
,
CPC_SP1PWDN
|
CPC_SP2PWDN
|
CPC_LDAPWDF
);
/* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */
/* 1dB AGC hysteresis */
/* MICes bias 2V */
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_4
,
AC4_MB_HED
(
0
));
/* DAC left and right routed to SPK2 */
/* SPK1/2 unmuted */
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_5
,
AC5_DAC2SPK1
(
3
)
|
AC5_AST2SPK1
|
AC5_KCL2SPK1
|
AC5_DAC2SPK2
(
3
)
|
AC5_AST2SPK2
|
AC5_KCL2SPK2
|
AC5_HDSCPTC
);
/* OUT8P/N muted, CPOUT muted */
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_6
,
AC6_MUTLSPK
|
AC6_MUTSPK2
|
AC6_LDSCPTC
|
AC6_VGNDSCPTC
);
current_playback_target
=
PLAYBACK_TARGET_HEADPHONE
;
}
/*
* Checks whether the headset is detected.
* If headset is detected, the type is returned. Type can be
* 0x01 = stereo headset detected
* 0x02 = cellurar headset detected
* 0x03 = stereo + cellurar headset detected
* If headset is not detected 0 is returned.
*/
u16
get_headset_detected
(
void
)
{
u16
curDetected
;
u16
curType
;
u16
curVal
;
curType
=
0
;
/* not detected */
curVal
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_7
);
curDetected
=
curVal
&
AC7_HDDETFL
;
if
(
curDetected
)
{
printk
(
"headset detected, checking type from %d
\n
"
,
curVal
);
curType
=
((
curVal
&
0x6000
)
>>
13
);
printk
(
"headset type detected = %d
\n
"
,
curType
);
}
else
{
printk
(
"headset not detected
\n
"
);
}
return
curType
;
}
void
init_playback_targets
(
void
)
{
u16
val
;
set_loudspeaker_to_playback_target
();
/* Left line input volume control
* = SET_LINE in the OSS driver
*/
set_mixer_volume_as_headset_gain_control_volume
(
DEFAULT_INPUT_VOLUME
);
/* Set headset to be controllable by handset mixer
* AGC enable for handset input
* Handset input not muted
*/
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
);
val
=
val
|
HNGC_AGCEN_HND
;
val
=
val
&
~
HNGC_ADMUT_HND
;
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
,
val
);
/* mic input volume control
* SET_MIC in the OSS driver
*/
set_mixer_volume_as_handset_gain_control_volume
(
DEFAULT_INPUT_VOLUME
);
/* Left/Right headphone channel volume control
* Zero-cross detect on
*/
set_mixer_volume_as_dac_gain_control_volume
(
DEFAULT_OUTPUT_VOLUME
,
DEFAULT_OUTPUT_VOLUME
);
/* unmute */
dac_gain_control_unmute_control
(
1
,
1
);
}
/*
* Initializes tsc2101 recourd source (to line) and playback target (to loudspeaker)
*/
void
snd_omap_init_mixer
(
void
)
{
FN_IN
;
/* Headset/Hook switch detect enabled */
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_AUDIO_CTRL_7
,
AC7_DETECT
);
init_record_sources
();
init_playback_targets
();
FN_OUT
(
0
);
}
static
int
__pcm_playback_target_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
static
char
*
texts
[
PLAYBACK_TARGET_COUNT
]
=
{
"Loudspeaker"
,
"Headphone"
};
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_ENUMERATED
;
uinfo
->
count
=
1
;
uinfo
->
value
.
enumerated
.
items
=
PLAYBACK_TARGET_COUNT
;
if
(
uinfo
->
value
.
enumerated
.
item
>
PLAYBACK_TARGET_COUNT
-
1
)
{
uinfo
->
value
.
enumerated
.
item
=
PLAYBACK_TARGET_COUNT
-
1
;
}
strcpy
(
uinfo
->
value
.
enumerated
.
name
,
texts
[
uinfo
->
value
.
enumerated
.
item
]);
return
0
;
}
static
int
__pcm_playback_target_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
ucontrol
->
value
.
integer
.
value
[
0
]
=
current_playback_target
;
return
0
;
}
static
int
__pcm_playback_target_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
int
retVal
;
int
curVal
;
retVal
=
0
;
curVal
=
ucontrol
->
value
.
integer
.
value
[
0
];
if
((
curVal
>=
0
)
&&
(
curVal
<
PLAYBACK_TARGET_COUNT
)
&&
(
curVal
!=
current_playback_target
))
{
if
(
curVal
==
0
)
{
set_loudspeaker_to_playback_target
();
}
else
{
set_headphone_to_playback_target
();
}
retVal
=
1
;
}
return
retVal
;
}
static
int
__pcm_playback_volume_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
2
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
100
;
return
0
;
}
/*
* Alsa mixer interface function for getting the volume read from the DGC in a
* 0 -100 alsa mixer format.
*/
static
int
__pcm_playback_volume_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
u16
volL
;
u16
volR
;
u16
val
;
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_DAC_GAIN_CTRL
);
M_DPRINTK
(
"registry value = %d!
\n
"
,
val
);
volL
=
DGC_DALVL_EXTRACT
(
val
);
volR
=
DGC_DARVL_EXTRACT
(
val
);
/* make sure that other bits are not on */
volL
=
volL
&
~
DGC_DALMU
;
volR
=
volR
&
~
DGC_DARMU
;
volL
=
get_dac_gain_control_volume_as_mixer_volume
(
volL
);
volR
=
get_dac_gain_control_volume_as_mixer_volume
(
volR
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
volL
;
/* L */
ucontrol
->
value
.
integer
.
value
[
1
]
=
volR
;
/* R */
M_DPRINTK
(
"mixer volume left = %ld, right = %ld
\n
"
,
ucontrol
->
value
.
integer
.
value
[
0
],
ucontrol
->
value
.
integer
.
value
[
1
]);
return
0
;
}
static
int
__pcm_playback_volume_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
return
set_mixer_volume_as_dac_gain_control_volume
(
ucontrol
->
value
.
integer
.
value
[
0
],
ucontrol
->
value
.
integer
.
value
[
1
]);
}
static
int
__pcm_playback_switch_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BOOLEAN
;
uinfo
->
count
=
2
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
1
;
return
0
;
}
/*
* When DGC_DALMU (bit 15) is 1, the left channel is muted.
* When DGC_DALMU is 0, left channel is not muted.
* Same logic apply also for the right channel.
*/
static
int
__pcm_playback_switch_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
u16
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_DAC_GAIN_CTRL
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
IS_DGC_DALMU_UNMUTED
(
val
);
ucontrol
->
value
.
integer
.
value
[
1
]
=
IS_DGC_DARMU_UNMUTED
(
val
);
return
0
;
}
static
int
__pcm_playback_switch_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
return
dac_gain_control_unmute_control
(
ucontrol
->
value
.
integer
.
value
[
0
],
ucontrol
->
value
.
integer
.
value
[
1
]);
}
static
int
__headset_playback_volume_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
100
;
return
0
;
}
static
int
__headset_playback_volume_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
u16
val
;
u16
vol
;
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HEADSET_GAIN_CTRL
);
M_DPRINTK
(
"registry value = %d
\n
"
,
val
);
vol
=
HGC_ADPGA_HED_EXTRACT
(
val
);
vol
=
vol
&
~
HGC_ADMUT_HED
;
vol
=
get_headset_gain_control_volume_as_mixer_volume
(
vol
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
vol
;
M_DPRINTK
(
"mixer volume returned = %ld
\n
"
,
ucontrol
->
value
.
integer
.
value
[
0
]);
return
0
;
}
static
int
__headset_playback_volume_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
return
set_mixer_volume_as_headset_gain_control_volume
(
ucontrol
->
value
.
integer
.
value
[
0
]);
}
static
int
__headset_playback_switch_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BOOLEAN
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
1
;
return
0
;
}
/* When HGC_ADMUT_HED (bit 15) is 1, the headset is muted.
* When HGC_ADMUT_HED is 0, headset is not muted.
*/
static
int
__headset_playback_switch_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
u16
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HEADSET_GAIN_CTRL
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
IS_DGC_HGCMU_UNMUTED
(
val
);
return
0
;
}
static
int
__headset_playback_switch_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
int
count
=
0
;
u16
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HEADSET_GAIN_CTRL
);
/* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
* so if values are same, it's time to change the registry value...
*/
if
(
ucontrol
->
value
.
integer
.
value
[
0
]
==
GET_DGC_HGCMU_BIT_VALUE
(
val
))
{
if
(
ucontrol
->
value
.
integer
.
value
[
0
]
==
0
)
{
/* mute --> turn bit on */
val
=
val
|
HGC_ADMUT_HED
;
}
else
{
/* unmute --> turn bit off */
val
=
val
&
~
HGC_ADMUT_HED
;
}
count
++
;
M_DPRINTK
(
"changed value, is_unmuted = %d
\n
"
,
IS_DGC_HGCMU_UNMUTED
(
val
));
}
if
(
count
)
{
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HEADSET_GAIN_CTRL
,
val
);
}
return
count
;
}
static
int
__handset_playback_volume_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
100
;
return
0
;
}
static
int
__handset_playback_volume_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
u16
val
;
u16
vol
;
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
);
M_DPRINTK
(
"registry value = %d
\n
"
,
val
);
vol
=
HNGC_ADPGA_HND_EXTRACT
(
val
);
vol
=
vol
&
~
HNGC_ADMUT_HND
;
vol
=
get_handset_gain_control_volume_as_mixer_volume
(
vol
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
vol
;
M_DPRINTK
(
"mixer volume returned = %ld
\n
"
,
ucontrol
->
value
.
integer
.
value
[
0
]);
return
0
;
}
static
int
__handset_playback_volume_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
return
set_mixer_volume_as_handset_gain_control_volume
(
ucontrol
->
value
.
integer
.
value
[
0
]);
}
static
int
__handset_playback_switch_info
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_info_t
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BOOLEAN
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
1
;
return
0
;
}
/* When HNGC_ADMUT_HND (bit 15) is 1, the handset is muted.
* When HNGC_ADMUT_HND is 0, handset is not muted.
*/
static
int
__handset_playback_switch_get
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
u16
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
IS_DGC_HNGCMU_UNMUTED
(
val
);
return
0
;
}
static
int
__handset_playback_switch_put
(
snd_kcontrol_t
*
kcontrol
,
snd_ctl_elem_value_t
*
ucontrol
)
{
int
count
=
0
;
u16
val
=
omap_tsc2101_read
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
);
/* in alsa mixer 1 --> on, 0 == off. In tsc2101 registry 1 --> off, 0 --> on
* so if values are same, it's time to change the registry value...
*/
if
(
ucontrol
->
value
.
integer
.
value
[
0
]
==
GET_DGC_HNGCMU_BIT_VALUE
(
val
))
{
if
(
ucontrol
->
value
.
integer
.
value
[
0
]
==
0
)
{
/* mute --> turn bit on */
val
=
val
|
HNGC_ADMUT_HND
;
}
else
{
/* unmute --> turn bit off */
val
=
val
&
~
HNGC_ADMUT_HND
;
}
M_DPRINTK
(
"changed value, is_unmuted = %d
\n
"
,
IS_DGC_HNGCMU_UNMUTED
(
val
));
count
++
;
}
if
(
count
)
{
omap_tsc2101_write
(
TSC2101_AUDIO_CODEC_REGISTERS_PAGE2
,
TSC2101_HANDSET_GAIN_CTRL
,
val
);
}
return
count
;
}
static
snd_kcontrol_new_t
tsc2101_control
[]
__devinitdata
=
{
{
.
name
=
"Playback Playback Route"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
0
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__pcm_playback_target_info
,
.
get
=
__pcm_playback_target_get
,
.
put
=
__pcm_playback_target_put
,
},
{
.
name
=
"Master Playback Volume"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
0
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__pcm_playback_volume_info
,
.
get
=
__pcm_playback_volume_get
,
.
put
=
__pcm_playback_volume_put
,
},
{
.
name
=
"Master Playback Switch"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
0
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__pcm_playback_switch_info
,
.
get
=
__pcm_playback_switch_get
,
.
put
=
__pcm_playback_switch_put
,
},
{
.
name
=
"Headset Playback Volume"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
1
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__headset_playback_volume_info
,
.
get
=
__headset_playback_volume_get
,
.
put
=
__headset_playback_volume_put
,
},
{
.
name
=
"Headset Playback Switch"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
1
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__headset_playback_switch_info
,
.
get
=
__headset_playback_switch_get
,
.
put
=
__headset_playback_switch_put
,
},
{
.
name
=
"Handset Playback Volume"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
2
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__handset_playback_volume_info
,
.
get
=
__handset_playback_volume_get
,
.
put
=
__handset_playback_volume_put
,
},
{
.
name
=
"Handset Playback Switch"
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
index
=
2
,
.
access
=
SNDRV_CTL_ELEM_ACCESS_READWRITE
,
.
info
=
__handset_playback_switch_info
,
.
get
=
__handset_playback_switch_get
,
.
put
=
__handset_playback_switch_put
,
}
};
#ifdef CONFIG_PM
void
snd_omap_suspend_mixer
(
void
)
{
}
void
snd_omap_resume_mixer
(
void
)
{
snd_omap_init_mixer
();
}
#endif
int
snd_omap_mixer
(
struct
snd_card_omap_codec
*
tsc2101
)
{
int
i
=
0
;
int
err
=
0
;
if
(
!
tsc2101
)
{
return
-
EINVAL
;
}
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
tsc2101_control
);
i
++
)
{
if
((
err
=
snd_ctl_add
(
tsc2101
->
card
,
snd_ctl_new1
(
&
tsc2101_control
[
i
],
tsc2101
->
card
)))
<
0
)
{
return
err
;
}
}
return
0
;
}
sound/arm/omap/omap-alsa-tsc2101-mixer.h
0 → 100644
View file @
6bc09656
/*
* sound/arm/omap/omap-alsa-tsc2101-mixer.c
*
* Alsa Driver for TSC2101 codec for OMAP platform boards.
*
* Copyright (C) 2005 Mika Laitio <lamikr@cc.jyu.fi> and
* Everett Coleman II <gcc80x86@fuzzyneural.net>
*
* Based on the ideas in omap-aic23.c and sa11xx-uda1341.c
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History:
*
* 2006-03-01 Mika Laitio - Mixer for the tsc2101 driver used in omap boards.
* Can switch between headset and loudspeaker playback,
* mute and unmute dgc, set dgc volume. Record source switch,
* keyclick, buzzer and headset volume and handset volume control
* are still missing.
*/
#ifndef OMAPALSATSC2101MIXER_H_
#define OMAPALSATSC2101MIXER_H_
#include <asm/hardware/tsc2101.h>
#include <../drivers/ssi/omap-tsc2101.h>
#include "omap-alsa-dma.h"
/* tsc2101 DAC gain control volume specific */
#define OUTPUT_VOLUME_MIN 0x7F // 1111111 = -63.5 DB
#define OUTPUT_VOLUME_MAX 0x32 // 110010
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX)
/* use input vol of 75 for 0dB gain */
#define INPUT_VOLUME_MIN 0x0
#define INPUT_VOLUME_MAX 0x7D
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define PLAYBACK_TARGET_COUNT 0x02
#define PLAYBACK_TARGET_LOUDSPEAKER 0x00
#define PLAYBACK_TARGET_HEADPHONE 0x01
/* following are used for register 03h Mixer PGA control bits D7-D5 for selecting record source */
#define REC_SRC_TARGET_COUNT 0x08
#define REC_SRC_SINGLE_ENDED_MICIN_HED MPC_MICSEL(0) // oss code referred to MIXER_LINE
#define REC_SRC_SINGLE_ENDED_MICIN_HND MPC_MICSEL(1) // oss code referred to MIXER_MIC
#define REC_SRC_SINGLE_ENDED_AUX1 MPC_MICSEL(2)
#define REC_SRC_SINGLE_ENDED_AUX2 MPC_MICSEL(3)
#define REC_SRC_MICIN_HED_AND_AUX1 MPC_MICSEL(4)
#define REC_SRC_MICIN_HED_AND_AUX2 MPC_MICSEL(5)
#define REC_SRC_MICIN_HND_AND_AUX1 MPC_MICSEL(6)
#define REC_SRC_MICIN_HND_AND_AUX2 MPC_MICSEL(7)
#define DEFAULT_OUTPUT_VOLUME 90 // default output volume to dac dgc
#define DEFAULT_INPUT_VOLUME 20 // default record volume
#define TSC2101_AUDIO_CODEC_REGISTERS_PAGE2 (2)
#endif
/*OMAPALSATSC2101MIXER_H_*/
sound/arm/omap/omap-alsa-tsc2101.c
0 → 100644
View file @
6bc09656
/*
* arch/arm/mach-omap1/omap-alsa-tsc2101.c
*
* Alsa codec Driver for TSC2101 chip for OMAP platform boards.
* Code obtained from oss omap drivers
*
* Copyright (C) 2004 Texas Instruments, Inc.
* Written by Nishanth Menon and Sriram Kannan
*
* Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/delay.h>
#include <linux/soundcard.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/arch/mcbsp.h>
#include <linux/slab.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <asm/mach-types.h>
#include <asm/arch/dma.h>
#include <asm/arch/clock.h>
#include <asm/hardware/tsc2101.h>
#include <../drivers/ssi/omap-tsc2101.h>
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-tsc2101.h"
static
struct
clk
*
tsc2101_mclk
=
0
;
//#define DUMP_TSC2101_AUDIO_REGISTERS
#undef DUMP_TSC2101_AUDIO_REGISTERS
/*
* Hardware capabilities
*/
/*
* DAC USB-mode sampling rates (MCLK = 12 MHz)
* The rates and rate_reg_into MUST be in the same order
*/
static
unsigned
int
rates
[]
=
{
7350
,
8000
,
8018
,
8727
,
8820
,
9600
,
11025
,
12000
,
14700
,
16000
,
22050
,
24000
,
29400
,
32000
,
44100
,
48000
,
};
static
snd_pcm_hw_constraint_list_t
tsc2101_hw_constraints_rates
=
{
.
count
=
ARRAY_SIZE
(
rates
),
.
list
=
rates
,
.
mask
=
0
,
};
static
const
struct
tsc2101_samplerate_reg_info
rate_reg_info
[
NUMBER_SAMPLE_RATES_SUPPORTED
]
=
{
/* Div 6 */
{
7350
,
7
,
1
},
{
8000
,
7
,
0
},
/* Div 5.5 */
{
8018
,
6
,
1
},
{
8727
,
6
,
0
},
/* Div 5 */
{
8820
,
5
,
1
},
{
9600
,
5
,
0
},
/* Div 4 */
{
11025
,
4
,
1
},
{
12000
,
4
,
0
},
/* Div 3 */
{
14700
,
3
,
1
},
{
16000
,
3
,
0
},
/* Div 2 */
{
22050
,
2
,
1
},
{
24000
,
2
,
0
},
/* Div 1.5 */
{
29400
,
1
,
1
},
{
32000
,
1
,
0
},
/* Div 1 */
{
44100
,
0
,
1
},
{
48000
,
0
,
0
},
};
static
snd_pcm_hardware_t
tsc2101_snd_omap_alsa_playback
=
{
.
info
=
(
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
(
SNDRV_PCM_RATE_8000
|
SNDRV_PCM_RATE_11025
|
SNDRV_PCM_RATE_16000
|
SNDRV_PCM_RATE_22050
|
SNDRV_PCM_RATE_32000
|
SNDRV_PCM_RATE_44100
|
SNDRV_PCM_RATE_48000
|
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
7350
,
.
rate_max
=
48000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
128
*
1024
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8
*
1024
,
.
periods_min
=
16
,
.
periods_max
=
255
,
.
fifo_size
=
0
,
};
static
snd_pcm_hardware_t
tsc2101_snd_omap_alsa_capture
=
{
.
info
=
(
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
(
SNDRV_PCM_RATE_8000
|
SNDRV_PCM_RATE_11025
|
SNDRV_PCM_RATE_16000
|
SNDRV_PCM_RATE_22050
|
SNDRV_PCM_RATE_32000
|
SNDRV_PCM_RATE_44100
|
SNDRV_PCM_RATE_48000
|
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
7350
,
.
rate_max
=
48000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
128
*
1024
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8
*
1024
,
.
periods_min
=
16
,
.
periods_max
=
255
,
.
fifo_size
=
0
,
};
/*
* Simplified write for tsc Audio
*/
inline
void
tsc2101_audio_write
(
u8
address
,
u16
data
)
{
omap_tsc2101_write
(
PAGE2_AUDIO_CODEC_REGISTERS
,
address
,
data
);
}
/*
* Simplified read for tsc Audio
*/
inline
u16
tsc2101_audio_read
(
u8
address
)
{
return
(
omap_tsc2101_read
(
PAGE2_AUDIO_CODEC_REGISTERS
,
address
));
}
#ifdef DUMP_TSC2101_AUDIO_REGISTERS
void
dump_tsc2101_audio_reg
(
void
)
{
printk
(
"TSC2101_AUDIO_CTRL_1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_1
));
printk
(
"TSC2101_HEADSET_GAIN_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_HEADSET_GAIN_CTRL
));
printk
(
"TSC2101_DAC_GAIN_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_DAC_GAIN_CTRL
));
printk
(
"TSC2101_MIXER_PGA_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_MIXER_PGA_CTRL
));
printk
(
"TSC2101_AUDIO_CTRL_2 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_2
));
printk
(
"TSC2101_CODEC_POWER_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_CODEC_POWER_CTRL
));
printk
(
"TSC2101_AUDIO_CTRL_3 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_3
));
printk
(
"TSC2101_LCH_BASS_BOOST_N0 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_N0
));
printk
(
"TSC2101_LCH_BASS_BOOST_N1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_N1
));
printk
(
"TSC2101_LCH_BASS_BOOST_N2 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_N2
));
printk
(
"TSC2101_LCH_BASS_BOOST_N3 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_N3
));
printk
(
"TSC2101_LCH_BASS_BOOST_N4 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_N4
));
printk
(
"TSC2101_LCH_BASS_BOOST_N5 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_N5
));
printk
(
"TSC2101_LCH_BASS_BOOST_D1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_D1
));
printk
(
"TSC2101_LCH_BASS_BOOST_D2 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_D2
));
printk
(
"TSC2101_LCH_BASS_BOOST_D4 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_D4
));
printk
(
"TSC2101_LCH_BASS_BOOST_D5 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_LCH_BASS_BOOST_D5
));
printk
(
"TSC2101_RCH_BASS_BOOST_N0 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_N0
));
printk
(
"TSC2101_RCH_BASS_BOOST_N1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_N1
));
printk
(
"TSC2101_RCH_BASS_BOOST_N2 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_N2
));
printk
(
"TSC2101_RCH_BASS_BOOST_N3 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_N3
));
printk
(
"TSC2101_RCH_BASS_BOOST_N4 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_N4
));
printk
(
"TSC2101_RCH_BASS_BOOST_N5 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_N5
));
printk
(
"TSC2101_RCH_BASS_BOOST_D1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_D1
));
printk
(
"TSC2101_RCH_BASS_BOOST_D2 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_D2
));
printk
(
"TSC2101_RCH_BASS_BOOST_D4 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_D4
));
printk
(
"TSC2101_RCH_BASS_BOOST_D5 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_RCH_BASS_BOOST_D5
));
printk
(
"TSC2101_PLL_PROG_1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_PLL_PROG_1
));
printk
(
"TSC2101_PLL_PROG_1 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_PLL_PROG_2
));
printk
(
"TSC2101_AUDIO_CTRL_4 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_4
));
printk
(
"TSC2101_HANDSET_GAIN_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_HANDSET_GAIN_CTRL
));
printk
(
"TSC2101_BUZZER_GAIN_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_BUZZER_GAIN_CTRL
));
printk
(
"TSC2101_AUDIO_CTRL_5 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_5
));
printk
(
"TSC2101_AUDIO_CTRL_6 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_6
));
printk
(
"TSC2101_AUDIO_CTRL_7 = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_7
));
printk
(
"TSC2101_GPIO_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_GPIO_CTRL
));
printk
(
"TSC2101_AGC_CTRL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_AGC_CTRL
));
printk
(
"TSC2101_POWERDOWN_STS = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_POWERDOWN_STS
));
printk
(
"TSC2101_MIC_AGC_CONTROL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_MIC_AGC_CONTROL
));
printk
(
"TSC2101_CELL_AGC_CONTROL = 0x%04x
\n
"
,
tsc2101_audio_read
(
TSC2101_CELL_AGC_CONTROL
));
}
#endif
/*
* ALSA operations according to board file
*/
/*
* Sample rate changing
*/
void
tsc2101_set_samplerate
(
long
sample_rate
)
{
u8
count
=
0
;
u16
data
=
0
;
int
clkgdv
=
0
;
u16
srgr1
,
srgr2
;
/* wait for any frame to complete */
udelay
(
125
);
ADEBUG
();
sample_rate
=
sample_rate
;
/* Search for the right sample rate */
while
((
rate_reg_info
[
count
].
sample_rate
!=
sample_rate
)
&&
(
count
<
NUMBER_SAMPLE_RATES_SUPPORTED
))
{
count
++
;
}
if
(
count
==
NUMBER_SAMPLE_RATES_SUPPORTED
)
{
printk
(
KERN_ERR
"Invalid Sample Rate %d requested
\n
"
,
(
int
)
sample_rate
);
return
;
// -EPERM;
}
/* Set AC1 */
data
=
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_1
);
/* Clear prev settings */
data
&=
~
(
AC1_DACFS
(
0x07
)
|
AC1_ADCFS
(
0x07
));
data
|=
AC1_DACFS
(
rate_reg_info
[
count
].
divisor
)
|
AC1_ADCFS
(
rate_reg_info
[
count
].
divisor
);
tsc2101_audio_write
(
TSC2101_AUDIO_CTRL_1
,
data
);
/* Set the AC3 */
data
=
tsc2101_audio_read
(
TSC2101_AUDIO_CTRL_3
);
/*Clear prev settings */
data
&=
~
(
AC3_REFFS
|
AC3_SLVMS
);
data
|=
(
rate_reg_info
[
count
].
fs_44kHz
)
?
AC3_REFFS
:
0
;
#ifdef TSC_MASTER
data
|=
AC3_SLVMS
;
#endif
/* #ifdef TSC_MASTER */
tsc2101_audio_write
(
TSC2101_AUDIO_CTRL_3
,
data
);
/* program the PLLs */
if
(
rate_reg_info
[
count
].
fs_44kHz
)
{
/* 44.1 khz - 12 MHz Mclk */
tsc2101_audio_write
(
TSC2101_PLL_PROG_1
,
PLL1_PLLSEL
|
PLL1_PVAL
(
1
)
|
PLL1_I_VAL
(
7
));
/* PVAL 1; I_VAL 7 */
tsc2101_audio_write
(
TSC2101_PLL_PROG_2
,
PLL2_D_VAL
(
0x1490
));
/* D_VAL 5264 */
}
else
{
/* 48 khz - 12 Mhz Mclk */
tsc2101_audio_write
(
TSC2101_PLL_PROG_1
,
PLL1_PLLSEL
|
PLL1_PVAL
(
1
)
|
PLL1_I_VAL
(
8
));
/* PVAL 1; I_VAL 8 */
tsc2101_audio_write
(
TSC2101_PLL_PROG_2
,
PLL2_D_VAL
(
0x780
));
/* D_VAL 1920 */
}
/* Set the sample rate */
#ifndef TSC_MASTER
clkgdv
=
CODEC_CLOCK
/
(
sample_rate
*
(
DEFAULT_BITPERSAMPLE
*
2
-
1
));
if
(
clkgdv
)
srgr1
=
(
FWID
(
DEFAULT_BITPERSAMPLE
-
1
)
|
CLKGDV
(
clkgdv
));
else
return
(
1
);
/* Stereo Mode */
srgr2
=
(
CLKSM
|
FSGM
|
FPER
(
DEFAULT_BITPERSAMPLE
*
2
-
1
));
#else
srgr1
=
(
FWID
(
DEFAULT_BITPERSAMPLE
-
1
)
|
CLKGDV
(
clkgdv
));
srgr2
=
((
GSYNC
|
CLKSP
|
FSGM
|
FPER
(
DEFAULT_BITPERSAMPLE
*
2
-
1
)));
#endif
/* end of #ifdef TSC_MASTER */
OMAP_MCBSP_WRITE
(
OMAP1610_MCBSP1_BASE
,
SRGR2
,
srgr2
);
OMAP_MCBSP_WRITE
(
OMAP1610_MCBSP1_BASE
,
SRGR1
,
srgr1
);
}
void
tsc2101_configure
(
void
)
{
}
/*
* Omap MCBSP clock and Power Management configuration
*
* Here we have some functions that allows clock to be enabled and
* disabled only when needed. Besides doing clock configuration
* it allows turn on/turn off audio when necessary.
*/
/*
* Do clock framework mclk search
*/
void
tsc2101_clock_setup
(
void
)
{
tsc2101_mclk
=
clk_get
(
0
,
"mclk"
);
}
/*
* Do some sanity check, set clock rate, starts it and turn codec audio on
*/
int
tsc2101_clock_on
(
void
)
{
int
curUseCount
;
uint
curRate
;
int
err
;
curUseCount
=
clk_get_usecount
(
tsc2101_mclk
);
DPRINTK
(
"clock use count = %d
\n
"
,
curUseCount
);
if
(
curUseCount
>
0
)
{
// MCLK is already in use
printk
(
KERN_WARNING
"MCLK already in use at %d Hz. We change it to %d Hz
\n
"
,
(
uint
)
clk_get_rate
(
tsc2101_mclk
),
CODEC_CLOCK
);
}
curRate
=
(
uint
)
clk_get_rate
(
tsc2101_mclk
);
DPRINTK
(
"old clock rate = %d
\n
"
,
curRate
);
if
(
curRate
!=
CODEC_CLOCK
)
{
err
=
clk_set_rate
(
tsc2101_mclk
,
CODEC_CLOCK
);
if
(
err
)
{
printk
(
KERN_WARNING
"Cannot set MCLK clock rate for TSC2101 CODEC, error code = %d
\n
"
,
err
);
//return -ECANCELED;
}
}
else
{
printk
(
KERN_INFO
"omap_alsa_tsc2101_clock_on(), no need to change rate, no need to change clock rate, rate already %d Hz.
\n
"
,
CODEC_CLOCK
);
}
err
=
clk_enable
(
tsc2101_mclk
);
curRate
=
(
uint
)
clk_get_rate
(
tsc2101_mclk
);
curUseCount
=
clk_get_usecount
(
tsc2101_mclk
);
DPRINTK
(
"MCLK = %d [%d], usecount = %d, clk_enable retval = %d
\n
"
,
curRate
,
CODEC_CLOCK
,
curUseCount
,
err
);
// Now turn the audio on
omap_tsc2101_write
(
PAGE2_AUDIO_CODEC_REGISTERS
,
TSC2101_CODEC_POWER_CTRL
,
0x0000
);
return
0
;
}
/*
* Do some sanity check, turn clock off and then turn
* codec audio off
*/
int
tsc2101_clock_off
(
void
)
{
int
curUseCount
;
int
curRate
;
curUseCount
=
clk_get_usecount
(
tsc2101_mclk
);
DPRINTK
(
"clock use count = %d
\n
"
,
curUseCount
);
if
(
curUseCount
>
0
)
{
curRate
=
clk_get_rate
(
tsc2101_mclk
);
DPRINTK
(
"clock rate = %d
\n
"
,
curRate
);
if
(
curRate
!=
CODEC_CLOCK
)
{
printk
(
KERN_WARNING
"MCLK for audio should be %d Hz. But is %d Hz
\n
"
,
(
uint
)
clk_get_rate
(
tsc2101_mclk
),
CODEC_CLOCK
);
}
clk_disable
(
tsc2101_mclk
);
DPRINTK
(
"clock disabled
\n
"
);
}
tsc2101_audio_write
(
TSC2101_CODEC_POWER_CTRL
,
~
(
CPC_SP1PWDN
|
CPC_SP2PWDN
|
CPC_BASSBC
));
DPRINTK
(
"audio codec off
\n
"
);
#ifdef DUMP_TSC2101_AUDIO_REGISTERS
printk
(
"tsc2101_clock_off()
\n
"
);
dump_tsc2101_audio_reg
();
#endif
return
0
;
}
int
tsc2101_get_default_samplerate
(
void
)
{
return
DEFAULT_SAMPLE_RATE
;
}
static
int
__init
snd_omap_alsa_tsc2101_probe
(
struct
platform_device
*
pdev
)
{
int
ret
;
struct
omap_alsa_codec_config
*
codec_cfg
;
codec_cfg
=
pdev
->
dev
.
platform_data
;
if
(
codec_cfg
!=
NULL
)
{
codec_cfg
->
hw_constraints_rates
=
&
tsc2101_hw_constraints_rates
;
codec_cfg
->
snd_omap_alsa_playback
=
&
tsc2101_snd_omap_alsa_playback
;
codec_cfg
->
snd_omap_alsa_capture
=
&
tsc2101_snd_omap_alsa_capture
;
codec_cfg
->
codec_configure_dev
=
tsc2101_configure
;
codec_cfg
->
codec_set_samplerate
=
tsc2101_set_samplerate
;
codec_cfg
->
codec_clock_setup
=
tsc2101_clock_setup
;
codec_cfg
->
codec_clock_on
=
tsc2101_clock_on
;
codec_cfg
->
codec_clock_off
=
tsc2101_clock_off
;
codec_cfg
->
get_default_samplerate
=
tsc2101_get_default_samplerate
;
ret
=
snd_omap_alsa_post_probe
(
pdev
,
codec_cfg
);
}
else
ret
=
-
ENODEV
;
return
ret
;
}
static
struct
platform_driver
omap_alsa_driver
=
{
.
probe
=
snd_omap_alsa_tsc2101_probe
,
.
remove
=
snd_omap_alsa_remove
,
.
suspend
=
snd_omap_alsa_suspend
,
.
resume
=
snd_omap_alsa_resume
,
.
driver
=
{
.
name
=
"omap_alsa_mcbsp"
,
},
};
static
int
__init
omap_alsa_tsc2101_init
(
void
)
{
int
err
;
ADEBUG
();
err
=
platform_driver_register
(
&
omap_alsa_driver
);
return
err
;
}
static
void
__exit
omap_alsa_tsc2101_exit
(
void
)
{
ADEBUG
();
platform_driver_unregister
(
&
omap_alsa_driver
);
}
module_init
(
omap_alsa_tsc2101_init
);
module_exit
(
omap_alsa_tsc2101_exit
);
sound/arm/omap/omap-alsa-tsc2101.h
0 → 100644
View file @
6bc09656
/*
* arch/arc/mach-omap1/omap-alsa-tsc2101.h
*
* Alsa Driver for TSC2101 codec for OMAP platform boards.
*
* Based on former omap-aic23.h and tsc2101 OSS drivers.
* Copyright (C) 2004 Texas Instruments, Inc.
* Written by Nishanth Menon and Sriram Kannan
*
* Copyright (C) 2006 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Alsa modularization by Daniel Petrini (d.pensator@gmail.com)
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef OMAP_ALSA_TSC2101_H_
#define OMAP_ALSA_TSC2101_H_
#include <linux/types.h>
/* Define to set the tsc as the master w.r.t McBSP */
#define TSC_MASTER
#define NUMBER_SAMPLE_RATES_SUPPORTED 16
/*
* AUDIO related MACROS
*/
#ifndef DEFAULT_BITPERSAMPLE
#define DEFAULT_BITPERSAMPLE 16
#endif
#define DEFAULT_SAMPLE_RATE 44100
#define CODEC_CLOCK 12000000
#define AUDIO_MCBSP OMAP_MCBSP1
#define PAGE2_AUDIO_CODEC_REGISTERS (2)
struct
tsc2101_samplerate_reg_info
{
u16
sample_rate
;
u8
divisor
;
u8
fs_44kHz
;
/* if 0 48 khz, if 1 44.1 khz fsref */
};
/*
* Defines codec specific functions pointers that can be used from the
* common omap-alse base driver for all omap codecs. (tsc2101 and aic23)
*/
inline
void
tsc2101_configure
(
void
);
void
tsc2101_set_samplerate
(
long
rate
);
void
tsc2101_clock_setup
(
void
);
int
tsc2101_clock_on
(
void
);
int
tsc2101_clock_off
(
void
);
int
tsc2101_get_default_samplerate
(
void
);
#endif
/*OMAP_ALSA_TSC2101_H_*/
sound/arm/omap
-aic23
.c
→
sound/arm/omap
/omap-alsa
.c
View file @
6bc09656
/*
* sound/arm/omap-a
ic23
.c
* sound/arm/omap-a
lsa
.c
*
* Alsa Driver for
AIC23 codec on OSK5912 platform board
* Alsa Driver for
OMAP
*
* Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
* Written by Daniel Petrini, David Cohen, Anderson Briglia
* {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br
*
* Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
*
* Based on sa11xx-uda1341.c,
* Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
*
...
...
@@ -35,129 +37,29 @@
* 2005-07-29 INdT Kernel Team - Alsa driver for omap osk. Creation of new
* file omap-aic23.c
*
* 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu
* 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed
* by Ajaya Babu
*
*/
#include <linux/config.h>
#include <sound/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/clk.h>
#ifdef CONFIG_PM
#include <linux/pm.h>
#endif
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/arch/dma.h>
#include <asm/arch/aic23.h>
#include <asm/arch/mcbsp.h>
#include <asm/arch/clock.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/memalloc.h>
#include <asm/arch/omap-alsa.h>
#include "omap-alsa-dma.h"
#include "omap-aic23.h"
#undef DEBUG
#ifdef DEBUG
#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__)
#else
#define ADEBUG()
/* nop */
#endif
/* Define to set the AIC23 as the master w.r.t McBSP */
#define AIC23_MASTER
/*
* AUDIO related MACROS
*/
#define DEFAULT_BITPERSAMPLE 16
#define AUDIO_RATE_DEFAULT 44100
#define AUDIO_MCBSP OMAP_MCBSP1
#define NUMBER_SAMPLE_RATES_SUPPORTED 10
MODULE_AUTHOR
(
"Daniel Petrini, David Cohen, Anderson Briglia - INdT"
);
MODULE_AUTHOR
(
"Mika Laitio, Daniel Petrini, David Cohen, Anderson Briglia - INdT"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_DESCRIPTION
(
"OMAP AIC23 driver for ALSA"
);
MODULE_SUPPORTED_DEVICE
(
"{{AIC23,OMAP AIC23}}"
);
MODULE_ALIAS
(
"omap_mcbsp.1"
);
MODULE_DESCRIPTION
(
"OMAP driver for ALSA"
);
MODULE_ALIAS
(
"omap_alsa_mcbsp.1"
);
static
char
*
id
=
NULL
;
MODULE_PARM_DESC
(
id
,
"OMAP OSK ALSA Driver for AIC23 chip."
);
static
struct
snd_card_omap_aic23
*
omap_aic23
=
NULL
;
static
struct
clk
*
aic23_mclk
=
0
;
struct
sample_rate_rate_reg_info
{
u8
control
;
/* SR3, SR2, SR1, SR0 and BOSR */
u8
divider
;
/* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
};
/*
* DAC USB-mode sampling rates (MCLK = 12 MHz)
* The rates and rate_reg_into MUST be in the same order
*/
static
unsigned
int
rates
[]
=
{
4000
,
8000
,
16000
,
22050
,
24000
,
32000
,
44100
,
48000
,
88200
,
96000
,
};
static
const
struct
sample_rate_rate_reg_info
rate_reg_info
[
NUMBER_SAMPLE_RATES_SUPPORTED
]
=
{
{
0x06
,
1
},
/* 4000 */
{
0x06
,
0
},
/* 8000 */
{
0x0C
,
1
},
/* 16000 */
{
0x11
,
1
},
/* 22050 */
{
0x00
,
1
},
/* 24000 */
{
0x0C
,
0
},
/* 32000 */
{
0x11
,
0
},
/* 44100 */
{
0x00
,
0
},
/* 48000 */
{
0x1F
,
0
},
/* 88200 */
{
0x0E
,
0
},
/* 96000 */
};
/*
* mcbsp configuration structure
*/
static
struct
omap_mcbsp_reg_cfg
initial_config_mcbsp
=
{
.
spcr2
=
FREE
|
FRST
|
GRST
|
XRST
|
XINTM
(
3
),
.
spcr1
=
RINTM
(
3
)
|
RRST
,
.
rcr2
=
RPHASE
|
RFRLEN2
(
OMAP_MCBSP_WORD_8
)
|
RWDLEN2
(
OMAP_MCBSP_WORD_16
)
|
RDATDLY
(
0
),
.
rcr1
=
RFRLEN1
(
OMAP_MCBSP_WORD_8
)
|
RWDLEN1
(
OMAP_MCBSP_WORD_16
),
.
xcr2
=
XPHASE
|
XFRLEN2
(
OMAP_MCBSP_WORD_8
)
|
XWDLEN2
(
OMAP_MCBSP_WORD_16
)
|
XDATDLY
(
0
)
|
XFIG
,
.
xcr1
=
XFRLEN1
(
OMAP_MCBSP_WORD_8
)
|
XWDLEN1
(
OMAP_MCBSP_WORD_16
),
.
srgr1
=
FWID
(
DEFAULT_BITPERSAMPLE
-
1
),
.
srgr2
=
GSYNC
|
CLKSP
|
FSGM
|
FPER
(
DEFAULT_BITPERSAMPLE
*
2
-
1
),
#ifndef AIC23_MASTER
/* configure McBSP to be the I2S master */
.
pcr0
=
FSXM
|
FSRM
|
CLKXM
|
CLKRM
|
CLKXP
|
CLKRP
,
#else
/* configure McBSP to be the I2S slave */
.
pcr0
=
CLKXP
|
CLKRP
,
#endif
/* AIC23_MASTER */
};
static
snd_pcm_hw_constraint_list_t
hw_constraints_rates
=
{
.
count
=
ARRAY_SIZE
(
rates
),
.
list
=
rates
,
.
mask
=
0
,
};
static
struct
snd_card_omap_codec
*
alsa_codec
=
NULL
;
static
struct
omap_alsa_codec_config
*
alsa_codec_config
=
NULL
;
/*
* HW interface start and stop helper functions
...
...
@@ -174,142 +76,53 @@ static int audio_ifc_stop(void)
return
0
;
}
/*
* Codec/mcbsp init and configuration section
* codec dependent code.
*/
/*
* Sample rate changing
*/
static
void
omap_aic23_set_samplerate
(
struct
snd_card_omap_aic23
*
omap_aic23
,
long
rate
)
{
u8
count
=
0
;
u16
data
=
0
;
/* Fix the rate if it has a wrong value */
if
(
rate
>=
96000
)
rate
=
96000
;
else
if
(
rate
>=
88200
)
rate
=
88200
;
else
if
(
rate
>=
48000
)
rate
=
48000
;
else
if
(
rate
>=
44100
)
rate
=
44100
;
else
if
(
rate
>=
32000
)
rate
=
32000
;
else
if
(
rate
>=
24000
)
rate
=
24000
;
else
if
(
rate
>=
22050
)
rate
=
22050
;
else
if
(
rate
>=
16000
)
rate
=
16000
;
else
if
(
rate
>=
8000
)
rate
=
8000
;
else
rate
=
4000
;
/* Search for the right sample rate */
/* Verify what happens if the rate is not supported
* now it goes to 96Khz */
while
((
rates
[
count
]
!=
rate
)
&&
(
count
<
(
NUMBER_SAMPLE_RATES_SUPPORTED
-
1
)))
{
count
++
;
}
data
=
(
rate_reg_info
[
count
].
divider
<<
CLKIN_SHIFT
)
|
(
rate_reg_info
[
count
].
control
<<
BOSR_SHIFT
)
|
USB_CLK_ON
;
audio_aic23_write
(
SAMPLE_RATE_CONTROL_ADDR
,
data
);
omap_aic23
->
samplerate
=
rate
;
}
static
inline
void
aic23_configure
(
void
)
{
/* Reset codec */
audio_aic23_write
(
RESET_CONTROL_ADDR
,
0
);
/* Initialize the AIC23 internal state */
/* Analog audio path control, DAC selected, delete INSEL_MIC for line in */
audio_aic23_write
(
ANALOG_AUDIO_CONTROL_ADDR
,
DEFAULT_ANALOG_AUDIO_CONTROL
);
/* Digital audio path control, de-emphasis control 44.1kHz */
audio_aic23_write
(
DIGITAL_AUDIO_CONTROL_ADDR
,
DEEMP_44K
);
/* Digital audio interface, master/slave mode, I2S, 16 bit */
#ifdef AIC23_MASTER
audio_aic23_write
(
DIGITAL_AUDIO_FORMAT_ADDR
,
MS_MASTER
|
IWL_16
|
FOR_DSP
);
#else
audio_aic23_write
(
DIGITAL_AUDIO_FORMAT_ADDR
,
IWL_16
|
FOR_DSP
);
#endif
/* Enable digital interface */
audio_aic23_write
(
DIGITAL_INTERFACE_ACT_ADDR
,
ACT_ON
);
}
static
void
omap_aic23_audio_init
(
struct
snd_card_omap_aic23
*
omap_aic23
)
static
void
omap_alsa_audio_init
(
struct
snd_card_omap_codec
*
omap_alsa
)
{
/* Setup DMA stuff */
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
id
=
"Alsa AIC23
out"
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
stream_id
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
id
=
"Alsa omap
out"
;
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
stream_id
=
SNDRV_PCM_STREAM_PLAYBACK
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
dma_dev
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
dma_dev
=
OMAP_DMA_MCBSP1_TX
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
hw_start
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
hw_start
=
audio_ifc_start
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
hw_stop
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
].
hw_stop
=
audio_ifc_stop
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
id
=
"Alsa AIC23
in"
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
stream_id
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
id
=
"Alsa omap
in"
;
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
stream_id
=
SNDRV_PCM_STREAM_CAPTURE
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
dma_dev
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
dma_dev
=
OMAP_DMA_MCBSP1_RX
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
hw_start
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
hw_start
=
audio_ifc_start
;
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
hw_stop
=
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_CAPTURE
].
hw_stop
=
audio_ifc_stop
;
/* configuring the McBSP */
omap_mcbsp_request
(
AUDIO_MCBSP
);
/* if configured, then stop mcbsp */
omap_mcbsp_stop
(
AUDIO_MCBSP
);
omap_mcbsp_config
(
AUDIO_MCBSP
,
&
initial_config_mcbsp
);
omap_mcbsp_start
(
AUDIO_MCBSP
);
aic23_configure
();
}
/*
* DMA functions
* Depends on omap-a
ic23
-dma.c functions and (omap) dma.c
* Depends on omap-a
lsa
-dma.c functions and (omap) dma.c
*
*/
#define DMA_BUF_SIZE 1024 * 8
static
int
audio_dma_request
(
struct
audio_stream
*
s
,
void
(
*
callback
)
(
void
*
))
{
int
err
;
ADEBUG
();
err
=
omap_request_sound_dma
(
s
->
dma_dev
,
s
->
id
,
s
,
&
s
->
lch
);
err
=
omap_request_
alsa_
sound_dma
(
s
->
dma_dev
,
s
->
id
,
s
,
&
s
->
lch
);
if
(
err
<
0
)
printk
(
KERN_ERR
"unable to grab audio dma 0x%x
\n
"
,
s
->
dma_dev
);
printk
(
KERN_ERR
"Unable to grab audio dma 0x%x
\n
"
,
s
->
dma_dev
);
return
err
;
}
static
int
audio_dma_free
(
struct
audio_stream
*
s
)
{
int
err
=
0
;
ADEBUG
();
err
=
omap_free_sound_dma
(
s
,
&
s
->
lch
);
err
=
omap_free_
alsa_
sound_dma
(
s
,
&
s
->
lch
);
if
(
err
<
0
)
printk
(
KERN_ERR
"Unable to free audio dma channels!
\n
"
);
return
err
;
...
...
@@ -340,7 +153,7 @@ static u_int audio_get_dma_pos(struct audio_stream *s)
/* Now, the position related to the end of that period */
offset
=
bytes_to_frames
(
runtime
,
s
->
offset
)
-
bytes_to_frames
(
runtime
,
count
);
if
(
offset
>=
runtime
->
buffer_size
||
offset
<
0
)
if
(
offset
>=
runtime
->
buffer_size
)
offset
=
0
;
return
offset
;
...
...
@@ -360,9 +173,9 @@ static void audio_stop_dma(struct audio_stream *s)
s
->
periods
=
0
;
/* this stops the dma channel and clears the buffer ptrs */
omap_
audio_stop
_dma
(
s
);
omap_
stop_alsa_sound
_dma
(
s
);
omap_clear_sound_dma
(
s
);
omap_clear_
alsa_
sound_dma
(
s
);
spin_unlock_irqrestore
(
&
s
->
dma_lock
,
flags
);
}
...
...
@@ -377,14 +190,22 @@ static void audio_process_dma(struct audio_stream *s)
unsigned
int
dma_size
;
unsigned
int
offset
;
int
ret
;
#ifdef CONFIG_MACH_OMAP_H6300
unsigned
long
flags
;
#endif
ADEBUG
();
runtime
=
substream
->
runtime
;
if
(
s
->
active
)
{
dma_size
=
frames_to_bytes
(
runtime
,
runtime
->
period_size
);
offset
=
dma_size
*
s
->
period
;
snd_assert
(
dma_size
<=
DMA_BUF_SIZE
,);
ret
=
omap_start_sound_dma
(
s
,
#ifdef CONFIG_MACH_OMAP_H6300
spin_lock_irqsave
(
&
s
->
dma_lock
,
flags
);
omap_stop_alsa_sound_dma
(
s
);
spin_unlock_irqrestore
(
&
s
->
dma_lock
,
flags
);
#endif
ret
=
omap_start_alsa_sound_dma
(
s
,
(
dma_addr_t
)
runtime
->
dma_area
+
offset
,
dma_size
);
if
(
ret
)
{
...
...
@@ -404,10 +225,11 @@ static void audio_process_dma(struct audio_stream *s)
/*
* This is called when dma IRQ occurs at the end of each transmited block
*/
void
audio_dma_callback
(
void
*
data
)
void
callback_omap_alsa_sound_dma
(
void
*
data
)
{
struct
audio_stream
*
s
=
data
;
ADEBUG
();
/*
* If we are getting a callback for an active stream then we inform
* the PCM middle layer we've finished a period
...
...
@@ -416,28 +238,26 @@ void audio_dma_callback(void *data)
snd_pcm_period_elapsed
(
s
->
stream
);
spin_lock
(
&
s
->
dma_lock
);
if
(
s
->
periods
>
0
)
{
if
(
s
->
periods
>
0
)
s
->
periods
--
;
}
audio_process_dma
(
s
);
spin_unlock
(
&
s
->
dma_lock
);
}
/*
* Alsa section
* PCM settings and callbacks
*/
static
int
snd_omap_aic23_trigger
(
snd_pcm_substream_t
*
substream
,
int
cmd
)
static
int
snd_omap_alsa_trigger
(
snd_pcm_substream_t
*
substream
,
int
cmd
)
{
struct
snd_card_omap_
aic23
*
chip
=
struct
snd_card_omap_
codec
*
chip
=
snd_pcm_substream_chip
(
substream
);
int
stream_id
=
substream
->
pstr
->
stream
;
struct
audio_stream
*
s
=
&
chip
->
s
[
stream_id
];
int
err
=
0
;
ADEBUG
();
ADEBUG
();
/* note local interrupts are already disabled in the midlevel code */
spin_lock
(
&
s
->
dma_lock
);
switch
(
cmd
)
{
...
...
@@ -459,15 +279,16 @@ static int snd_omap_aic23_trigger(snd_pcm_substream_t * substream, int cmd)
return
err
;
}
static
int
snd_omap_a
ic23
_prepare
(
snd_pcm_substream_t
*
substream
)
static
int
snd_omap_a
lsa
_prepare
(
snd_pcm_substream_t
*
substream
)
{
struct
snd_card_omap_aic23
*
chip
=
snd_pcm_substream_chip
(
substream
);
struct
snd_card_omap_codec
*
chip
=
snd_pcm_substream_chip
(
substream
);
snd_pcm_runtime_t
*
runtime
=
substream
->
runtime
;
struct
audio_stream
*
s
=
&
chip
->
s
[
substream
->
pstr
->
stream
];
ADEBUG
();
/* set requested samplerate */
omap_aic23_set_samplerate
(
chip
,
runtime
->
rate
);
alsa_codec_config
->
codec_set_samplerate
(
runtime
->
rate
);
chip
->
samplerate
=
runtime
->
rate
;
s
->
period
=
0
;
s
->
periods
=
0
;
...
...
@@ -475,138 +296,88 @@ static int snd_omap_aic23_prepare(snd_pcm_substream_t * substream)
return
0
;
}
static
snd_pcm_uframes_t
snd_omap_aic23_pointer
(
snd_pcm_substream_t
*
substream
)
static
snd_pcm_uframes_t
snd_omap_alsa_pointer
(
snd_pcm_substream_t
*
substream
)
{
struct
snd_card_omap_aic23
*
chip
=
snd_pcm_substream_chip
(
substream
);
struct
snd_card_omap_codec
*
chip
=
snd_pcm_substream_chip
(
substream
);
ADEBUG
();
return
audio_get_dma_pos
(
&
chip
->
s
[
substream
->
pstr
->
stream
]);
}
/* Hardware capabilities */
static
snd_pcm_hardware_t
snd_omap_aic23_capture
=
{
.
info
=
(
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
(
SNDRV_PCM_RATE_8000
|
SNDRV_PCM_RATE_16000
|
SNDRV_PCM_RATE_22050
|
SNDRV_PCM_RATE_32000
|
SNDRV_PCM_RATE_44100
|
SNDRV_PCM_RATE_48000
|
SNDRV_PCM_RATE_88200
|
SNDRV_PCM_RATE_96000
|
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
8000
,
.
rate_max
=
96000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
128
*
1024
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8
*
1024
,
.
periods_min
=
16
,
.
periods_max
=
255
,
.
fifo_size
=
0
,
};
static
snd_pcm_hardware_t
snd_omap_aic23_playback
=
{
.
info
=
(
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_LE
),
.
rates
=
(
SNDRV_PCM_RATE_8000
|
SNDRV_PCM_RATE_16000
|
SNDRV_PCM_RATE_22050
|
SNDRV_PCM_RATE_32000
|
SNDRV_PCM_RATE_44100
|
SNDRV_PCM_RATE_48000
|
SNDRV_PCM_RATE_88200
|
SNDRV_PCM_RATE_96000
|
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
8000
,
.
rate_max
=
96000
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
128
*
1024
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8
*
1024
,
.
periods_min
=
16
,
.
periods_max
=
255
,
.
fifo_size
=
0
,
};
static
int
snd_card_omap_aic23_open
(
snd_pcm_substream_t
*
substream
)
static
int
snd_card_omap_alsa_open
(
snd_pcm_substream_t
*
substream
)
{
struct
snd_card_omap_
aic23
*
chip
=
struct
snd_card_omap_
codec
*
chip
=
snd_pcm_substream_chip
(
substream
);
snd_pcm_runtime_t
*
runtime
=
substream
->
runtime
;
int
stream_id
=
substream
->
pstr
->
stream
;
int
err
;
ADEBUG
();
ADEBUG
();
chip
->
s
[
stream_id
].
stream
=
substream
;
omap_aic23_clock_on
();
alsa_codec_config
->
codec_clock_on
();
if
(
stream_id
==
SNDRV_PCM_STREAM_PLAYBACK
)
runtime
->
hw
=
snd_omap_aic23_playback
;
runtime
->
hw
=
*
(
alsa_codec_config
->
snd_omap_alsa_playback
)
;
else
runtime
->
hw
=
snd_omap_aic23_capture
;
if
((
err
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
))
<
0
)
runtime
->
hw
=
*
(
alsa_codec_config
->
snd_omap_alsa_capture
);
if
((
err
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
))
<
0
)
return
err
;
if
((
err
=
snd_pcm_hw_constraint_list
(
runtime
,
0
,
if
((
err
=
snd_pcm_hw_constraint_list
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_RATE
,
&
hw_constraints_rates
))
<
0
)
alsa_codec_config
->
hw_constraints_rates
))
<
0
)
return
err
;
return
0
;
}
static
int
snd_card_omap_a
ic23
_close
(
snd_pcm_substream_t
*
substream
)
static
int
snd_card_omap_a
lsa
_close
(
snd_pcm_substream_t
*
substream
)
{
struct
snd_card_omap_aic23
*
chip
=
snd_pcm_substream_chip
(
substream
);
ADEBUG
();
struct
snd_card_omap_codec
*
chip
=
snd_pcm_substream_chip
(
substream
);
omap_aic23_clock_off
();
ADEBUG
();
alsa_codec_config
->
codec_clock_off
();
chip
->
s
[
substream
->
pstr
->
stream
].
stream
=
NULL
;
return
0
;
}
/* HW params & free */
static
int
snd_omap_aic23_hw_params
(
snd_pcm_substream_t
*
substream
,
static
int
snd_omap_alsa_hw_params
(
snd_pcm_substream_t
*
substream
,
snd_pcm_hw_params_t
*
hw_params
)
{
return
snd_pcm_lib_malloc_pages
(
substream
,
params_buffer_bytes
(
hw_params
));
}
static
int
snd_omap_a
ic23
_hw_free
(
snd_pcm_substream_t
*
substream
)
static
int
snd_omap_a
lsa
_hw_free
(
snd_pcm_substream_t
*
substream
)
{
return
snd_pcm_lib_free_pages
(
substream
);
}
/* pcm operations */
static
snd_pcm_ops_t
snd_card_omap_aic23_playback_ops
=
{
.
open
=
snd_card_omap_aic23_open
,
.
close
=
snd_card_omap_aic23_close
,
static
snd_pcm_ops_t
snd_card_omap_alsa_playback_ops
=
{
.
open
=
snd_card_omap_alsa_open
,
.
close
=
snd_card_omap_alsa_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
snd_omap_a
ic23
_hw_params
,
.
hw_free
=
snd_omap_a
ic23
_hw_free
,
.
prepare
=
snd_omap_a
ic23
_prepare
,
.
trigger
=
snd_omap_a
ic23
_trigger
,
.
pointer
=
snd_omap_a
ic23
_pointer
,
.
hw_params
=
snd_omap_a
lsa
_hw_params
,
.
hw_free
=
snd_omap_a
lsa
_hw_free
,
.
prepare
=
snd_omap_a
lsa
_prepare
,
.
trigger
=
snd_omap_a
lsa
_trigger
,
.
pointer
=
snd_omap_a
lsa
_pointer
,
};
static
snd_pcm_ops_t
snd_card_omap_a
ic23
_capture_ops
=
{
.
open
=
snd_card_omap_a
ic23
_open
,
.
close
=
snd_card_omap_a
ic23
_close
,
static
snd_pcm_ops_t
snd_card_omap_a
lsa
_capture_ops
=
{
.
open
=
snd_card_omap_a
lsa
_open
,
.
close
=
snd_card_omap_a
lsa
_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
snd_omap_a
ic23
_hw_params
,
.
hw_free
=
snd_omap_a
ic23
_hw_free
,
.
prepare
=
snd_omap_a
ic23
_prepare
,
.
trigger
=
snd_omap_a
ic23
_trigger
,
.
pointer
=
snd_omap_a
ic23
_pointer
,
.
hw_params
=
snd_omap_a
lsa
_hw_params
,
.
hw_free
=
snd_omap_a
lsa
_hw_free
,
.
prepare
=
snd_omap_a
lsa
_prepare
,
.
trigger
=
snd_omap_a
lsa
_trigger
,
.
pointer
=
snd_omap_a
lsa
_pointer
,
};
/*
...
...
@@ -614,16 +385,14 @@ static snd_pcm_ops_t snd_card_omap_aic23_capture_ops = {
*
* Inits pcm alsa structures, allocate the alsa buffer, suspend, resume
*/
static
int
__init
snd_card_omap_a
ic23_pcm
(
struct
snd_card_omap_aic23
*
omap_aic23
,
int
device
)
static
int
__init
snd_card_omap_a
lsa_pcm
(
struct
snd_card_omap_codec
*
omap_alsa
,
int
device
)
{
snd_pcm_t
*
pcm
;
int
err
;
ADEBUG
();
if
((
err
=
snd_pcm_new
(
omap_aic23
->
card
,
"AIC23 PCM"
,
device
,
1
,
1
,
&
pcm
))
<
0
)
ADEBUG
();
if
((
err
=
snd_pcm_new
(
omap_alsa
->
card
,
"OMAP PCM"
,
device
,
1
,
1
,
&
pcm
))
<
0
)
return
err
;
/* sets up initial buffer with continuous allocation */
...
...
@@ -634,98 +403,70 @@ static int __init snd_card_omap_aic23_pcm(struct snd_card_omap_aic23
128
*
1024
,
128
*
1024
);
snd_pcm_set_ops
(
pcm
,
SNDRV_PCM_STREAM_PLAYBACK
,
&
snd_card_omap_a
ic23
_playback_ops
);
&
snd_card_omap_a
lsa
_playback_ops
);
snd_pcm_set_ops
(
pcm
,
SNDRV_PCM_STREAM_CAPTURE
,
&
snd_card_omap_a
ic23
_capture_ops
);
pcm
->
private_data
=
omap_a
ic23
;
&
snd_card_omap_a
lsa
_capture_ops
);
pcm
->
private_data
=
omap_a
lsa
;
pcm
->
info_flags
=
0
;
strcpy
(
pcm
->
name
,
"omap a
ic23
pcm"
);
strcpy
(
pcm
->
name
,
"omap a
lsa
pcm"
);
omap_a
ic23_audio_init
(
omap_aic23
);
omap_a
lsa_audio_init
(
omap_alsa
);
/* setup DMA controller */
audio_dma_request
(
&
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
],
audio_dma_callback
);
audio_dma_request
(
&
omap_a
ic23
->
s
[
SNDRV_PCM_STREAM_CAPTURE
],
audio_dma_callback
);
audio_dma_request
(
&
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
],
callback_omap_alsa_sound_dma
);
audio_dma_request
(
&
omap_a
lsa
->
s
[
SNDRV_PCM_STREAM_CAPTURE
],
callback_omap_alsa_sound_dma
);
omap_a
ic23
->
pcm
=
pcm
;
omap_a
lsa
->
pcm
=
pcm
;
return
0
;
}
#ifdef CONFIG_PM
static
int
snd_omap_aic23_suspend
(
snd_card_t
*
card
,
pm_message_t
state
)
/*
* Driver suspend/resume - calls alsa functions. Some hints from aaci.c
*/
int
snd_omap_alsa_suspend
(
struct
platform_device
*
pdev
,
pm_message_t
state
)
{
struct
snd_card_omap_
aic23
*
chip
=
card
->
private_data
;
ADEBUG
(
);
struct
snd_card_omap_
codec
*
chip
;
snd_card_t
*
card
=
platform_get_drvdata
(
pdev
);
if
(
card
->
power_state
!=
SNDRV_CTL_POWER_D3hot
)
{
chip
=
card
->
private_data
;
if
(
chip
->
card
->
power_state
!=
SNDRV_CTL_POWER_D3hot
)
{
snd_power_change_state
(
chip
->
card
,
SNDRV_CTL_POWER_D3hot
);
snd_pcm_suspend_all
(
chip
->
pcm
);
/* Mutes and turn clock off */
omap_aic23
_clock_off
();
alsa_codec_config
->
codec
_clock_off
();
snd_omap_suspend_mixer
();
}
}
return
0
;
}
/*
* Prepare hardware for resume
*/
static
int
snd_omap_aic23_resume
(
snd_card_t
*
card
)
int
snd_omap_alsa_resume
(
struct
platform_device
*
pdev
)
{
struct
snd_card_omap_
aic23
*
chip
=
card
->
private_data
;
ADEBUG
(
);
struct
snd_card_omap_
codec
*
chip
;
snd_card_t
*
card
=
platform_get_drvdata
(
pdev
);
if
(
card
->
power_state
!=
SNDRV_CTL_POWER_D0
)
{
chip
=
card
->
private_data
;
if
(
chip
->
card
->
power_state
!=
SNDRV_CTL_POWER_D0
)
{
snd_power_change_state
(
chip
->
card
,
SNDRV_CTL_POWER_D0
);
omap_aic23
_clock_on
();
alsa_codec_config
->
codec
_clock_on
();
snd_omap_resume_mixer
();
}
return
0
;
}
/*
* Driver suspend/resume - calls alsa functions. Some hints from aaci.c
*/
static
int
omap_aic23_suspend
(
struct
platform_device
*
pdev
,
pm_message_t
state
)
{
snd_card_t
*
card
=
platform_get_drvdata
(
pdev
);
if
(
card
->
power_state
!=
SNDRV_CTL_POWER_D3hot
)
{
snd_omap_aic23_suspend
(
card
,
PMSG_SUSPEND
);
}
return
0
;
}
static
int
omap_aic23_resume
(
struct
platform_device
*
pdev
)
{
snd_card_t
*
card
=
platform_get_drvdata
(
pdev
);
if
(
card
->
power_state
!=
SNDRV_CTL_POWER_D0
)
{
snd_omap_aic23_resume
(
card
);
}
return
0
;
}
#else
#define snd_omap_aic23_suspend NULL
#define snd_omap_aic23_resume NULL
#define omap_aic23_suspend NULL
#define omap_aic23_resume NULL
#endif
/* CONFIG_PM */
/*
*/
void
snd_omap_aic23_free
(
snd_card_t
*
card
)
void
snd_omap_alsa_free
(
snd_card_t
*
card
)
{
struct
snd_card_omap_
aic23
*
chip
=
card
->
private_data
;
struct
snd_card_omap_
codec
*
chip
=
card
->
private_data
;
ADEBUG
();
/*
...
...
@@ -733,197 +474,106 @@ void snd_omap_aic23_free(snd_card_t * card)
* Can't do it immediately, since it may still have
* buffered data.
*/
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
(
2
);
schedule_timeout_interruptible
(
2
);
omap_mcbsp_stop
(
AUDIO_MCBSP
);
omap_mcbsp_free
(
AUDIO_MCBSP
);
audio_aic23_write
(
RESET_CONTROL_ADDR
,
0
);
audio_aic23_write
(
POWER_DOWN_CONTROL_ADDR
,
0xff
);
audio_dma_free
(
&
chip
->
s
[
SNDRV_PCM_STREAM_PLAYBACK
]);
audio_dma_free
(
&
chip
->
s
[
SNDRV_PCM_STREAM_CAPTURE
]);
}
/*
* Omap MCBSP clock configuration
*
* Here we have some functions that allows clock to be enabled and
* disabled only when needed. Besides doing clock configuration
* it allows turn on/turn off audio when necessary.
*/
#define CODEC_CLOCK 12000000
#define AUDIO_RATE_DEFAULT 44100
/*
* Do clock framework mclk search
*/
static
__init
void
omap_aic23_clock_setup
(
void
)
{
aic23_mclk
=
clk_get
(
0
,
"mclk"
);
}
/*
* Do some sanity check, set clock rate, starts it and
* turn codec audio on
*/
int
omap_aic23_clock_on
(
void
)
{
if
(
clk_get_usecount
(
aic23_mclk
)
>
0
)
{
/* MCLK is already in use */
printk
(
KERN_WARNING
"MCLK in use at %d Hz. We change it to %d Hz
\n
"
,
(
uint
)
clk_get_rate
(
aic23_mclk
),
CODEC_CLOCK
);
}
if
(
clk_set_rate
(
aic23_mclk
,
CODEC_CLOCK
))
{
printk
(
KERN_ERR
"Cannot set MCLK for AIC23 CODEC
\n
"
);
return
-
ECANCELED
;
}
clk_enable
(
aic23_mclk
);
printk
(
KERN_DEBUG
"MCLK = %d [%d], usecount = %d
\n
"
,
(
uint
)
clk_get_rate
(
aic23_mclk
),
CODEC_CLOCK
,
clk_get_usecount
(
aic23_mclk
));
/* Now turn the audio on */
audio_aic23_write
(
POWER_DOWN_CONTROL_ADDR
,
~
DEVICE_POWER_OFF
&
~
OUT_OFF
&
~
DAC_OFF
&
~
ADC_OFF
&
~
MIC_OFF
&
~
LINE_OFF
);
return
0
;
}
/*
* Do some sanity check, turn clock off and then turn
* codec audio off
*/
int
omap_aic23_clock_off
(
void
)
{
if
(
clk_get_usecount
(
aic23_mclk
)
>
0
)
{
if
(
clk_get_rate
(
aic23_mclk
)
!=
CODEC_CLOCK
)
{
printk
(
KERN_WARNING
"MCLK for audio should be %d Hz. But is %d Hz
\n
"
,
(
uint
)
clk_get_rate
(
aic23_mclk
),
CODEC_CLOCK
);
}
clk_disable
(
aic23_mclk
);
}
audio_aic23_write
(
POWER_DOWN_CONTROL_ADDR
,
DEVICE_POWER_OFF
|
OUT_OFF
|
DAC_OFF
|
ADC_OFF
|
MIC_OFF
|
LINE_OFF
);
return
0
;
}
/* module init & exit */
/*
* Inits alsa soudcard structure
* Inits alsa soudcard structure.
* Called by the probe method in codec after function pointers has been set.
*/
static
int
__init
snd_omap_aic23_probe
(
struct
platform_device
*
pdev
)
int
snd_omap_alsa_post_probe
(
struct
platform_device
*
pdev
,
struct
omap_alsa_codec_config
*
config
)
{
int
err
=
0
;
int
def_rate
;
snd_card_t
*
card
;
ADEBUG
();
alsa_codec_config
=
config
;
alsa_codec_config
->
codec_clock_setup
();
alsa_codec_config
->
codec_clock_on
();
omap_mcbsp_request
(
AUDIO_MCBSP
);
omap_mcbsp_stop
(
AUDIO_MCBSP
);
omap_mcbsp_config
(
AUDIO_MCBSP
,
alsa_codec_config
->
mcbsp_regs_alsa
);
omap_mcbsp_start
(
AUDIO_MCBSP
);
if
(
alsa_codec_config
&&
alsa_codec_config
->
codec_configure_dev
)
alsa_codec_config
->
codec_configure_dev
();
/* gets clock from clock framework */
omap_aic23_clock_setup
();
alsa_codec_config
->
codec_clock_off
();
/* register the soundcard */
card
=
snd_card_new
(
-
1
,
id
,
THIS_MODULE
,
sizeof
(
omap_aic23
));
card
=
snd_card_new
(
-
1
,
id
,
THIS_MODULE
,
sizeof
(
alsa_codec
));
if
(
card
==
NULL
)
return
-
ENOMEM
;
goto
nodev1
;
omap_aic23
=
kcalloc
(
1
,
sizeof
(
*
omap_aic23
),
GFP_KERNEL
);
if
(
omap_aic23
==
NULL
)
return
-
ENOMEM
;
alsa_codec
=
kcalloc
(
1
,
sizeof
(
*
alsa_codec
),
GFP_KERNEL
);
if
(
alsa_codec
==
NULL
)
goto
nodev2
;
card
->
private_data
=
(
void
*
)
omap_aic23
;
card
->
private_free
=
snd_omap_a
ic23
_free
;
card
->
private_data
=
(
void
*
)
alsa_codec
;
card
->
private_free
=
snd_omap_a
lsa
_free
;
omap_aic23
->
card
=
card
;
omap_aic23
->
samplerate
=
AUDIO_RATE_DEFAULT
;
alsa_codec
->
card
=
card
;
def_rate
=
alsa_codec_config
->
get_default_samplerate
();
alsa_codec
->
samplerate
=
def_rate
;
spin_lock_init
(
&
omap_aic23
->
s
[
0
].
dma_lock
);
spin_lock_init
(
&
omap_aic23
->
s
[
1
].
dma_lock
);
spin_lock_init
(
&
alsa_codec
->
s
[
0
].
dma_lock
);
spin_lock_init
(
&
alsa_codec
->
s
[
1
].
dma_lock
);
/* mixer */
if
((
err
=
snd_omap_mixer
(
omap_aic23
))
<
0
)
goto
nodev
;
if
((
err
=
snd_omap_mixer
(
alsa_codec
))
<
0
)
goto
nodev
3
;
/* PCM */
if
((
err
=
snd_card_omap_a
ic23_pcm
(
omap_aic23
,
0
))
<
0
)
goto
nodev
;
if
((
err
=
snd_card_omap_a
lsa_pcm
(
alsa_codec
,
0
))
<
0
)
goto
nodev
3
;
strcpy
(
card
->
driver
,
"
AIC23
"
);
strcpy
(
card
->
shortname
,
"OSK AIC23"
);
sprintf
(
card
->
longname
,
"OMAP OSK with AIC23"
);
strcpy
(
card
->
driver
,
"
OMAP_ALSA
"
);
strcpy
(
card
->
shortname
,
alsa_codec_config
->
name
);
sprintf
(
card
->
longname
,
alsa_codec_config
->
name
);
snd_omap_init_mixer
();
snd_card_set_dev
(
card
,
&
pdev
->
dev
);
if
((
err
=
snd_card_register
(
card
))
==
0
)
{
printk
(
KERN_INFO
"
OSK
audio support initialized
\n
"
);
printk
(
KERN_INFO
"audio support initialized
\n
"
);
platform_set_drvdata
(
pdev
,
card
);
return
0
;
}
nodev:
snd_omap_aic23_free
(
card
);
nodev3:
kfree
(
alsa_codec
);
nodev2:
snd_card_free
(
card
);
nodev1:
omap_mcbsp_stop
(
AUDIO_MCBSP
);
omap_mcbsp_free
(
AUDIO_MCBSP
);
return
err
;
}
static
int
snd_omap_aic23
_remove
(
struct
platform_device
*
pdev
)
int
snd_omap_alsa
_remove
(
struct
platform_device
*
pdev
)
{
snd_card_t
*
card
=
platform_get_drvdata
(
pdev
);
struct
snd_card_omap_
aic23
*
chip
=
card
->
private_data
;
struct
snd_card_omap_
codec
*
chip
=
card
->
private_data
;
snd_card_free
(
card
);
omap_aic23
=
NULL
;
alsa_codec
=
NULL
;
card
->
private_data
=
NULL
;
kfree
(
chip
);
platform_set_drvdata
(
pdev
,
NULL
);
return
0
;
}
static
struct
platform_driver
omap_alsa_driver
=
{
.
probe
=
snd_omap_aic23_probe
,
.
remove
=
snd_omap_aic23_remove
,
.
suspend
=
omap_aic23_suspend
,
.
resume
=
omap_aic23_resume
,
.
driver
=
{
.
name
=
"omap_mcbsp"
,
},
};
static
int
__init
omap_aic23_init
(
void
)
{
int
err
;
ADEBUG
();
err
=
platform_driver_register
(
&
omap_alsa_driver
);
return
err
;
}
static
void
__exit
omap_aic23_exit
(
void
)
{
ADEBUG
();
platform_driver_unregister
(
&
omap_alsa_driver
);
}
module_init
(
omap_aic23_init
);
module_exit
(
omap_aic23_exit
);
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