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
ac67e5b6
Commit
ac67e5b6
authored
Nov 07, 2008
by
Kevin Hilman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DaVinci: remove obsolete/unmaintained OSS driver
Signed-off-by:
Kevin Hilman
<
khilman@deeprootsystems.com
>
parent
3ccd1e4e
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
0 additions
and
4227 deletions
+0
-4227
arch/arm/mach-davinci/Kconfig
arch/arm/mach-davinci/Kconfig
+0
-8
arch/arm/mach-davinci/Makefile
arch/arm/mach-davinci/Makefile
+0
-1
arch/arm/mach-davinci/mcbsp.c
arch/arm/mach-davinci/mcbsp.c
+0
-519
sound/oss/Kconfig
sound/oss/Kconfig
+0
-32
sound/oss/davinci-aic33.h
sound/oss/davinci-aic33.h
+0
-246
sound/oss/davinci-audio-aic33.c
sound/oss/davinci-audio-aic33.c
+0
-1066
sound/oss/davinci-audio-dma-intfc.c
sound/oss/davinci-audio-dma-intfc.c
+0
-1018
sound/oss/davinci-audio-dma-intfc.h
sound/oss/davinci-audio-dma-intfc.h
+0
-63
sound/oss/davinci-audio.c
sound/oss/davinci-audio.c
+0
-1159
sound/oss/davinci-audio.h
sound/oss/davinci-audio.h
+0
-115
No files found.
arch/arm/mach-davinci/Kconfig
View file @
ac67e5b6
...
...
@@ -18,14 +18,6 @@ config MACH_DAVINCI_EVM
Configure this option to specify the whether the board used
for development is a DaVinci EVM
config DAVINCI_MCBSP
bool
prompt "DaVinci McBSP Driver" if SOUND_DAVINCI=n
depends on ARCH_DAVINCI
default SOUND_DAVINCI
---help---
DaVinci McBSP driver. Auto-enabled by DaVinci sound driver.
config DAVINCI_RESET_CLOCKS
bool "Reset unused clocks during boot"
depends on ARCH_DAVINCI
...
...
arch/arm/mach-davinci/Makefile
View file @
ac67e5b6
...
...
@@ -9,4 +9,3 @@ obj-y := time.o irq.o clock.o serial.o io.o id.o psc.o \
# Board specific
obj-$(CONFIG_MACH_DAVINCI_EVM)
+=
board-evm.o
obj-$(CONFIG_DAVINCI_MCBSP)
+=
mcbsp.o
arch/arm/mach-davinci/mcbsp.c
deleted
100644 → 0
View file @
3ccd1e4e
/*
* arch/arm/mach-davinci/mcbsp.c
*
* Copyright (C) 2004 Nokia Corporation
* Author: Samuel Ortiz <samuel.ortiz@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Multichannel mode not supported.
*
* 2005-10-01 Rishi Bhattacharya / Sharath Kumar - Modified to support TI
* Davinci DM644x processor
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <asm/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/memory.h>
#include <mach/edma.h>
#include <mach/irqs.h>
#include <mach/mcbsp.h>
struct
clk
*
mbspclk
=
NULL
;
/* #define CONFIG_MCBSP_DEBUG */
#ifdef CONFIG_MCBSP_DEBUG
#define DBG(x...) printk(KERN_INFO x)
#else
#define DBG(x...) do { } while (0)
#endif
struct
davinci_mcbsp
{
u32
io_base
;
u8
id
;
u8
free
;
davinci_mcbsp_word_length
rx_word_length
;
davinci_mcbsp_word_length
tx_word_length
;
/* IRQ based TX/RX */
int
rx_irq
;
int
tx_irq
;
/* DMA stuff */
u8
dma_rx_sync
;
short
dma_rx_lch
;
u8
dma_tx_sync
;
short
dma_tx_lch
;
/* Completion queues */
struct
completion
tx_irq_completion
;
struct
completion
rx_irq_completion
;
struct
completion
tx_dma_completion
;
struct
completion
rx_dma_completion
;
spinlock_t
lock
;
};
static
struct
davinci_mcbsp
mcbsp
[
DAVINCI_MAX_MCBSP_COUNT
];
unsigned
short
DAVINCI_MCBSP_READ
(
int
base
,
int
reg
)
{
unsigned
long
data
,
temp
,
*
p
=
(
unsigned
long
*
)(
base
+
reg
);
temp
=
(
unsigned
long
)
p
;
if
(
temp
&
0x2
)
{
/*non word offset */
temp
&=
0xfffffffc
;
p
=
(
unsigned
long
*
)
temp
;
data
=
inl
(
p
);
return
(
unsigned
short
)(
data
>>
16
);
}
else
{
/*word offset */
return
((
unsigned
short
)
inl
(
p
));
}
}
void
DAVINCI_MCBSP_WRITE
(
int
base
,
int
reg
,
unsigned
short
val
)
{
unsigned
long
data
,
temp
,
*
p
=
(
unsigned
long
*
)(
base
+
reg
);
temp
=
(
unsigned
long
)
p
;
if
(
temp
&
0x2
)
{
/*non word offset */
temp
&=
0xfffffffc
;
p
=
(
unsigned
long
*
)
temp
;
data
=
inl
(
p
);
data
&=
0x0000ffff
;
data
|=
((
unsigned
long
)
val
<<
16
);
outl
(
data
,
p
);
}
else
{
/*word offset */
data
=
inl
(
p
);
data
&=
0xffff0000
;
data
|=
val
;
outl
(
data
,
p
);
}
}
static
void
davinci_mcbsp_dump_reg
(
u8
id
)
{
DBG
(
"**** MCBSP%d regs ****
\n
"
,
mcbsp
[
id
].
id
);
DBG
(
"SPCR2: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
SPCR2
));
DBG
(
"SPCR1: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
SPCR1
));
DBG
(
"RCR2: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
RCR2
));
DBG
(
"RCR1: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
RCR1
));
DBG
(
"XCR2: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
XCR2
));
DBG
(
"XCR1: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
XCR1
));
DBG
(
"SRGR2: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
SRGR2
));
DBG
(
"SRGR1: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
SRGR1
));
DBG
(
"PCR0: 0x%04x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp
[
id
].
io_base
,
PCR0
));
DBG
(
"***********************
\n
"
);
}
static
void
davinci_mcbsp_tx_dma_callback
(
int
lch
,
u16
ch_status
,
void
*
data
)
{
struct
davinci_mcbsp
*
mcbsp_dma_tx
=
(
struct
davinci_mcbsp
*
)(
data
);
DBG
(
"TX DMA callback : 0x%x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp_dma_tx
->
io_base
,
SPCR2
));
/* We can free the channels */
DBG
(
"mcbsp_dma_tx->dma_tx_lch = %d
\n
"
,
mcbsp_dma_tx
->
dma_tx_lch
);
davinci_stop_dma
(
mcbsp_dma_tx
->
dma_tx_lch
);
davinci_free_dma
(
mcbsp_dma_tx
->
dma_tx_lch
);
mcbsp_dma_tx
->
dma_tx_lch
=
-
1
;
complete
(
&
mcbsp_dma_tx
->
tx_dma_completion
);
}
static
void
davinci_mcbsp_rx_dma_callback
(
int
lch
,
u16
ch_status
,
void
*
data
)
{
struct
davinci_mcbsp
*
mcbsp_dma_rx
=
(
struct
davinci_mcbsp
*
)(
data
);
DBG
(
"RX DMA callback : 0x%x
\n
"
,
DAVINCI_MCBSP_READ
(
mcbsp_dma_rx
->
io_base
,
SPCR2
));
/* We can free the channels */
davinci_free_dma
(
mcbsp_dma_rx
->
dma_rx_lch
);
mcbsp_dma_rx
->
dma_rx_lch
=
-
1
;
complete
(
&
mcbsp_dma_rx
->
rx_dma_completion
);
}
/*
* davinci_mcbsp_config simply write a config to the
* appropriate McBSP.
* You either call this function or set the McBSP registers
* by yourself before calling davinci_mcbsp_start().
*/
void
davinci_mcbsp_config
(
unsigned
int
id
,
const
struct
davinci_mcbsp_reg_cfg
*
config
)
{
u32
io_base
=
mcbsp
[
id
].
io_base
;
DBG
(
"davinci-McBSP: McBSP%d io_base: 0x%8x
\n
"
,
id
+
1
,
io_base
);
DAVINCI_MCBSP_WRITE
(
io_base
,
PCR0
,
config
->
pcr0
);
DAVINCI_MCBSP_WRITE
(
io_base
,
RCR2
,
config
->
rcr2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
RCR1
,
config
->
rcr1
);
DAVINCI_MCBSP_WRITE
(
io_base
,
XCR2
,
config
->
xcr2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
XCR1
,
config
->
xcr1
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SRGR2
,
config
->
srgr2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SRGR1
,
config
->
srgr1
);
/* We write the given config */
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
config
->
spcr2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR1
,
config
->
spcr1
);
}
static
int
davinci_mcbsp_check
(
unsigned
int
id
)
{
if
(
id
>
DAVINCI_MAX_MCBSP_COUNT
-
1
)
{
DBG
(
"DAVINCI-McBSP: McBSP%d doesn't exist
\n
"
,
id
+
1
);
return
-
1
;
}
return
0
;
}
int
davinci_mcbsp_request
(
unsigned
int
id
)
{
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
-
EINVAL
;
spin_lock
(
&
mcbsp
[
id
].
lock
);
if
(
!
mcbsp
[
id
].
free
)
{
DBG
(
"DAVINCI-McBSP: McBSP%d is currently in use
\n
"
,
id
+
1
);
spin_unlock
(
&
mcbsp
[
id
].
lock
);
return
-
1
;
}
mcbsp
[
id
].
free
=
0
;
spin_unlock
(
&
mcbsp
[
id
].
lock
);
return
0
;
}
void
davinci_mcbsp_free
(
unsigned
int
id
)
{
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
;
spin_lock
(
&
mcbsp
[
id
].
lock
);
if
(
mcbsp
[
id
].
free
)
{
DBG
(
"DAVINCI-McBSP: McBSP%d was not reserved
\n
"
,
id
+
1
);
spin_unlock
(
&
mcbsp
[
id
].
lock
);
return
;
}
mcbsp
[
id
].
free
=
1
;
spin_unlock
(
&
mcbsp
[
id
].
lock
);
return
;
}
/*
* Here we start the McBSP, by enabling the sample
* generator, both transmitter and receivers,
* and the frame sync.
*/
void
davinci_mcbsp_start
(
unsigned
int
id
)
{
u32
io_base
;
u16
w
;
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
;
io_base
=
mcbsp
[
id
].
io_base
;
mcbsp
[
id
].
rx_word_length
=
((
DAVINCI_MCBSP_READ
(
io_base
,
RCR1
)
>>
5
)
&
0x7
);
mcbsp
[
id
].
tx_word_length
=
((
DAVINCI_MCBSP_READ
(
io_base
,
XCR1
)
>>
5
)
&
0x7
);
/* Start the sample generator */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
w
|
(
1
<<
6
));
/* Enable transmitter and receiver */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
w
|
1
);
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR1
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR1
,
w
|
1
);
udelay
(
100
);
/* Start frame sync */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
w
|
(
1
<<
7
));
/* Dump mcbsp reg */
davinci_mcbsp_dump_reg
(
id
);
}
void
davinci_mcbsp_stop
(
unsigned
int
id
)
{
u32
io_base
;
u16
w
;
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
;
io_base
=
mcbsp
[
id
].
io_base
;
/* Reset transmitter */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
w
&
~
(
1
));
/* Reset receiver */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR1
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR1
,
w
&
~
(
1
));
/* Reset the sample rate generator */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
w
&
~
(
1
<<
6
));
/* Reset the frame sync generator */
w
=
DAVINCI_MCBSP_READ
(
io_base
,
SPCR2
);
DAVINCI_MCBSP_WRITE
(
io_base
,
SPCR2
,
w
&
~
(
1
<<
7
));
}
/*
* IRQ based word transmission.
*/
void
davinci_mcbsp_xmit_word
(
unsigned
int
id
,
u32
word
)
{
u32
io_base
;
davinci_mcbsp_word_length
word_length
=
mcbsp
[
id
].
tx_word_length
;
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
;
io_base
=
mcbsp
[
id
].
io_base
;
if
(
word_length
>
DAVINCI_MCBSP_WORD_16
)
DAVINCI_MCBSP_WRITE
(
io_base
,
DXR2
,
word
>>
16
);
DAVINCI_MCBSP_WRITE
(
io_base
,
DXR1
,
word
&
0xffff
);
}
u32
davinci_mcbsp_recv_word
(
unsigned
int
id
)
{
u32
io_base
;
u16
word_lsb
,
word_msb
=
0
;
davinci_mcbsp_word_length
word_length
=
mcbsp
[
id
].
rx_word_length
;
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
-
EINVAL
;
io_base
=
mcbsp
[
id
].
io_base
;
if
(
word_length
>
DAVINCI_MCBSP_WORD_16
)
word_msb
=
DAVINCI_MCBSP_READ
(
io_base
,
DRR2
);
word_lsb
=
DAVINCI_MCBSP_READ
(
io_base
,
DRR1
);
return
(
word_lsb
|
(
word_msb
<<
16
));
}
/*
* Simple DMA based buffer rx/tx routines.
* Nothing fancy, just a single buffer tx/rx through DMA.
* The DMA resources are released once the transfer is done.
* For anything fancier, you should use your own customized DMA
* routines and callbacks.
*/
int
davinci_mcbsp_xmit_buffer
(
unsigned
int
id
,
dma_addr_t
buffer
,
unsigned
int
length
)
{
int
dma_tx_ch
;
int
tcc
;
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
-
EINVAL
;
if
(
davinci_request_dma
(
mcbsp
[
id
].
dma_tx_sync
,
"McBSP TX"
,
davinci_mcbsp_tx_dma_callback
,
&
mcbsp
[
id
],
&
dma_tx_ch
,
&
tcc
,
EVENTQ_0
))
{
DBG
(
"DAVINCI-McBSP: Unable to request DMA channel for McBSP%d TX."
"Trying IRQ based TX
\n
"
,
id
+
1
);
return
-
EAGAIN
;
}
mcbsp
[
id
].
dma_tx_lch
=
dma_tx_ch
;
DBG
(
"TX DMA on channel %d
\n
"
,
dma_tx_ch
);
init_completion
(
&
(
mcbsp
[
id
].
tx_dma_completion
));
davinci_set_dma_transfer_params
(
mcbsp
[
id
].
dma_tx_lch
,
2
,
length
/
2
,
1
,
0
,
ASYNC
);
davinci_set_dma_dest_params
(
mcbsp
[
id
].
dma_tx_lch
,
(
unsigned
long
)
0x01E02004
,
0
,
0
);
davinci_set_dma_src_params
(
mcbsp
[
id
].
dma_tx_lch
,
(
buffer
),
0
,
0
);
davinci_set_dma_src_index
(
mcbsp
[
id
].
dma_tx_lch
,
2
,
0
);
davinci_set_dma_dest_index
(
mcbsp
[
id
].
dma_tx_lch
,
0
,
0
);
davinci_start_dma
(
mcbsp
[
id
].
dma_tx_lch
);
wait_for_completion
(
&
(
mcbsp
[
id
].
tx_dma_completion
));
return
0
;
}
int
davinci_mcbsp_recv_buffer
(
unsigned
int
id
,
dma_addr_t
buffer
,
unsigned
int
length
)
{
int
dma_rx_ch
;
int
tcc
;
if
(
davinci_mcbsp_check
(
id
)
<
0
)
return
-
EINVAL
;
if
(
davinci_request_dma
(
mcbsp
[
id
].
dma_rx_sync
,
"McBSP RX"
,
davinci_mcbsp_rx_dma_callback
,
&
mcbsp
[
id
],
&
dma_rx_ch
,
&
tcc
,
EVENTQ_0
))
{
DBG
(
"Unable to request DMA channel for McBSP%d RX."
"Trying IRQ based RX
\n
"
,
id
+
1
);
return
-
EAGAIN
;
}
mcbsp
[
id
].
dma_rx_lch
=
dma_rx_ch
;
DBG
(
"RX DMA on channel %d
\n
"
,
dma_rx_ch
);
init_completion
(
&
(
mcbsp
[
id
].
rx_dma_completion
));
davinci_set_dma_transfer_params
(
mcbsp
[
id
].
dma_rx_lch
,
2
,
length
/
2
,
1
,
0
,
ASYNC
);
davinci_set_dma_src_params
(
mcbsp
[
id
].
dma_rx_lch
,
(
unsigned
long
)
0x01E02000
,
0
,
0
);
davinci_set_dma_dest_params
(
mcbsp
[
id
].
dma_rx_lch
,
(
unsigned
long
)
virt_to_phys
((
void
*
)
buffer
),
0
,
0
);
davinci_set_dma_src_index
(
mcbsp
[
id
].
dma_rx_lch
,
0
,
0
);
davinci_set_dma_dest_index
(
mcbsp
[
id
].
dma_rx_lch
,
2
,
0
);
davinci_start_dma
(
mcbsp
[
id
].
dma_rx_lch
);
wait_for_completion
(
&
(
mcbsp
[
id
].
rx_dma_completion
));
return
0
;
}
struct
clk
*
davinci_mcbsp_get_clock
(
void
)
{
return
mbspclk
;
}
struct
davinci_mcbsp_info
{
u32
virt_base
;
u8
dma_rx_sync
,
dma_tx_sync
;
u16
rx_irq
,
tx_irq
;
};
static
const
struct
davinci_mcbsp_info
mcbsp_davinci
[]
=
{
[
0
]
=
{
.
virt_base
=
IO_ADDRESS
(
DAVINCI_MCBSP1_BASE
),
.
dma_rx_sync
=
DAVINCI_DMA_MCBSP1_RX
,
.
dma_tx_sync
=
DAVINCI_DMA_MCBSP1_TX
,
.
rx_irq
=
DAVINCI_McBSP1RX
,
.
tx_irq
=
DAVINCI_McBSP1TX
},
};
static
int
__init
davinci_mcbsp_init
(
void
)
{
int
mcbsp_count
=
0
,
i
;
static
const
struct
davinci_mcbsp_info
*
mcbsp_info
;
struct
clk
*
clkp
;
printk
(
KERN_INFO
"Initializing DaVinci McBSP system
\n
"
);
clkp
=
clk_get
(
NULL
,
"McBSPCLK"
);
if
(
IS_ERR
(
clkp
))
{
printk
(
KERN_ERR
"mcbsp: could not acquire McBSP clk
\n
"
);
return
PTR_ERR
(
clkp
);
}
else
{
mbspclk
=
clkp
;
clk_enable
(
mbspclk
);
mcbsp_info
=
mcbsp_davinci
;
mcbsp_count
=
ARRAY_SIZE
(
mcbsp_davinci
);
for
(
i
=
0
;
i
<
DAVINCI_MAX_MCBSP_COUNT
;
i
++
)
{
if
(
i
>=
mcbsp_count
)
{
mcbsp
[
i
].
io_base
=
0
;
mcbsp
[
i
].
free
=
0
;
continue
;
}
mcbsp
[
i
].
id
=
i
+
1
;
mcbsp
[
i
].
free
=
1
;
mcbsp
[
i
].
dma_tx_lch
=
-
1
;
mcbsp
[
i
].
dma_rx_lch
=
-
1
;
mcbsp
[
i
].
io_base
=
mcbsp_info
[
i
].
virt_base
;
mcbsp
[
i
].
tx_irq
=
mcbsp_info
[
i
].
tx_irq
;
mcbsp
[
i
].
rx_irq
=
mcbsp_info
[
i
].
rx_irq
;
mcbsp
[
i
].
dma_rx_sync
=
mcbsp_info
[
i
].
dma_rx_sync
;
mcbsp
[
i
].
dma_tx_sync
=
mcbsp_info
[
i
].
dma_tx_sync
;
spin_lock_init
(
&
mcbsp
[
i
].
lock
);
}
}
return
0
;
}
static
void
__exit
davinci_mcbsp_exit
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
DAVINCI_MAX_MCBSP_COUNT
;
i
++
)
{
mcbsp
[
i
].
free
=
0
;
mcbsp
[
i
].
dma_tx_lch
=
-
1
;
mcbsp
[
i
].
dma_rx_lch
=
-
1
;
}
clk_disable
(
mbspclk
);
return
;
}
module_init
(
davinci_mcbsp_init
);
module_exit
(
davinci_mcbsp_exit
);
EXPORT_SYMBOL
(
davinci_mcbsp_config
);
EXPORT_SYMBOL
(
davinci_mcbsp_request
);
EXPORT_SYMBOL
(
davinci_mcbsp_free
);
EXPORT_SYMBOL
(
davinci_mcbsp_start
);
EXPORT_SYMBOL
(
davinci_mcbsp_stop
);
EXPORT_SYMBOL
(
davinci_mcbsp_xmit_word
);
EXPORT_SYMBOL
(
davinci_mcbsp_recv_word
);
EXPORT_SYMBOL
(
davinci_mcbsp_xmit_buffer
);
EXPORT_SYMBOL
(
davinci_mcbsp_recv_buffer
);
EXPORT_SYMBOL
(
davinci_mcbsp_get_clock
);
MODULE_AUTHOR
(
"Texas Instruments"
);
MODULE_DESCRIPTION
(
"McBSP driver for DaVinci."
);
MODULE_LICENSE
(
"GPL"
);
sound/oss/Kconfig
View file @
ac67e5b6
...
...
@@ -567,35 +567,3 @@ config SOUND_SH_DAC_AUDIO_CHANNEL
int "DAC channel"
default "1"
depends on SOUND_SH_DAC_AUDIO
config SOUND_DAVINCI
tristate "DaVinci Sound Driver"
depends on ARCH_DAVINCI && SOUND_PRIME!=n && SOUND
---help---
DaVinci Sound driver
config SOUND_DAVINCI_AIC33
tristate "AIC33 Stereo Codec"
depends on SOUND_DAVINCI
select SENSORS_TLV320AIC33
---help---
If you say yes here you get support for the I2C control
interface for Texas Instruments TLV320AIC33 audio codec.
menu "DaVinci Audio Options"
depends on SOUND_DAVINCI
choice
prompt "Mono/Stereo Jack Support"
default MONOSTEREO_SAMEJACK
config MONOSTEREO_DIFFJACK
bool "Mono and Stereo on different jacks"
config MONOSTEREO_SAMEJACK
bool "Mono and Stereo on same jacks"
endchoice
endmenu
sound/oss/davinci-aic33.h
deleted
100644 → 0
View file @
3ccd1e4e
/*
* linux/sound/oss/davinci-aic33.h
*
* Glue driver for AIC33 for Davinci processors
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
* -------
* 2005-10-18 Rishi Bhattacharya - Support for AIC33 codec and Davinci DM644x Processor
*/
#ifndef __ASM_ARCH_AIC33_H
#define __ASM_ARCH_AIC33_H
/* Codec TLV320AIC33 */
#define REGISTER_ADDR0 0x00
#define REGISTER_ADDR1 0x01
#define REGISTER_ADDR2 0x02
#define REGISTER_ADDR3 0x03
#define REGISTER_ADDR4 0x04
#define REGISTER_ADDR5 0x05
#define REGISTER_ADDR6 0x06
#define REGISTER_ADDR7 0x07
#define REGISTER_ADDR8 0x08
#define REGISTER_ADDR9 0x09
#define REGISTER_ADDR10 0x0A
#define REGISTER_ADDR11 0x0B
#define REGISTER_ADDR12 0x0C
#define REGISTER_ADDR15 0x0F
#define REGISTER_ADDR16 0x10
#define REGISTER_ADDR17 0x11
#define REGISTER_ADDR18 0x12
#define REGISTER_ADDR19 0x13
#define REGISTER_ADDR20 0x14
#define REGISTER_ADDR21 0x15
#define REGISTER_ADDR22 0x16
#define REGISTER_ADDR23 0x17
#define REGISTER_ADDR24 0x18
#define REGISTER_ADDR25 0x19
#define REGISTER_ADDR26 0x1A
#define REGISTER_ADDR27 0x1B
#define REGISTER_ADDR28 0x1C
#define REGISTER_ADDR29 0x1D
#define REGISTER_ADDR30 0x1E
#define REGISTER_ADDR31 0x1F
#define REGISTER_ADDR32 0x20
#define REGISTER_ADDR33 0x21
#define REGISTER_ADDR37 0x25
#define REGISTER_ADDR38 0x26
#define REGISTER_ADDR40 0x28
#define REGISTER_ADDR41 0x29
#define REGISTER_ADDR43 0x2B
#define REGISTER_ADDR44 0x2C
#define REGISTER_ADDR45 0x2D
#define REGISTER_ADDR46 0x2E
#define REGISTER_ADDR47 0x2F
#define REGISTER_ADDR51 0x33
#define REGISTER_ADDR58 0x3A
#define REGISTER_ADDR64 0x40
#define REGISTER_ADDR65 0x41
#define REGISTER_ADDR73 0x49
#define REGISTER_ADDR74 0x4A
#define REGISTER_ADDR75 0x4B
#define REGISTER_ADDR76 0x4C
#define REGISTER_ADDR77 0x4D
#define REGISTER_ADDR78 0x4E
#define REGISTER_ADDR79 0x4F
#define REGISTER_ADDR80 0x50
#define REGISTER_ADDR81 0x51
#define REGISTER_ADDR82 0x52
#define REGISTER_ADDR83 0x53
#define REGISTER_ADDR84 0x54
#define REGISTER_ADDR85 0x55
#define REGISTER_ADDR86 0x56
#define REGISTER_ADDR87 0x57
#define REGISTER_ADDR88 0x58
#define REGISTER_ADDR89 0x59
#define REGISTER_ADDR90 0x5A
#define REGISTER_ADDR91 0x5B
#define REGISTER_ADDR92 0x5C
#define REGISTER_ADDR93 0x5D
#define REGISTER_ADDR94 0x5E
// Page Select register 0
#define PAGE_SELECT0 0
#define PAGE_SELECT1 1
// Software reset register 1
#define SOFT_RESET 0x80
// Codec sample rate select register 2
#define ADC_FS_MAX 0xA0
#define ADC_FS_MIN 0x00
#define DAC_FS_MAX 0x0A
#define DAC_FS_MIN 0x00
// PLL Programming registerA 3
#define PLL_ENABLE 0x80
// Codec Datapath setup register 7
#define FS_REF_44_1 0x80
#define FS_REF_DEFAULT_48 0x00
#define ADC_DUAL_RATE_MODE 0x40
#define DAC_DUAL_RATE_MODE 0x20
#define LDAC_LCHAN 0x08
#define LDAC_RCHAN 0x10
#define LDAC_MONO_MIX 0x18
#define RDAC_RCHAN 0x02
#define RDAC_LCHAN 0x04
#define RDAC_MONO_MIX 0x06
//Audio serial data interface control registerA 8
#define BIT_CLK_MASTER 0x80
#define WORD_CLK_MASTER 0x40
#define DOUT_TRI_STATE 0x20
#define CLK_TRANS_MASTER 0x10
#define ENABLE_3D 0x04
#define DM_ENABLE_128 0x01
#define DM_ENABLE_64 0x02
#define DM_ENABLE_32 0x03
//Audio serial data interface control registerB 9
#define DSP_MODE 0x40
#define RJ_MODE 0x80
#define LJ_MODE 0xC0
#define WORD_LENGTH20 0x10
#define WORD_LENGTH24 0x20
#define WORD_LENGTH32 0x30
#define BITCLOCK_256CLK_FRAME 0x08
//Left/Right ADC PGA gain control register 15 & 16
#define ADC_PGA_MUTE 0x80
#define ADC_PGA_GAIN_MAX 0x78
#define ADC_PGA_GAIN_MIN 0x00
// MIC3L/R to left/right ADC control register 17 & 18
#define ADCPGA_GAIN_MAX 0x00
#define MIC3L_ADCPGA_GAIN_MIN 0x80
#define MIC3L_ADCPGA_DISCONNECT 0xF0
#define MIC3R_ADCPGA_GAIN_MIN 0x08
#define MIC3R_ADCPGA_DISCONNECT 0x0F
//LINE1L to left ADC Control Register 19
#define DIFF_MODE 0x80
#define LINE_ADCPGA_GAIN_MIN 0x40
#define LINE_ADCPGA_DISCONNECT 0x78
#define ADC_CHAN_ON 0x04
#define ADCPGA_SOFT_STEP2FS 0x01
#define ADCPGA_SOFT_STEP_OFF 0x03
//LINE2L to left ADC Control Register 20
#define ADC_WEAK_INPUT_BIAS 0x04
//MICBIAS control register 25
#define MICBIAS_OUTPUT_2_0V 0x40
#define MICBIAS_OUTPUT_2_5V 0x80
#define MICBIAS_OUTPUT_AVDD 0xC0
//LEFT/RIGHT AGC Control registerA 26 & 29
#define AGC_ENABLE 0x80
#define AGC_TARGET_GAIN_MAX 0x00
#define AGC_TARGET_GAIN_MIN 0x70
#define AGC_ATTACK_TIME_11 0x04
#define AGC_ATTACK_TIME_16 0x08
#define AGC_ATTACK_TIME_20 0x0C
#define AGC_DECAY_TIME_200 0x01
#define AGC_DECAY_TIME_400 0x02
#define AGC_DECAY_TIME_500 0x03
//LEFT AGC Control registerB 27 & 30
#define AGC_GAIN_ALLOWED_MAX 0xEE
#define AGC_GAIN_ALLOWED_MIN 0x00
//DAC Power and output driver control register 37
#define LEFT_DAC_POWER_ON 0x80
#define RIGHT_DAC_POWER_ON 0x40
//High Power Output Stage Control Register 40
#define LINE2L_BYPASS_DISABLE_DEFAULT 0x00
#define LINE2LP_BYPASS_SINGLE 0x10
#define LINE2LM_BYPASS_SINGLE 0x20
#define LINE2LPM_BYPASS_DIFFERENTIAL 0x30
#define LINE2R_BYPASS_DISABLE_DEFAULT 0x00
#define LINE2RP_BYPASS_SINGLE 0x04
#define LINE2RM_BYPASS_SINGLE 0x08
#define LINE2RPM_BYPASS_DIFFERENTIAL 0x0C
//DAC Output Switching Control Register 41
#define LEFT_DAC_DEFAULT_L1 0x00
#define LEFT_DAC_L2 0x80
#define LEFT_DAC_L3 0x40
#define RIGHT_DAC_DEFAULT_R1 0x00
#define RIGHT_DAC_R2 0x08
#define RIGHT_DAC_R3 0x04
//LEFT/RIGHT DAC Digital volume control register 43 & 44
#define DAC_CHAN_MUTE 0x80
#define DAC_DIG_VOL_GAIN_MAX 0x00 // 0.0db
#define DAC_DIG_VOL_GAIN_MIN 0x7F // -63.5db
//LINE2L to HPLOUT Volume Control Register 45
#define LINE2L_HPLOUT_ROUTED 0x80
//PGA_L to HPLOUT Volume Control Register 46
#define PGAL_HPLOUT_ROUTED 0x80
//any to LOP/M Volume control
#define LOPM_ON 0x80
#define LOPM_VOL_GAIN_MAX 0x00 //0 db
#define LOPM_VOL_GAIN_MIN 0x76 //-78.3 db is MUTE
//MONO_LOP/M output level volume control register 79
#define LOPM_POWER_ON 0x01
#define LOPM_MUTE_OFF 0x08
#define LOPM_OUTPUT_LEVEL_MIN 0x00
#define LOPM_OUTPUT_LEVEL_MAX 0x90
//Module Power Status Register 94
#define HPROUT_DRIVER_POWER_ON 0x02
#define LIV_MAX 0x0077
#define LIV_MIN 0x0000
#define LHV_MAX 0x0077
#define LHV_MIN 0x0000
#define LIG_MAX 0x0077
#define LIG_MIN 0x0000
#define LOG_MAX 0x007f
#define LOG_MIN 0x0000
#endif
/* __ASM_ARCH_AIC33_H */
sound/oss/davinci-audio-aic33.c
deleted
100644 → 0
View file @
3ccd1e4e
/*
* linux/sound/oss/davinci-audio-aic33.c
*
* Glue driver for AIC33 for Davinci processors
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
* -------
* 2005-10-18 Rishi Bhattacharya - Support for AIC33 codec and Davinci DM644x Processor
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/clk.h>
#include <sound/davincisound.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/mcbsp.h>
#include "davinci-aic33.h"
#include "davinci-audio.h"
#include "davinci-audio-dma-intfc.h"
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#define PROC_START_FILE "driver/aic33-audio-start"
#define PROC_STOP_FILE "driver/aic33-audio-stop"
#endif
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(ARGS...) do { \
printk("<%s>: ",__FUNCTION__);printk(ARGS); \
} while (0)
#else
#define DPRINTK( x... )
#endif
#define CODEC_NAME "AIC33"
#define PLATFORM_NAME "DAVINCI"
/* Define to set the AIC33 as the master w.r.t McBSP */
#define AIC33_MASTER
/* codec clock frequency */
#define MCLK 22
/*
* AUDIO related MACROS
*/
#define DEFAULT_BITPERSAMPLE 16
#define AUDIO_RATE_DEFAULT 48000
#define DEFAULT_MCBSP_CLOCK 81000000
/* Select the McBSP For Audio */
#define AUDIO_MCBSP DAVINCI_MCBSP1
#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC)
#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME)
#define MONO 1
#define STEREO 2
#define SET_VOLUME 1
#define SET_LINE 2
#define SET_MIC 3
#define SET_RECSRC 4
#define SET_IGAIN 5
#define SET_OGAIN 6
#define SET_BASS 7
#define SET_TREBLE 8
#define SET_MICBIAS 9
#define DEFAULT_OUTPUT_VOLUME 50
#define DEFAULT_INPUT_VOLUME 20
/* 0 ==> mute line in */
#define DEFAULT_INPUT_IGAIN 20
#define DEFAULT_INPUT_OGAIN 100
#define OUTPUT_VOLUME_MIN LHV_MIN
#define OUTPUT_VOLUME_MAX LHV_MAX
#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN)
#define INPUT_VOLUME_MIN LIV_MIN
#define INPUT_VOLUME_MAX LIV_MAX
#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN)
#define INPUT_GAIN_MIN LIG_MIN
#define INPUT_GAIN_MAX LIG_MAX
#define INPUT_GAIN_RANGE (INPUT_GAIN_MAX - INPUT_GAIN_MIN)
#define OUTPUT_GAIN_MIN LOG_MIN
#define OUTPUT_GAIN_MAX LOG_MAX
#define OUTPUT_GAIN_RANGE (INPUT_GAIN_MAX - INPUT_GAIN_MIN)
#define NUMBER_SAMPLE_RATES_SUPPORTED 11
static
audio_stream_t
output_stream
=
{
.
id
=
"AIC33 out"
,
.
dma_dev
=
DAVINCI_DMA_MCBSP1_TX
,
.
input_or_output
=
FMODE_WRITE
};
static
audio_stream_t
input_stream
=
{
.
id
=
"AIC33 in"
,
.
dma_dev
=
DAVINCI_DMA_MCBSP1_RX
,
.
input_or_output
=
FMODE_READ
};
static
int
audio_dev_id
,
mixer_dev_id
;
static
struct
aic33_local_info
{
u8
volume
;
u16
volume_reg
;
u8
line
;
u8
mic
;
int
recsrc
;
int
nochan
;
u8
igain
;
u8
ogain
;
u8
micbias
;
u8
bass
;
u8
treble
;
u16
input_volume_reg
;
int
mod_cnt
;
}
aic33_local
;
struct
sample_rate_reg_info
{
u32
sample_rate
;
u32
Fsref
;
float
divider
;
u8
data
;
};
/* To Store the default sample rate */
static
long
audio_samplerate
=
AUDIO_RATE_DEFAULT
;
extern
struct
clk
*
davinci_mcbsp_get_clock
(
void
);
/* DAC USB-mode sampling rates*/
static
const
struct
sample_rate_reg_info
reg_info
[
NUMBER_SAMPLE_RATES_SUPPORTED
]
=
{
/* {sample_rate, Fsref, divider, data}*/
{
96000
,
96000
,
1
,
0x00
},
{
88200
,
88200
,
1
,
0x00
},
{
48000
,
48000
,
1
,
0x00
},
{
44100
,
44100
,
1
,
0x00
},
{
32000
,
48000
,
1
.
5
,
0x11
},
{
24000
,
96000
,
4
,
0x66
},
{
22050
,
44100
,
2
,
0x22
},
{
16000
,
48000
,
3
,
0x44
},
{
12000
,
48000
,
4
,
0x66
},
{
11025
,
44100
,
4
,
0x66
},
{
8000
,
48000
,
6
,
0xAA
},
};
static
struct
davinci_mcbsp_reg_cfg
initial_config
=
{
.
spcr2
=
FREE
|
XINTM
(
3
),
.
spcr1
=
RINTM
(
3
),
.
rcr2
=
RWDLEN2
(
DAVINCI_MCBSP_WORD_16
)
|
RDATDLY
(
1
),
.
rcr1
=
RFRLEN1
(
1
)
|
RWDLEN1
(
DAVINCI_MCBSP_WORD_16
),
.
xcr2
=
XWDLEN2
(
DAVINCI_MCBSP_WORD_16
)
|
XDATDLY
(
1
)
|
XFIG
,
.
xcr1
=
XFRLEN1
(
1
)
|
XWDLEN1
(
DAVINCI_MCBSP_WORD_16
),
.
srgr1
=
FWID
(
DEFAULT_BITPERSAMPLE
-
1
),
.
srgr2
=
FSGM
|
FPER
(
DEFAULT_BITPERSAMPLE
*
2
-
1
),
#ifndef AIC33_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
/* AIC33_MASTER */
};
static
void
davinci_aic33_initialize
(
void
*
dummy
);
static
void
davinci_aic33_shutdown
(
void
*
dummy
);
static
int
davinci_aic33_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
uint
cmd
,
ulong
arg
);
static
int
davinci_aic33_probe
(
void
);
#ifdef MODULE
static
void
davinci_aic33_remove
(
void
);
#endif
static
int
davinci_aic33_suspend
(
void
);
static
int
davinci_aic33_resume
(
void
);
static
inline
void
aic33_configure
(
void
);
static
int
mixer_open
(
struct
inode
*
inode
,
struct
file
*
file
);
static
int
mixer_release
(
struct
inode
*
inode
,
struct
file
*
file
);
static
int
mixer_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
uint
cmd
,
ulong
arg
);
#ifdef CONFIG_PROC_FS
static
int
codec_start
(
char
*
buf
,
char
**
start
,
off_t
offset
,
int
count
,
int
*
eof
,
void
*
data
);
static
int
codec_stop
(
char
*
buf
,
char
**
start
,
off_t
offset
,
int
count
,
int
*
eof
,
void
*
data
);
#endif
/* File Op structure for mixer */
static
struct
file_operations
davinci_mixer_fops
=
{
.
open
=
mixer_open
,
.
release
=
mixer_release
,
.
ioctl
=
mixer_ioctl
,
.
owner
=
THIS_MODULE
};
/* To store characteristic info regarding the codec for the audio driver */
static
audio_state_t
aic33_state
=
{
.
owner
=
THIS_MODULE
,
.
output_stream
=
&
output_stream
,
.
input_stream
=
&
input_stream
,
/* .need_tx_for_rx = 1, //Once the Full Duplex works */
.
need_tx_for_rx
=
0
,
.
hw_init
=
davinci_aic33_initialize
,
.
hw_shutdown
=
davinci_aic33_shutdown
,
.
client_ioctl
=
davinci_aic33_ioctl
,
.
hw_probe
=
davinci_aic33_probe
,
.
hw_remove
=
__exit_p
(
davinci_aic33_remove
),
.
hw_suspend
=
davinci_aic33_suspend
,
.
hw_resume
=
davinci_aic33_resume
,
.
sem
=
__SEMAPHORE_INITIALIZER
(
aic33_state
.
sem
,
1
),
};
/* This will be defined in the audio.h */
static
struct
file_operations
*
davinci_audio_fops
;
extern
int
tlv320aic33_write_value
(
u8
reg
,
u16
value
);
/* TLV320AIC33 write */
static
__inline__
void
audio_aic33_write
(
u8
address
,
u16
data
)
{
if
(
tlv320aic33_write_value
(
address
,
data
)
<
0
)
printk
(
KERN_INFO
"aic33 write failed for reg = %d
\n
"
,
address
);
}
static
int
aic33_update
(
int
flag
,
int
val
)
{
u16
volume
;
u16
gain
;
/* Ignore separate left/right channel for now,
even the codec does support it. */
val
&=
0xff
;
switch
(
flag
)
{
case
SET_VOLUME
:
if
(
val
<
0
||
val
>
100
)
{
DPRINTK
(
"Trying a bad volume value(%d)!
\n
"
,
val
);
return
-
EPERM
;
}
// Convert 0 -> 100 volume to 0x77 (LHV_MIN) -> 0x00 (LHV_MAX)
volume
=
((
val
*
OUTPUT_VOLUME_RANGE
)
/
100
)
+
OUTPUT_VOLUME_MIN
;
aic33_local
.
volume_reg
=
OUTPUT_VOLUME_MAX
-
volume
;
if
(
aic33_local
.
nochan
==
STEREO
)
{
audio_aic33_write
(
47
,
LOPM_ON
|
aic33_local
.
volume_reg
);
audio_aic33_write
(
64
,
LOPM_ON
|
aic33_local
.
volume_reg
);
audio_aic33_write
(
82
,
LOPM_ON
|
aic33_local
.
volume_reg
);
audio_aic33_write
(
92
,
LOPM_ON
|
aic33_local
.
volume_reg
);
}
else
if
(
aic33_local
.
nochan
==
MONO
)
{
#ifdef CONFIG_MONOSTEREO_DIFFJACK
/* DACL1 to MONO_LOP/M routing and volume control */
audio_aic33_write
(
75
,
LOPM_ON
|
aic33_local
.
volume_reg
);
/* DACR1 to MONO_LOP/M routing and volume control */
audio_aic33_write
(
78
,
LOPM_ON
|
aic33_local
.
volume_reg
);
#else
audio_aic33_write
(
47
,
LOPM_ON
|
aic33_local
.
volume_reg
);
audio_aic33_write
(
64
,
LOPM_ON
|
aic33_local
.
volume_reg
);
audio_aic33_write
(
82
,
LOPM_ON
|
aic33_local
.
volume_reg
);
audio_aic33_write
(
92
,
LOPM_ON
|
aic33_local
.
volume_reg
);
#endif
}
break
;
case
SET_LINE
:
case
SET_MIC
:
if
(
val
<
0
||
val
>
100
)
{
DPRINTK
(
"Trying a bad volume value(%d)!
\n
"
,
val
);
return
-
EPERM
;
}
volume
=
((
val
*
INPUT_VOLUME_RANGE
)
/
100
)
+
INPUT_VOLUME_MIN
;
aic33_local
.
input_volume_reg
=
volume
;
audio_aic33_write
(
15
,
aic33_local
.
input_volume_reg
);
audio_aic33_write
(
16
,
aic33_local
.
input_volume_reg
);
break
;
case
SET_RECSRC
:
if
(
hweight32
(
val
)
>
1
)
val
&=
~
aic33_local
.
recsrc
;
if
(
val
==
SOUND_MASK_MIC
)
{
/* enable the mic input*/
DPRINTK
(
"Enabling mic
\n
"
);
audio_aic33_write
(
17
,
0x0
);
audio_aic33_write
(
18
,
0x0
);
/* enable ADC's and disable the line input*/
audio_aic33_write
(
19
,
0x7C
);
audio_aic33_write
(
22
,
0x7C
);
}
else
if
(
val
==
SOUND_MASK_LINE
)
{
/* enable ADC's, enable line iput */
DPRINTK
(
" Enabling line in
\n
"
);
audio_aic33_write
(
19
,
0x4
);
audio_aic33_write
(
22
,
0x4
);
/* disable the mic input */
audio_aic33_write
(
17
,
0xff
);
audio_aic33_write
(
18
,
0xff
);
}
else
{
/* do nothing */
}
aic33_local
.
recsrc
=
val
;
break
;
case
SET_IGAIN
:
if
(
val
<
0
||
val
>
100
)
{
DPRINTK
(
"Trying a bad igain value(%d)!
\n
"
,
val
);
return
-
EPERM
;
}
gain
=
((
val
*
INPUT_GAIN_RANGE
)
/
100
)
+
INPUT_GAIN_MIN
;
DPRINTK
(
"gain reg val = 0x%x"
,
gain
<<
1
);
/* Left AGC control */
audio_aic33_write
(
26
,
0x80
);
audio_aic33_write
(
27
,
gain
<<
1
);
audio_aic33_write
(
28
,
0x0
);
/* Right AGC control */
audio_aic33_write
(
29
,
0x80
);
audio_aic33_write
(
30
,
gain
<<
1
);
audio_aic33_write
(
31
,
0x0
);
break
;
case
SET_OGAIN
:
if
(
val
<
0
||
val
>
100
)
{
DPRINTK
(
"Trying a bad igain value(%d)!
\n
"
,
val
);
return
-
EPERM
;
}
gain
=
((
val
*
OUTPUT_GAIN_RANGE
)
/
100
)
+
OUTPUT_GAIN_MIN
;
gain
=
OUTPUT_GAIN_MAX
-
gain
;
/* Left/Right DAC digital volume gain */
audio_aic33_write
(
43
,
gain
);
audio_aic33_write
(
44
,
gain
);
break
;
case
SET_MICBIAS
:
if
(
val
<
0
||
val
>
3
)
{
DPRINTK
(
"Request for non supported mic bias level(%d)!
\n
"
,
val
);
return
-
EPERM
;
}
if
(
val
==
0
)
audio_aic33_write
(
25
,
0x00
);
else
if
(
val
==
1
)
audio_aic33_write
(
25
,
MICBIAS_OUTPUT_2_0V
);
else
if
(
val
==
2
)
audio_aic33_write
(
25
,
MICBIAS_OUTPUT_2_5V
);
else
if
(
val
==
3
)
audio_aic33_write
(
25
,
MICBIAS_OUTPUT_AVDD
);
break
;
case
SET_BASS
:
break
;
case
SET_TREBLE
:
break
;
}
return
0
;
}
static
int
mixer_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
/* Any mixer specific initialization */
return
0
;
}
static
int
mixer_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
/* Any mixer specific Un-initialization */
return
0
;
}
static
int
mixer_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
uint
cmd
,
ulong
arg
)
{
int
val
;
int
ret
=
0
;
int
nr
=
_IOC_NR
(
cmd
);
/*
* We only accept mixer (type 'M') ioctls.
*/
if
(
_IOC_TYPE
(
cmd
)
!=
'M'
)
return
-
EINVAL
;
DPRINTK
(
" 0x%08x
\n
"
,
cmd
);
if
(
cmd
==
SOUND_MIXER_INFO
)
{
struct
mixer_info
mi
;
strncpy
(
mi
.
id
,
"AIC33"
,
sizeof
(
mi
.
id
));
strncpy
(
mi
.
name
,
"TI AIC33"
,
sizeof
(
mi
.
name
));
mi
.
modify_counter
=
aic33_local
.
mod_cnt
;
return
copy_to_user
((
void
*
)
arg
,
&
mi
,
sizeof
(
mi
));
}
if
(
_IOC_DIR
(
cmd
)
&
_IOC_WRITE
)
{
ret
=
get_user
(
val
,
(
int
*
)
arg
);
if
(
ret
)
goto
out
;
switch
(
nr
)
{
case
SOUND_MIXER_VOLUME
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_VOLUME
,
val
);
if
(
!
ret
)
aic33_local
.
volume
=
val
;
break
;
case
SOUND_MIXER_LINE
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_LINE
,
val
);
if
(
!
ret
)
aic33_local
.
line
=
val
;
break
;
case
SOUND_MIXER_MIC
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_MIC
,
val
);
if
(
!
ret
)
aic33_local
.
mic
=
val
;
break
;
case
SOUND_MIXER_RECSRC
:
if
((
val
&
SOUND_MASK_LINE
)
||
(
val
&
SOUND_MASK_MIC
))
{
if
(
aic33_local
.
recsrc
!=
val
)
{
aic33_local
.
mod_cnt
++
;
aic33_update
(
SET_RECSRC
,
val
);
}
}
else
{
ret
=
-
EINVAL
;
}
break
;
case
SOUND_MIXER_BASS
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_BASS
,
val
);
if
(
!
ret
)
aic33_local
.
bass
=
val
;
break
;
case
SOUND_MIXER_TREBLE
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_TREBLE
,
val
);
if
(
!
ret
)
aic33_local
.
treble
=
val
;
break
;
case
SOUND_MIXER_IGAIN
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_IGAIN
,
val
);
if
(
!
ret
)
aic33_local
.
igain
=
val
;
break
;
case
SOUND_MIXER_OGAIN
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_OGAIN
,
val
);
if
(
!
ret
)
aic33_local
.
ogain
=
val
;
break
;
case
SOUND_MIXER_MICBIAS
:
aic33_local
.
mod_cnt
++
;
ret
=
aic33_update
(
SET_MICBIAS
,
val
);
if
(
!
ret
)
aic33_local
.
micbias
=
val
;
break
;
default:
ret
=
-
EINVAL
;
}
}
if
(
ret
==
0
&&
_IOC_DIR
(
cmd
)
&
_IOC_READ
)
{
ret
=
0
;
switch
(
nr
)
{
case
SOUND_MIXER_VOLUME
:
val
=
aic33_local
.
volume
;
break
;
case
SOUND_MIXER_LINE
:
val
=
aic33_local
.
line
;
break
;
case
SOUND_MIXER_MIC
:
val
=
aic33_local
.
mic
;
break
;
case
SOUND_MIXER_RECSRC
:
val
=
aic33_local
.
recsrc
;
break
;
case
SOUND_MIXER_RECMASK
:
val
=
REC_MASK
;
break
;
case
SOUND_MIXER_IGAIN
:
val
=
aic33_local
.
igain
;
break
;
case
SOUND_MIXER_OGAIN
:
val
=
aic33_local
.
ogain
;
break
;
case
SOUND_MIXER_DEVMASK
:
val
=
DEV_MASK
;
break
;
case
SOUND_MIXER_BASS
:
val
=
aic33_local
.
bass
;
break
;
case
SOUND_MIXER_TREBLE
:
val
=
aic33_local
.
treble
;
break
;
case
SOUND_MIXER_CAPS
:
val
=
0
;
break
;
case
SOUND_MIXER_STEREODEVS
:
val
=
SOUND_MASK_VOLUME
;
break
;
case
SOUND_MIXER_MICBIAS
:
val
=
aic33_local
.
micbias
;
break
;
default:
val
=
0
;
ret
=
-
EINVAL
;
break
;
}
if
(
ret
==
0
)
ret
=
put_user
(
val
,
(
int
*
)
arg
);
}
out:
return
ret
;
}
int
davinci_set_samplerate
(
long
sample_rate
)
{
u8
count
=
0
;
/* wait for any frame to complete */
udelay
(
125
);
/* Search for the right sample rate */
while
((
reg_info
[
count
].
sample_rate
!=
sample_rate
)
&&
(
count
<
NUMBER_SAMPLE_RATES_SUPPORTED
))
{
count
++
;
}
if
(
count
==
NUMBER_SAMPLE_RATES_SUPPORTED
)
{
DPRINTK
(
"Invalid Sample Rate %d requested
\n
"
,
(
int
)
sample_rate
);
return
-
EPERM
;
}
/* CODEC DATAPATH SETUP */
/* Fsref to 48kHz, dual rate mode upto 96kHz */
if
(
reg_info
[
count
].
Fsref
==
96000
)
audio_aic33_write
(
7
,
FS_REF_DEFAULT_48
|
ADC_DUAL_RATE_MODE
|
DAC_DUAL_RATE_MODE
|
LDAC_LCHAN
|
RDAC_RCHAN
);
/* Fsref to 44.1kHz, dual rate mode upto 88.2kHz */
else
if
(
reg_info
[
count
].
Fsref
==
88200
)
audio_aic33_write
(
7
,
FS_REF_44_1
|
ADC_DUAL_RATE_MODE
|
DAC_DUAL_RATE_MODE
|
LDAC_LCHAN
|
RDAC_RCHAN
);
/* Fsref to 48kHz */
else
if
(
reg_info
[
count
].
Fsref
==
48000
)
audio_aic33_write
(
7
,
FS_REF_DEFAULT_48
|
LDAC_LCHAN
|
RDAC_RCHAN
);
/* Fsref to 44.1kHz */
else
if
(
reg_info
[
count
].
Fsref
==
44100
)
audio_aic33_write
(
7
,
FS_REF_44_1
|
LDAC_LCHAN
|
RDAC_RCHAN
);
/* Codec sample rate select */
audio_aic33_write
(
2
,
reg_info
[
count
].
data
);
/* If PLL is to be used for generation of Fsref
Generate the Fsref using the PLL */
#if(MCLK==33)
if
((
reg_info
[
count
].
Fsref
==
96000
)
|
(
reg_info
[
count
].
Fsref
==
48000
))
{
/* For MCLK = 33.8688 MHz and to get Fsref = 48kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R= 1, K = 5.8049, which results in J = 5, D = 8049 */
/*Enable the PLL | Q-value | P-value */
audio_aic33_write
(
3
,
PLL_ENABLE
|
0x10
|
0x02
);
audio_aic33_write
(
4
,
0x14
);
/* J-value */
audio_aic33_write
(
5
,
0x7D
);
/* D-value 8-MSB's */
audio_aic33_write
(
6
,
0x04
);
/* D-value 6-LSB's */
}
else
if
((
reg_info
[
count
].
Fsref
==
88200
)
|
(
reg_info
[
count
].
Fsref
==
44100
))
{
/* MCLK = 33.8688 MHz and to get Fsref = 44.1kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R =1, K = 5.3333, which results in J = 5, D = 3333 */
/*Enable the PLL | Q-value | P-value */
audio_aic33_write
(
3
,
PLL_ENABLE
|
0x10
|
0x02
);
audio_aic33_write
(
4
,
0x14
);
/* J-value */
audio_aic33_write
(
5
,
0x34
);
/* D-value 8-MSB's */
audio_aic33_write
(
6
,
0x14
);
/* D-value 6-LSB's */
}
#elif(MCLK==22)
if
((
reg_info
[
count
].
Fsref
==
96000
)
|
(
reg_info
[
count
].
Fsref
==
48000
))
{
/* For MCLK = 22.5792 MHz and to get Fsref = 48kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R= 1, K = 8.7075, which results in J = 8, D = 7075 */
/*Enable the PLL | Q-value | P-value */
audio_aic33_write
(
3
,
PLL_ENABLE
|
0x10
|
0x02
);
audio_aic33_write
(
4
,
(
8
<<
2
));
/* J-value */
audio_aic33_write
(
5
,
(
unsigned
char
)(
7075
>>
6
));
/* D-value 8-MSB's */
audio_aic33_write
(
6
,
(
unsigned
char
)(
7075
<<
2
));
/* D-value 6-LSB's */
}
else
if
((
reg_info
[
count
].
Fsref
==
88200
)
|
(
reg_info
[
count
].
Fsref
==
44100
))
{
/* MCLK = 22.5792 MHz and to get Fsref = 44.1kHz
Fsref = (MCLK * k * R)/(2048 * p);
Select P = 2, R =1, K = 8.0000, which results in J = 8, D = 0000 */
/*Enable the PLL | Q-value | P-value */
audio_aic33_write
(
3
,
PLL_ENABLE
|
0x10
|
0x02
);
audio_aic33_write
(
4
,
(
8
<<
2
));
/* J-value */
audio_aic33_write
(
5
,
0x00
);
/* D-value 8-MSB's */
audio_aic33_write
(
6
,
0x00
);
/* D-value 6-LSB's */
}
#else
#error "unknown audio codec frequency"
#endif
audio_samplerate
=
sample_rate
;
#ifndef AIC33_MASTER
{
int
clkgdv
=
0
;
unsigned
long
clkval
=
0
;
struct
clk
*
mbspclk
;
/*
Set Sample Rate at McBSP
Formula :
Codec System Clock = Input clock to McBSP;
clkgdv = ((Codec System Clock / (SampleRate * BitsPerSample * 2)) - 1);
FWID = BitsPerSample - 1;
FPER = (BitsPerSample * 2) - 1;
*/
mbspclk
=
davinci_mcbsp_get_clock
();
if
(
mbspclk
==
NULL
)
{
DPRINTK
(
" Failed to get internal clock to MCBSP"
);
return
-
EPERM
;
}
clkval
=
clk_get_rate
(
mbspclk
);
DPRINTK
(
"mcbsp_clk = %ld
\n
"
,
clkval
);
if
(
clkval
)
clkgdv
=
(
clkval
/
(
sample_rate
*
DEFAULT_BITPERSAMPLE
*
2
))
-
1
;
else
{
DPRINTK
(
" Failed to get the MCBSP clock
\n
"
);
return
-
EPERM
;
}
DPRINTK
(
"clkgdv = %d
\n
"
,
clkgdv
);
if
(
clkgdv
>
255
||
clkgdv
<
0
)
{
/* For requested sampling rate, the input clock to MCBSP cant be devided
down to get the in range clock devider value for 16 bits sample */
DPRINTK
(
"Invalid Sample Rate %d requested
\n
"
,
(
int
)
sample_rate
);
return
-
EPERM
;
}
initial_config
.
srgr1
=
(
FWID
(
DEFAULT_BITPERSAMPLE
-
1
)
|
CLKGDV
(
clkgdv
));
initial_config
.
srgr2
=
(
CLKSM
|
FSGM
|
FPER
(
DEFAULT_BITPERSAMPLE
*
2
-
1
));
davinci_mcbsp_stop
(
AUDIO_MCBSP
);
davinci_mcbsp_config
(
AUDIO_MCBSP
,
&
initial_config
);
}
#endif
/* AIC33_MASTER */
return
0
;
}
static
void
davinci_aic33_shutdown
(
void
*
dummy
)
{
/*
Turn off codec after it is done.
Can't do it immediately, since it may still have
buffered data.
Wait 20ms (arbitrary value) and then turn it off.
*/
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
(
2
);
davinci_mcbsp_stop
(
AUDIO_MCBSP
);
davinci_mcbsp_free
(
AUDIO_MCBSP
);
/* Self clearing aic33 software reset */
audio_aic33_write
(
1
,
0x80
);
}
static
void
davinci_set_mono_stereo
(
int
mode
)
{
if
(
mode
==
MONO
)
{
#ifdef CONFIG_MONOSTEREO_DIFFJACK
/* MONO_LOP/M Output level control register */
audio_aic33_write
(
79
,
0x99
);
#else
/* Driver power ON pop control */
audio_aic33_write
(
42
,
0x6C
);
/* LEFT_LOP/M, RIGHT_LOP/M output level control */
audio_aic33_write
(
86
,
0x99
);
audio_aic33_write
(
93
,
0x99
);
#endif
/* Left DAC power up, Right DAC power down */
audio_aic33_write
(
37
,
0xa0
);
}
else
if
(
mode
==
STEREO
)
{
/* Driver power ON pop control */
audio_aic33_write
(
42
,
0x6C
);
/* HPLOUT/HPROUT output level control */
audio_aic33_write
(
51
,
0x99
);
audio_aic33_write
(
65
,
0x99
);
/* LEFT_LOP/M, RIGHT_LOP/M output level control */
audio_aic33_write
(
86
,
0x99
);
audio_aic33_write
(
93
,
0x99
);
/* Left/Right DAC power up */
audio_aic33_write
(
37
,
0xe0
);
}
else
DPRINTK
(
" REQUEST FOR INVALID MODE
\n
"
);
}
static
inline
void
aic33_configure
()
{
DPRINTK
(
" CONFIGURING AIC33
\n
"
);
/* Page select register */
audio_aic33_write
(
0
,
0x0
);
//audio_aic33_write(38, 0x10);
davinci_set_mono_stereo
(
aic33_local
.
nochan
);
#ifdef AIC33_MASTER
/* Enable bit and word clock as Master mode, 3-d disabled */
audio_aic33_write
(
8
,
0xc0
/*0xc4 */
);
#endif
aic33_update
(
SET_LINE
,
aic33_local
.
line
);
aic33_update
(
SET_VOLUME
,
aic33_local
.
volume
);
aic33_update
(
SET_RECSRC
,
aic33_local
.
recsrc
);
aic33_update
(
SET_IGAIN
,
aic33_local
.
igain
);
aic33_update
(
SET_OGAIN
,
aic33_local
.
ogain
);
aic33_update
(
SET_MICBIAS
,
aic33_local
.
micbias
);
}
static
void
davinci_aic33_initialize
(
void
*
dummy
)
{
DPRINTK
(
"entry
\n
"
);
/* initialize with default sample rate */
audio_samplerate
=
AUDIO_RATE_DEFAULT
;
if
(
davinci_mcbsp_request
(
AUDIO_MCBSP
)
<
0
)
{
DPRINTK
(
"MCBSP request failed
\n
"
);
return
;
}
/* if configured, then stop mcbsp */
davinci_mcbsp_stop
(
AUDIO_MCBSP
);
/* set initial (default) sample rate */
davinci_set_samplerate
(
audio_samplerate
);
davinci_mcbsp_config
(
AUDIO_MCBSP
,
&
initial_config
);
DPRINTK
(
"exit
\n
"
);
}
static
int
davinci_aic33_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
uint
cmd
,
ulong
arg
)
{
long
val
;
int
ret
=
0
;
DPRINTK
(
" 0x%08x
\n
"
,
cmd
);
/*
* These are platform dependent ioctls which are not handled by the
* generic davinci-audio module.
*/
switch
(
cmd
)
{
case
SNDCTL_DSP_STEREO
:
ret
=
get_user
(
val
,
(
int
*
)
arg
);
if
(
ret
)
return
ret
;
/* the Davinci supports AIC33 as stereo, mono on stereo jack */
ret
=
(
val
==
0
)
?
-
EINVAL
:
1
;
return
put_user
(
ret
,
(
int
*
)
arg
);
case
SNDCTL_DSP_CHANNELS
:
ret
=
get_user
(
val
,
(
long
*
)
arg
);
if
(
ret
)
{
DPRINTK
(
"get_user failed
\n
"
);
break
;
}
if
(
val
==
STEREO
)
{
DPRINTK
(
"Driver support for AIC33 as stereo
\n
"
);
aic33_local
.
nochan
=
STEREO
;
davinci_set_mono_stereo
(
aic33_local
.
nochan
);
}
else
if
(
val
==
MONO
)
{
DPRINTK
(
"Driver support for AIC33 as mono
\n
"
);
aic33_local
.
nochan
=
MONO
;
davinci_set_mono_stereo
(
aic33_local
.
nochan
);
}
else
{
DPRINTK
(
"Driver support for AIC33 as stereo/mono mode
\n
"
);
return
-
EPERM
;
}
case
SOUND_PCM_READ_CHANNELS
:
/* the Davinci supports AIC33 as stereo, mono on stereo jack */
if
(
aic33_local
.
nochan
==
MONO
)
return
put_user
(
MONO
,
(
long
*
)
arg
);
else
return
put_user
(
STEREO
,
(
long
*
)
arg
);
case
SNDCTL_DSP_SPEED
:
ret
=
get_user
(
val
,
(
long
*
)
arg
);
if
(
ret
)
{
DPRINTK
(
"get_user failed
\n
"
);
break
;
}
ret
=
davinci_set_samplerate
(
val
);
if
(
ret
)
{
DPRINTK
(
"davinci_set_samplerate failed
\n
"
);
break
;
}
/* fall through */
case
SOUND_PCM_READ_RATE
:
return
put_user
(
audio_samplerate
,
(
long
*
)
arg
);
case
SNDCTL_DSP_SETFMT
:
/* set Format */
ret
=
get_user
(
val
,
(
long
*
)
arg
);
if
(
ret
)
{
DPRINTK
(
"get_user failed
\n
"
);
break
;
}
if
(
val
!=
AFMT_S16_LE
)
{
DPRINTK
(
"Driver supports only AFMT_S16_LE audio format
\n
"
);
return
-
EPERM
;
}
case
SOUND_PCM_READ_BITS
:
case
SNDCTL_DSP_GETFMTS
:
/* we can do 16-bit only */
return
put_user
(
AFMT_S16_LE
,
(
long
*
)
arg
);
default:
/* Maybe this is meant for the mixer (As per OSS Docs) */
return
mixer_ioctl
(
inode
,
file
,
cmd
,
arg
);
}
return
ret
;
}
static
int
davinci_aic33_probe
(
void
)
{
/* Get the fops from audio oss driver */
if
(
!
(
davinci_audio_fops
=
audio_get_fops
()))
{
DPRINTK
(
"Unable to get the file operations for AIC33 OSS driver
\n
"
);
audio_unregister_codec
(
&
aic33_state
);
return
-
EPERM
;
}
aic33_local
.
volume
=
DEFAULT_OUTPUT_VOLUME
;
aic33_local
.
line
=
DEFAULT_INPUT_VOLUME
;
aic33_local
.
recsrc
=
SOUND_MASK_LINE
;
/* either of SOUND_MASK_LINE/SOUND_MASK_MIC */
aic33_local
.
igain
=
DEFAULT_INPUT_IGAIN
;
aic33_local
.
ogain
=
DEFAULT_INPUT_OGAIN
;
aic33_local
.
nochan
=
STEREO
;
aic33_local
.
micbias
=
1
;
aic33_local
.
mod_cnt
=
0
;
/* register devices */
audio_dev_id
=
register_sound_dsp
(
davinci_audio_fops
,
-
1
);
mixer_dev_id
=
register_sound_mixer
(
&
davinci_mixer_fops
,
-
1
);
#ifdef CONFIG_PROC_FS
create_proc_read_entry
(
PROC_START_FILE
,
0
/* default mode */
,
NULL
/* parent dir */
,
codec_start
,
NULL
/* client data */
);
create_proc_read_entry
(
PROC_STOP_FILE
,
0
/* default mode */
,
NULL
/* parent dir */
,
codec_stop
,
NULL
/* client data */
);
#endif
/* Announcement Time */
DPRINTK
(
PLATFORM_NAME
" "
CODEC_NAME
" audio support initialized
\n
"
);
return
0
;
}
#ifdef MODULE
static
void
__exit
davinci_aic33_remove
(
void
)
{
/* Un-Register the codec with the audio driver */
unregister_sound_dsp
(
audio_dev_id
);
unregister_sound_mixer
(
mixer_dev_id
);
#ifdef CONFIG_PROC_FS
remove_proc_entry
(
PROC_START_FILE
,
NULL
);
remove_proc_entry
(
PROC_STOP_FILE
,
NULL
);
#endif
}
#endif
/* MODULE */
static
int
davinci_aic33_suspend
(
void
)
{
/* Empty for the moment */
return
0
;
}
static
int
davinci_aic33_resume
(
void
)
{
/* Empty for the moment */
return
0
;
}
static
int
__init
audio_aic33_init
(
void
)
{
int
err
=
0
;
/* register the codec with the audio driver */
if
((
err
=
audio_register_codec
(
&
aic33_state
)))
{
DPRINTK
(
"Failed to register AIC33 driver with Audio OSS Driver
\n
"
);
}
else
{
DPRINTK
(
"codec driver register success
\n
"
);
}
/* configure aic33 with default params */
aic33_configure
();
return
err
;
}
static
void
__exit
audio_aic33_exit
(
void
)
{
davinci_aic33_shutdown
(
NULL
);
(
void
)
audio_unregister_codec
(
&
aic33_state
);
return
;
}
#ifdef CONFIG_PROC_FS
static
int
codec_start
(
char
*
buf
,
char
**
start
,
off_t
offset
,
int
count
,
int
*
eof
,
void
*
data
)
{
davinci_aic33_initialize
(
NULL
);
DPRINTK
(
"AIC33 codec initialization done.
\n
"
);
return
0
;
}
static
int
codec_stop
(
char
*
buf
,
char
**
start
,
off_t
offset
,
int
count
,
int
*
eof
,
void
*
data
)
{
davinci_aic33_shutdown
(
NULL
);
DPRINTK
(
"AIC33 codec shutdown.
\n
"
);
return
0
;
}
#endif
/* CONFIG_PROC_FS */
module_init
(
audio_aic33_init
);
module_exit
(
audio_aic33_exit
);
MODULE_AUTHOR
(
"Texas Instruments"
);
MODULE_DESCRIPTION
(
"Glue audio driver for the TI AIC33 codec."
);
MODULE_LICENSE
(
"GPL"
);
sound/oss/davinci-audio-dma-intfc.c
deleted
100644 → 0
View file @
3ccd1e4e
/*
* linux/sound/oss/davinci-audio-dma-intfc.c
*
* Common audio DMA handling for the Davinci processors
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
*
* 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c. This file
* will contain only the DMA interface and buffer handling of OMAP
* audio driver.
*
* 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking of DMA logical channel.
*
* 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
*
* 2004-11-01 Nishanth Menon - 16xx platform code base modified to support multi channel chaining.
*
* 2004-12-15 Nishanth Menon - Improved 16xx platform channel logic introduced - tasklets, queue handling updated
*
* 2005-10-01 Rishi Bhattacharya / Sharath Kumar - Added support for TI Davinci DM644x processor
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/sysrq.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <mach/hardware.h>
#include <mach/mcbsp.h>
#include <mach/edma.h>
#include <mach/memory.h>
#include "davinci-audio-dma-intfc.h"
#include "davinci-audio.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);
#define AUDIO_NAME "davinci-audio"
#define MCBSP_DXR 0x01E02004
#define MCBSP_DRR 0x01E02000
#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
#define SPIN_ADDR (dma_addr_t)0
#define SPIN_SIZE 2048
#define NUMBER_OF_CHANNELS_TO_LINK 2
/* Channel Queue Handling macros
* tail always points to the current free entry
* Head always points to the current entry being used
* end is either head or tail
*/
#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0;
#define AUDIO_QUEUE_FULL(s) (NUMBER_OF_CHANNELS_TO_LINK == s->dma_q_count)
#define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count)
#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count)
#define __AUDIO_INCREMENT_QUEUE(end) ((end)=((end)+1)%NUMBER_OF_CHANNELS_TO_LINK)
#define AUDIO_INCREMENT_HEAD(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_head);\
s->dma_q_count--;
#define AUDIO_INCREMENT_TAIL(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_tail);\
s->dma_q_count++;
/* DMA buffer fragmentation sizes */
#define MAX_DMA_SIZE (0xffff*2)
#define CUT_DMA_SIZE MAX_DMA_SIZE
/**************************** DATA STRUCTURES *********************************/
struct
audio_isr_work_item
{
int
current_lch
;
u16
ch_status
;
audio_stream_t
*
s
;
};
static
char
work_item_running
=
0
;
static
struct
audio_isr_work_item
work1
,
work2
;
/*********************** MODULE SPECIFIC FUNCTIONS PROTOTYPES ****************/
static
void
audio_dsr_handler
(
unsigned
long
);
DECLARE_TASKLET
(
audio_isr_work1
,
audio_dsr_handler
,
(
unsigned
long
)
&
work1
);
DECLARE_TASKLET
(
audio_isr_work2
,
audio_dsr_handler
,
(
unsigned
long
)
&
work2
);
static
void
sound_dma_irq_handler
(
int
lch
,
u16
ch_status
,
void
*
data
);
static
void
audio_dma_callback
(
int
lch
,
u16
ch_status
,
void
*
data
);
static
int
davinci_start_sound_dma
(
audio_stream_t
*
s
,
dma_addr_t
dma_ptr
,
u_int
size
);
static
int
audio_set_dma_params_play
(
int
channel
,
dma_addr_t
dma_ptr
,
u_int
dma_size
);
static
int
audio_set_dma_params_capture
(
int
channel
,
dma_addr_t
dma_ptr
,
u_int
dma_size
);
static
int
audio_start_dma_chain
(
audio_stream_t
*
s
);
/************************** GLOBAL FUNCTIONS DEFINTIONS ***********************/
/*******************************************************************************
*
* Buffer creation/destruction
*
******************************************************************************/
int
audio_setup_buf
(
audio_stream_t
*
s
)
{
int
frag
;
int
dmasize
=
0
;
char
*
dmabuf
=
NULL
;
dma_addr_t
dmaphys
=
0
;
FN_IN
;
if
(
s
->
buffers
)
{
FN_OUT
(
1
);
return
-
EBUSY
;
}
/* Allocate memory for all buffer fragments */
s
->
buffers
=
kmalloc
(
sizeof
(
audio_buf_t
)
*
s
->
nbfrags
,
GFP_KERNEL
);
if
(
!
s
->
buffers
)
goto
err
;
/* Initialise all the memory to 0 */
memset
(
s
->
buffers
,
0
,
sizeof
(
audio_buf_t
)
*
s
->
nbfrags
);
for
(
frag
=
0
;
frag
<
s
->
nbfrags
;
frag
++
)
{
audio_buf_t
*
b
=
&
s
->
buffers
[
frag
];
/*
* Let's allocate non-cached memory for DMA buffers.
* We try to allocate all memory at once.
* If this fails (a common reason is memory fragmentation),
* then we allocate more smaller buffers.
*/
if
(
!
dmasize
)
{
dmasize
=
(
s
->
nbfrags
-
frag
)
*
s
->
fragsize
;
do
{
/* allocate consistent memory for DMA
dmaphys(handle)= device viewed address.
dmabuf = CPU-viewed address */
dmabuf
=
dma_alloc_coherent
(
NULL
,
dmasize
,
&
dmaphys
,
0
);
/* For allocating the IRAM memory */
//dmaphys = (dma_addr_t)(DAVINCI_IRAM_BASE + 0x1000);
//dmabuf = (DAVINCI_IRAM_VIRT + 0x1000);
if
(
!
dmabuf
)
dmasize
-=
s
->
fragsize
;
}
while
(
!
dmabuf
&&
dmasize
);
if
(
!
dmabuf
)
goto
err
;
b
->
master
=
dmasize
;
memzero
(
dmabuf
,
dmasize
);
}
b
->
data
=
dmabuf
;
b
->
dma_addr
=
dmaphys
;
dmabuf
+=
s
->
fragsize
;
dmaphys
+=
s
->
fragsize
;
dmasize
-=
s
->
fragsize
;
}
s
->
usr_head
=
s
->
dma_head
=
s
->
dma_tail
=
0
;
AUDIO_QUEUE_INIT
(
s
);
s
->
started
=
0
;
s
->
dma_started
=
0
;
s
->
bytecount
=
0
;
s
->
fragcount
=
0
;
s
->
prevbuf
=
0
;
init_completion
(
&
s
->
wfc
);
s
->
wfc
.
done
=
s
->
nbfrags
;
FN_OUT
(
0
);
return
0
;
err:
audio_discard_buf
(
s
);
FN_OUT
(
1
);
return
-
ENOMEM
;
}
void
audio_discard_buf
(
audio_stream_t
*
s
)
{
FN_IN
;
/* ensure DMA isn't using those buffers */
audio_reset
(
s
);
if
(
s
->
buffers
)
{
int
frag
;
for
(
frag
=
0
;
frag
<
s
->
nbfrags
;
frag
++
)
{
if
(
!
s
->
buffers
[
frag
].
master
)
continue
;
dma_free_coherent
(
NULL
,
s
->
buffers
[
frag
].
master
,
s
->
buffers
[
frag
].
data
,
s
->
buffers
[
frag
].
dma_addr
);
}
kfree
(
s
->
buffers
);
s
->
buffers
=
NULL
;
}
FN_OUT
(
0
);
}
/*******************************************************************************
*
* DMA channel requests
*
******************************************************************************/
int
davinci_request_sound_dma
(
int
device_id
,
const
char
*
device_name
,
void
*
data
,
int
*
master_ch
,
int
**
channels
)
{
int
i
,
err
=
0
;
int
*
chan
=
NULL
;
int
tcc
;
FN_IN
;
if
(
unlikely
((
NULL
==
channels
)
||
(
NULL
==
device_name
)))
{
BUG
();
return
-
EPERM
;
}
/* Try allocate memory for the num channels */
*
channels
=
(
int
*
)
kmalloc
(
sizeof
(
int
)
*
NUMBER_OF_CHANNELS_TO_LINK
,
GFP_KERNEL
);
chan
=
*
channels
;
if
(
NULL
==
chan
)
{
ERR
(
"No Memory for channel allocs!
\n
"
);
FN_OUT
(
-
ENOMEM
);
return
-
ENOMEM
;
}
/* request for the Master channel and setup the params */
i
=
0
;
err
=
davinci_request_dma
(
device_id
,
device_name
,
sound_dma_irq_handler
,
data
,
master_ch
,
&
tcc
,
EVENTQ_0
);
/* Handle Failure condition here */
if
(
err
<
0
)
{
ERR
(
"Error in requesting Master channel %d = 0x%x
\n
"
,
device_id
,
err
);
FN_OUT
(
err
);
return
err
;
}
DPRINTK
(
"Master chan = %d
\n
"
,
*
master_ch
);
for
(
i
=
0
;
i
<
NUMBER_OF_CHANNELS_TO_LINK
;
i
++
)
{
err
=
davinci_request_dma
(
DAVINCI_EDMA_PARAM_ANY
,
device_name
,
sound_dma_irq_handler
,
data
,
&
chan
[
i
],
&
tcc
,
EVENTQ_0
);
/* Handle Failure condition here */
if
(
err
<
0
)
{
int
j
;
for
(
j
=
0
;
j
<
i
;
j
++
)
davinci_free_dma
(
chan
[
j
]);
kfree
(
chan
);
*
channels
=
NULL
;
ERR
(
"Error in requesting channel %d=0x%x
\n
"
,
i
,
err
);
FN_OUT
(
err
);
return
err
;
}
}
/* Chain the channels together */
for
(
i
=
0
;
i
<
NUMBER_OF_CHANNELS_TO_LINK
;
i
++
)
{
int
cur_chan
=
chan
[
i
];
int
nex_chan
=
((
NUMBER_OF_CHANNELS_TO_LINK
-
1
==
i
)
?
chan
[
0
]
:
chan
[
i
+
1
]);
davinci_dma_link_lch
(
cur_chan
,
nex_chan
);
}
FN_OUT
(
0
);
return
0
;
}
/******************************************************************************
*
* DMA channel requests Freeing
*
******************************************************************************/
int
davinci_free_sound_dma
(
int
master_ch
,
int
**
channels
)
{
int
i
;
int
*
chan
=
NULL
;
FN_IN
;
if
(
unlikely
(
NULL
==
channels
))
{
BUG
();
return
-
EPERM
;
}
if
(
unlikely
(
NULL
==
*
channels
))
{
BUG
();
return
-
EPERM
;
}
chan
=
(
*
channels
);
/* release the Master channel */
davinci_free_dma
(
master_ch
);
for
(
i
=
0
;
i
<
NUMBER_OF_CHANNELS_TO_LINK
;
i
++
)
{
int
cur_chan
=
chan
[
i
];
int
nex_chan
=
((
NUMBER_OF_CHANNELS_TO_LINK
-
1
==
i
)
?
chan
[
0
]
:
chan
[
i
+
1
]);
davinci_dma_unlink_lch
(
cur_chan
,
nex_chan
);
davinci_free_dma
(
cur_chan
);
}
kfree
(
*
channels
);
*
channels
=
NULL
;
FN_OUT
(
0
);
return
0
;
}
/*******************************************************************************
*
* Process DMA requests - This will end up starting the transfer.
* Proper fragments of Transfers will be initiated.
*
******************************************************************************/
int
audio_process_dma
(
audio_stream_t
*
s
)
{
int
ret
=
0
;
unsigned
long
flags
;
FN_IN
;
/* Dont let the ISR over ride touching the in_use flag */
local_irq_save
(
flags
);
if
(
1
==
s
->
in_use
)
{
local_irq_restore
(
flags
);
DPRINTK
(
"Called again while In Use
\n
"
);
return
0
;
}
s
->
in_use
=
1
;
local_irq_restore
(
flags
);
if
(
s
->
stopped
)
goto
spin
;
if
(
s
->
dma_spinref
>
0
&&
s
->
pending_frags
)
{
s
->
dma_spinref
=
0
;
DMA_CLEAR
(
s
);
}
while
(
s
->
pending_frags
)
{
audio_buf_t
*
b
=
&
s
->
buffers
[
s
->
dma_head
];
u_int
dma_size
=
s
->
fragsize
-
b
->
offset
;
if
(
dma_size
>
MAX_DMA_SIZE
)
{
DPRINTK
(
"dma_size > MAX_DMA_SIZE
\n
"
);
dma_size
=
CUT_DMA_SIZE
;
}
ret
=
davinci_start_sound_dma
(
s
,
b
->
dma_addr
+
b
->
offset
,
dma_size
);
if
(
ret
)
{
DPRINTK
(
"error
\n
"
);
goto
process_out
;
}
b
->
dma_ref
++
;
b
->
offset
+=
dma_size
;
if
(
b
->
offset
>=
s
->
fragsize
)
{
s
->
pending_frags
--
;
if
(
++
s
->
dma_head
>=
s
->
nbfrags
)
s
->
dma_head
=
0
;
}
}
spin:
if
(
s
->
spin_idle
)
{
int
spincnt
=
0
;
DPRINTK
(
"we are spinning
\n
"
);
while
(
davinci_start_sound_dma
(
s
,
SPIN_ADDR
,
SPIN_SIZE
)
==
0
)
spincnt
++
;
/*
* Note: if there is still a data buffer being
* processed then the ref count is negative. This
* allows for the DMA termination to be accounted in
* the proper order. Of course dma_spinref can't be
* greater than 0 if dma_ref is not 0 since we kill
* the spinning above as soon as there is real data to process.
*/
if
(
s
->
buffers
&&
s
->
buffers
[
s
->
dma_tail
].
dma_ref
)
spincnt
=
-
spincnt
;
s
->
dma_spinref
+=
spincnt
;
}
process_out:
s
->
in_use
=
0
;
FN_OUT
(
ret
);
return
ret
;
}
/*******************************************************************************
*
* Prime Rx - Since the recieve buffer has no time limit as to when it would
* arrive, we need to prime it
*
******************************************************************************/
void
audio_prime_rx
(
audio_state_t
*
state
)
{
audio_stream_t
*
is
=
state
->
input_stream
;
FN_IN
;
if
(
state
->
need_tx_for_rx
)
{
/*
* With some codecs like the Philips UDA1341 we must ensure
* there is an output stream at any time while recording since
* this is how the UDA1341 gets its clock from the SA1100.
* So while there is no playback data to send, the output DMA
* will spin with all zeroes. We use the cache flush special
* area for that.
*/
state
->
output_stream
->
spin_idle
=
1
;
audio_process_dma
(
state
->
output_stream
);
}
is
->
pending_frags
=
is
->
nbfrags
;
init_completion
(
&
is
->
wfc
);
is
->
wfc
.
done
=
0
;
is
->
active
=
1
;
audio_process_dma
(
is
);
FN_OUT
(
0
);
return
;
}
/*******************************************************************************
*
* set the fragment size
*
******************************************************************************/
int
audio_set_fragments
(
audio_stream_t
*
s
,
int
val
)
{
FN_IN
;
if
(
s
->
active
)
return
-
EBUSY
;
if
(
s
->
buffers
)
audio_discard_buf
(
s
);
s
->
nbfrags
=
(
val
>>
16
)
&
0x7FFF
;
val
&=
0xFFFF
;
if
(
val
<
4
)
val
=
4
;
if
(
val
>
15
)
val
=
15
;
s
->
fragsize
=
1
<<
val
;
if
(
s
->
nbfrags
<
2
)
s
->
nbfrags
=
2
;
if
(
s
->
nbfrags
*
s
->
fragsize
>
128
*
1024
)
s
->
nbfrags
=
128
*
1024
/
s
->
fragsize
;
FN_OUT
(
0
);
if
(
audio_setup_buf
(
s
))
return
-
ENOMEM
;
return
val
|
(
s
->
nbfrags
<<
16
);
}
/*******************************************************************************
*
* Sync up the buffers before we shutdown, else under-run errors will happen
*
******************************************************************************/
int
audio_sync
(
struct
file
*
file
)
{
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
s
=
state
->
output_stream
;
audio_buf_t
*
b
;
u_int
shiftval
=
0
;
unsigned
long
flags
;
DECLARE_WAITQUEUE
(
wait
,
current
);
FN_IN
;
if
(
!
(
file
->
f_mode
&
FMODE_WRITE
)
||
!
s
->
buffers
||
s
->
mapped
)
{
FN_OUT
(
1
);
return
0
;
}
/*
* Send current buffer if it contains data. Be sure to send
* a full sample count.
*/
b
=
&
s
->
buffers
[
s
->
usr_head
];
mdelay
(
20
);
if
(
b
->
offset
&=
~
3
)
{
/*wait for a buffer to become free */
if
(
wait_for_completion_interruptible
(
&
s
->
wfc
))
return
0
;
/*
* HACK ALERT !
* To avoid increased complexity in the rest of the code
* where full fragment sizes are assumed, we cheat a little
* with the start pointer here and don't forget to restore
* it later.
*/
shiftval
=
s
->
fragsize
-
b
->
offset
;
b
->
offset
=
shiftval
;
b
->
dma_addr
-=
shiftval
;
b
->
data
-=
shiftval
;
local_irq_save
(
flags
);
s
->
bytecount
-=
shiftval
;
if
(
++
s
->
usr_head
>=
s
->
nbfrags
)
s
->
usr_head
=
0
;
s
->
pending_frags
++
;
audio_process_dma
(
s
);
local_irq_restore
(
flags
);
}
/* Let's wait for all buffers to complete */
set_current_state
(
TASK_INTERRUPTIBLE
);
add_wait_queue
(
&
s
->
wq
,
&
wait
);
while
((
s
->
pending_frags
||
(
s
->
wfc
.
done
<
s
->
nbfrags
))
&&
!
signal_pending
(
current
))
{
schedule
();
set_current_state
(
TASK_INTERRUPTIBLE
);
}
set_current_state
(
TASK_RUNNING
);
remove_wait_queue
(
&
s
->
wq
,
&
wait
);
/* undo the pointer hack above */
if
(
shiftval
)
{
local_irq_save
(
flags
);
b
->
dma_addr
+=
shiftval
;
b
->
data
+=
shiftval
;
/* ensure sane DMA code behavior if not yet processed */
if
(
b
->
offset
!=
0
)
b
->
offset
=
s
->
fragsize
;
local_irq_restore
(
flags
);
}
FN_OUT
(
0
);
return
0
;
}
/*******************************************************************************
*
* Stop all the DMA channels of the stream
*
******************************************************************************/
void
audio_stop_dma
(
audio_stream_t
*
s
)
{
unsigned
long
flags
;
FN_IN
;
DPRINTK
(
"audio_stop_dma
\n
"
);
if
(
s
->
dma_spinref
>
0
||
!
s
->
buffers
)
return
;
local_irq_save
(
flags
);
davinci_stop_dma
(
s
->
master_ch
);
s
->
dma_started
=
0
;
if
(
s
->
spin_idle
)
{
#if 0
DMA_START(s, SPIN_ADDR, SPIN_SIZE);
DMA_START(s, SPIN_ADDR, SPIN_SIZE);
#endif
s
->
dma_spinref
=
2
;
}
else
s
->
dma_spinref
=
0
;
local_irq_restore
(
flags
);
FN_OUT
(
0
);
return
;
}
/*******************************************************************************
*
* Get the dma posn
*
******************************************************************************/
u_int
audio_get_dma_pos
(
audio_stream_t
*
s
)
{
audio_buf_t
*
b
=
&
s
->
buffers
[
s
->
dma_tail
];
u_int
offset
=
0
;
FN_IN
;
if
(
b
->
dma_ref
)
{
edmacc_paramentry_regs
temp
;
davinci_get_dma_params
(
s
->
master_ch
,
&
temp
);
if
(
s
->
input_or_output
==
FMODE_WRITE
)
offset
=
temp
.
src
-
b
->
dma_addr
;
else
if
(
s
->
input_or_output
==
FMODE_READ
)
offset
=
temp
.
dst
-
b
->
dma_addr
;
if
(
offset
>=
s
->
fragsize
)
offset
=
s
->
fragsize
-
4
;
}
else
if
(
s
->
pending_frags
)
{
offset
=
b
->
offset
;
}
else
{
offset
=
0
;
}
FN_OUT
(
offset
);
return
offset
;
}
/*******************************************************************************
*
* Reset the audio buffers
*
******************************************************************************/
void
audio_reset
(
audio_stream_t
*
s
)
{
audio_buf_t
*
b
;
FN_IN
;
if
(
s
->
buffers
)
{
davinci_mcbsp_stop
(
0
);
s
->
started
=
0
;
audio_stop_dma
(
s
);
/* back up pointers to be ready to restart from the same spot */
while
(
s
->
dma_head
!=
s
->
dma_tail
)
{
b
=
&
s
->
buffers
[
s
->
dma_head
];
if
(
b
->
dma_ref
)
{
b
->
dma_ref
=
0
;
b
->
offset
=
0
;
}
s
->
pending_frags
++
;
if
(
s
->
dma_head
==
0
)
s
->
dma_head
=
s
->
nbfrags
;
s
->
dma_head
--
;
}
b
=
&
s
->
buffers
[
s
->
dma_head
];
if
(
b
->
dma_ref
)
{
b
->
offset
=
0
;
b
->
dma_ref
=
0
;
}
s
->
buffers
[
s
->
dma_head
].
offset
=
0
;
s
->
buffers
[
s
->
usr_head
].
offset
=
0
;
s
->
usr_head
=
s
->
dma_head
;
s
->
pending_frags
=
0
;
init_completion
(
&
s
->
wfc
);
s
->
wfc
.
done
=
s
->
nbfrags
;
}
AUDIO_QUEUE_INIT
(
s
);
s
->
active
=
0
;
s
->
stopped
=
0
;
FN_OUT
(
0
);
return
;
}
/*******************************************************************************
*
* Clear any pending transfers
*
******************************************************************************/
void
davinci_clear_sound_dma
(
audio_stream_t
*
s
)
{
#if 0
FN_IN;
davinci_clear_dma(s->lch[s->dma_q_head]);
FN_OUT(0);
#endif
return
;
}
/*******************************************************************************
*
* DMA related functions
*
******************************************************************************/
static
int
audio_set_dma_params_play
(
int
channel
,
dma_addr_t
dma_ptr
,
u_int
dma_size
)
{
FN_IN
;
DPRINTK
(
"audio_set_dma_params_play channel = %d dma_ptr = %x \
dma_size=%x
\n
"
,
channel
,
dma_ptr
,
dma_size
);
davinci_set_dma_src_params
(
channel
,
(
unsigned
long
)(
dma_ptr
),
0
,
0
);
davinci_set_dma_dest_params
(
channel
,
(
unsigned
long
)
MCBSP_DXR
,
0
,
0
);
davinci_set_dma_src_index
(
channel
,
2
,
0
);
davinci_set_dma_dest_index
(
channel
,
0
,
0
);
davinci_set_dma_transfer_params
(
channel
,
2
,
dma_size
/
2
,
1
,
0
,
ASYNC
);
FN_OUT
(
0
);
return
0
;
}
static
int
audio_set_dma_params_capture
(
int
channel
,
dma_addr_t
dma_ptr
,
u_int
dma_size
)
{
FN_IN
;
DPRINTK
(
"audio_set_dma_params_capture channel = %d dma_ptr = %x \
dma_size=%x
\n
"
,
channel
,
dma_ptr
,
dma_size
);
davinci_set_dma_src_params
(
channel
,
(
unsigned
long
)
MCBSP_DRR
,
0
,
0
);
davinci_set_dma_dest_params
(
channel
,
(
unsigned
long
)(
dma_ptr
),
0
,
0
);
davinci_set_dma_src_index
(
channel
,
0
,
0
);
davinci_set_dma_dest_index
(
channel
,
2
,
0
);
davinci_set_dma_transfer_params
(
channel
,
2
,
dma_size
/
2
,
1
,
0
,
ASYNC
);
FN_OUT
(
0
);
return
0
;
}
static
int
audio_start_dma_chain
(
audio_stream_t
*
s
)
{
int
channel
=
s
->
lch
[
s
->
dma_q_head
];
FN_IN
;
if
(
!
s
->
dma_started
)
{
edmacc_paramentry_regs
temp
;
davinci_get_dma_params
(
channel
,
&
temp
);
davinci_set_dma_params
(
s
->
master_ch
,
&
temp
);
davinci_start_dma
(
s
->
master_ch
);
s
->
dma_started
=
1
;
}
if
(
!
s
->
started
)
{
davinci_mcbsp_start
(
0
);
s
->
started
=
1
;
}
/* else the dma itself will progress forward with out our help */
FN_OUT
(
0
);
return
0
;
}
/* Start DMA -
* Do the initial set of work to initialize all the channels as required.
* We shall then initate a transfer
*/
static
int
davinci_start_sound_dma
(
audio_stream_t
*
s
,
dma_addr_t
dma_ptr
,
u_int
dma_size
)
{
int
ret
=
-
EPERM
;
FN_IN
;
if
(
unlikely
(
dma_size
>
MAX_DMA_SIZE
))
{
ERR
(
"DmaSoundDma: Start: overflowed %d-%d
\n
"
,
dma_size
,
MAX_DMA_SIZE
);
return
-
EOVERFLOW
;
}
if
(
AUDIO_QUEUE_FULL
(
s
))
{
DPRINTK
(
"queue full
\n
"
);
ret
=
-
2
;
goto
sound_out
;
}
if
(
s
->
input_or_output
==
FMODE_WRITE
)
/*playback */
{
ret
=
audio_set_dma_params_play
(
s
->
lch
[
s
->
dma_q_tail
],
dma_ptr
,
dma_size
);
}
else
{
ret
=
audio_set_dma_params_capture
(
s
->
lch
[
s
->
dma_q_tail
],
dma_ptr
,
dma_size
);
}
if
(
ret
!=
0
)
{
ret
=
-
2
;
/* indicate queue full */
goto
sound_out
;
}
AUDIO_INCREMENT_TAIL
(
s
);
ret
=
audio_start_dma_chain
(
s
);
if
(
ret
)
{
ERR
(
"dma start failed"
);
}
sound_out:
FN_OUT
(
ret
);
return
ret
;
}
/*******************************************************************************
*
* ISR related functions
*
******************************************************************************/
/* The work item handler */
static
void
audio_dsr_handler
(
unsigned
long
inData
)
{
void
*
data
=
(
void
*
)
inData
;
struct
audio_isr_work_item
*
work
=
data
;
audio_stream_t
*
s
=
(
work
->
s
);
int
sound_curr_lch
=
work
->
current_lch
;
u16
ch_status
=
work
->
ch_status
;
FN_IN
;
DPRINTK
(
"lch=%d,status=0x%x, data=%p as=%p
\n
"
,
sound_curr_lch
,
ch_status
,
data
,
s
);
if
(
AUDIO_QUEUE_EMPTY
(
s
))
{
DPRINTK
(
"Interrupt(%d) for empty queue(h=%d, T=%d)???
\n
"
,
sound_curr_lch
,
s
->
dma_q_head
,
s
->
dma_q_tail
);
DPRINTK
(
"nbfrag=%d,pendfrags=%d,USR-H=%d, QH-%d QT-%d
\n
"
,
s
->
nbfrags
,
s
->
pending_frags
,
s
->
usr_head
,
s
->
dma_head
,
s
->
dma_tail
);
AUDIO_INCREMENT_HEAD
(
s
);
/* Empty the queue */
FN_OUT
(
-
1
);
return
;
}
AUDIO_INCREMENT_HEAD
(
s
);
/* Empty the queue */
/* Try to fill again */
audio_dma_callback
(
sound_curr_lch
,
ch_status
,
s
);
FN_OUT
(
0
);
}
/* Macro to trace the IRQ calls - checks for multi-channel irqs */
//#define IRQ_TRACE
#ifdef IRQ_TRACE
#define MAX_UP 10
static
char
xyz
[
MAX_UP
]
=
{
0
};
static
int
h
=
0
;
#endif
/* ISRs have to be short and smart.. So we transfer every heavy duty stuff to
* the work item
*/
static
void
sound_dma_irq_handler
(
int
sound_curr_lch
,
u16
ch_status
,
void
*
data
)
{
audio_stream_t
*
s
=
(
audio_stream_t
*
)
data
;
FN_IN
;
if
(
ch_status
==
DMA_COMPLETE
)
{
#ifdef IRQ_TRACE
xyz
[
h
++
]
=
'0'
+
sound_curr_lch
;
if
(
h
==
MAX_UP
-
1
)
{
DPRINTK
(
"%s-"
,
xyz
);
h
=
0
;
}
#endif
sound_curr_lch
=
s
->
lch
[
s
->
dma_q_head
];
DPRINTK
(
"lch=%d,status=0x%x, data=%p
\n
"
,
sound_curr_lch
,
ch_status
,
data
);
if
(
AUDIO_QUEUE_LAST
(
s
))
{
audio_stream_t
*
s
=
data
;
audio_buf_t
*
b
=
&
s
->
buffers
[
s
->
dma_tail
];
if
(
s
->
dma_spinref
>
0
)
{
s
->
dma_spinref
--
;
}
else
if
(
!
s
->
buffers
)
{
DPRINTK
(
"davinci_audio: received DMA IRQ for non\
existent buffers!
\n
"
);
return
;
}
else
if
(
b
->
dma_ref
&&
--
b
->
dma_ref
==
0
&&
b
->
offset
>=
s
->
fragsize
)
{
/* This fragment is done */
b
->
offset
=
0
;
s
->
bytecount
+=
s
->
fragsize
;
s
->
fragcount
++
;
s
->
dma_spinref
=
-
s
->
dma_spinref
;
if
(
++
s
->
dma_tail
>=
s
->
nbfrags
)
s
->
dma_tail
=
0
;
if
(
!
s
->
mapped
)
{
complete
(
&
s
->
wfc
);
}
else
s
->
pending_frags
++
;
wake_up
(
&
s
->
wq
);
}
AUDIO_INCREMENT_HEAD
(
s
);
audio_stop_dma
(
s
);
return
;
}
/* Start the work item - we ping pong the work items */
if
(
!
work_item_running
)
{
work1
.
current_lch
=
sound_curr_lch
;
work1
.
ch_status
=
ch_status
;
work1
.
s
=
s
;
/* schedule tasklet 1 */
tasklet_schedule
(
&
audio_isr_work1
);
work_item_running
=
1
;
}
else
{
work2
.
current_lch
=
sound_curr_lch
;
work2
.
ch_status
=
ch_status
;
work2
.
s
=
s
;
/* schedule tasklet 2 */
tasklet_schedule
(
&
audio_isr_work2
);
work_item_running
=
0
;
}
}
else
{
DPRINTK
(
"Error in DMA
\n
"
);
}
FN_OUT
(
0
);
return
;
}
/* The call back that handles buffer stuff */
static
void
audio_dma_callback
(
int
lch
,
u16
ch_status
,
void
*
data
)
{
audio_stream_t
*
s
=
data
;
audio_buf_t
*
b
=
&
s
->
buffers
[
s
->
dma_tail
];
FN_IN
;
if
(
s
->
dma_spinref
>
0
)
{
s
->
dma_spinref
--
;
}
else
if
(
!
s
->
buffers
)
{
DPRINTK
(
"davinci_audio: received DMA IRQ for non existent buffers!
\n
"
);
return
;
}
else
if
(
b
->
dma_ref
&&
--
b
->
dma_ref
==
0
&&
b
->
offset
>=
s
->
fragsize
)
{
/* This fragment is done */
b
->
offset
=
0
;
s
->
bytecount
+=
s
->
fragsize
;
s
->
fragcount
++
;
s
->
dma_spinref
=
-
s
->
dma_spinref
;
if
(
++
s
->
dma_tail
>=
s
->
nbfrags
)
s
->
dma_tail
=
0
;
if
(
!
s
->
mapped
)
{
complete
(
&
s
->
wfc
);
}
else
s
->
pending_frags
++
;
wake_up
(
&
s
->
wq
);
}
audio_process_dma
(
s
);
FN_OUT
(
0
);
return
;
}
/*******************************************************************************
*
* audio_get_dma_callback(): return the dma interface call back function
*
******************************************************************************/
dma_callback_t
audio_get_dma_callback
(
void
)
{
FN_IN
;
FN_OUT
(
0
);
return
audio_dma_callback
;
}
MODULE_AUTHOR
(
"Texas Instruments"
);
MODULE_DESCRIPTION
(
"Common DMA handling for Audio driver on DAVINCI processors"
);
MODULE_LICENSE
(
"GPL"
);
EXPORT_SYMBOL
(
davinci_clear_sound_dma
);
EXPORT_SYMBOL
(
davinci_request_sound_dma
);
EXPORT_SYMBOL
(
davinci_free_sound_dma
);
EXPORT_SYMBOL
(
audio_get_dma_callback
);
EXPORT_SYMBOL
(
audio_setup_buf
);
EXPORT_SYMBOL
(
audio_process_dma
);
EXPORT_SYMBOL
(
audio_prime_rx
);
EXPORT_SYMBOL
(
audio_set_fragments
);
EXPORT_SYMBOL
(
audio_sync
);
EXPORT_SYMBOL
(
audio_stop_dma
);
EXPORT_SYMBOL
(
audio_get_dma_pos
);
EXPORT_SYMBOL
(
audio_reset
);
EXPORT_SYMBOL
(
audio_discard_buf
);
sound/oss/davinci-audio-dma-intfc.h
deleted
100644 → 0
View file @
3ccd1e4e
/*
* linux/sound/oss/davinci-audio-dma-intfc.h
*
* Common audio DMA handling for the Davinci processors
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
*
* 2005-10-01 Rishi Bhattacharya / Sharath Kumar - Added support for TI Davinci DM644x processor
*/
#ifndef __DAVINCI_AUDIO_DMA_INTFC_H
#define __DAVINCI_AUDIO_DMA_INTFC_H
/******************************* INCLUDES *************************************/
/* Requires davinci-audio.h */
#include "davinci-audio.h"
/************************** GLOBAL MACROS *************************************/
/* Provide the Macro interfaces common across platforms */
#define DMA_REQUEST(e,s, cb) {e=davinci_request_sound_dma(s->dma_dev, s->id, s, &s->master_ch, &s->lch);}
#define DMA_FREE(s) davinci_free_sound_dma(s->master_ch,&s->lch)
#define DMA_CLEAR(s) davinci_clear_sound_dma(s)
/************************** GLOBAL DATA STRUCTURES ****************************/
typedef
void
(
*
dma_callback_t
)
(
int
lch
,
u16
ch_status
,
void
*
data
);
/************************** GLOBAL FUNCTIONS **********************************/
dma_callback_t
audio_get_dma_callback
(
void
);
int
audio_setup_buf
(
audio_stream_t
*
s
);
int
audio_process_dma
(
audio_stream_t
*
s
);
void
audio_prime_rx
(
audio_state_t
*
state
);
int
audio_set_fragments
(
audio_stream_t
*
s
,
int
val
);
int
audio_sync
(
struct
file
*
file
);
void
audio_stop_dma
(
audio_stream_t
*
s
);
u_int
audio_get_dma_pos
(
audio_stream_t
*
s
);
void
audio_reset
(
audio_stream_t
*
s
);
void
audio_discard_buf
(
audio_stream_t
*
s
);
/**************** ARCH SPECIFIC FUNCIONS **************************************/
void
davinci_clear_sound_dma
(
audio_stream_t
*
s
);
int
davinci_request_sound_dma
(
int
device_id
,
const
char
*
device_name
,
void
*
data
,
int
*
master_ch
,
int
**
channels
);
int
davinci_free_sound_dma
(
int
master_ch
,
int
**
channels
);
#endif
/* #ifndef __DAVINCI_AUDIO_DMA_INTFC_H */
sound/oss/davinci-audio.c
deleted
100644 → 0
View file @
3ccd1e4e
/*
* linux/sound/oss/davinci-audio.c
*
* Common audio handling for the Davinci processors
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
*
* 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms
*
* 2004-11-01 Nishanth Menon - modified to support 16xx and 17xx
* platform multi channel chaining.
*
* 2004-11-04 Nishanth Menon - Added support for power management
*
* 2004-12-17 Nishanth Menon - Provided proper module handling support
*
* 2005-10-01 Rishi Bhattacharya - Adapted to TI Davinci Family of processors
*/
/***************************** INCLUDES ************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/pm.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include "davinci-audio-dma-intfc.h"
#include "davinci-audio.h"
/***************************** MACROS ************************************/
#undef DEBUG
/* #define DEBUG */
#ifdef DEBUG
#define DPRINTK printk
#define FN_IN printk("[davinci_audio.c:[%s] start\n", __FUNCTION__)
#define FN_OUT(n) printk("[davinci_audio.c:[%s] end(%d)\n", __FUNCTION__ , n)
#else
#define DPRINTK( x... )
#define FN_IN
#define FN_OUT(x)
#endif
#define DAVINCI_AUDIO_NAME "davinci-audio"
#define AUDIO_NBFRAGS_DEFAULT 4
#define AUDIO_FRAGSIZE_DEFAULT 3072
/*************/
/* HACK ALERT!: These values will bave to be tuned as this is a trade off b/w
* Sampling Rate vs buffer size and delay we are prepared to do before giving up
*/
#define MAX_QUEUE_FULL_RETRIES 1000000
#define QUEUE_WAIT_TIME 10
#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)
#define SPIN_ADDR (dma_addr_t)0
#define SPIN_SIZE 2048
/********************** MODULES SPECIFIC FUNCTION PROTOTYPES ***************/
static
int
audio_write
(
struct
file
*
file
,
const
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
);
static
int
audio_read
(
struct
file
*
file
,
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
);
static
int
audio_mmap
(
struct
file
*
file
,
struct
vm_area_struct
*
vma
);
static
unsigned
int
audio_poll
(
struct
file
*
file
,
struct
poll_table_struct
*
wait
);
static
loff_t
audio_llseek
(
struct
file
*
file
,
loff_t
offset
,
int
origin
);
static
int
audio_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
uint
cmd
,
ulong
arg
);
static
int
audio_open
(
struct
inode
*
inode
,
struct
file
*
file
);
static
int
audio_release
(
struct
inode
*
inode
,
struct
file
*
file
);
static
int
audio_probe
(
struct
platform_device
*
dev
);
static
int
audio_remove
(
struct
platform_device
*
dev
);
static
void
audio_shutdown
(
struct
platform_device
*
dev
);
static
int
audio_suspend
(
struct
platform_device
*
dev
,
pm_message_t
state
);
static
int
audio_resume
(
struct
platform_device
*
dev
);
/***************************** Data Structures ********************************/
/*
* The function pointer set to be registered by the codec.
*/
static
audio_state_t
audio_state
=
{
0
};
/* DMA Call back function */
static
dma_callback_t
audio_dma_callback
=
0
;
/* File Ops structure */
static
struct
file_operations
davinci_audio_fops
=
{
.
open
=
audio_open
,
.
release
=
audio_release
,
.
write
=
audio_write
,
.
read
=
audio_read
,
.
mmap
=
audio_mmap
,
.
poll
=
audio_poll
,
.
ioctl
=
audio_ioctl
,
.
llseek
=
audio_llseek
,
.
owner
=
THIS_MODULE
};
/* Driver information */
static
struct
platform_driver
davinci_audio_driver
=
{
.
probe
=
audio_probe
,
.
remove
=
audio_remove
,
.
suspend
=
audio_suspend
,
.
resume
=
audio_resume
,
.
shutdown
=
audio_shutdown
,
.
driver
=
{
.
name
=
DAVINCI_AUDIO_NAME
,
},
};
/* Device Information */
static
struct
platform_device
davinci_audio_device
=
{
.
name
=
DAVINCI_AUDIO_NAME
,
.
dev
=
{
.
driver_data
=
&
audio_state
,
},
.
id
=
0
,
};
/***************************** GLOBAL FUNCTIONs *******************************/
#ifdef CONFIG_PM
/* Power Management Functions for Linux Device Model */
/*******************************************************************************
*
* audio_ldm_suspend(): Suspend operation
*
******************************************************************************/
static
int
audio_ldm_suspend
(
void
*
data
)
{
audio_state_t
*
state
=
data
;
FN_IN
;
/*
* Reject the suspend request if we are already actively transmitting data
* Rationale: We dont want to be suspended while in the middle of a call!
*/
if
(
AUDIO_ACTIVE
(
state
)
&&
state
->
hw_init
)
{
DPRINTK
(
"Audio device Active, Cannot Suspend"
);
return
-
EPERM
;
#if 0
/* NOTE:
* This Piece of code is commented out in hope
* That one day we would need to suspend the device while
* audio operations are in progress and resume the operations
* once the resume is done.
* This is just a sample implementation of how it could be done.
* Currently NOT SUPPORTED
*/
audio_stream_t *is = state->input_stream;
audio_stream_t *os = state->output_stream;
int stopstate;
if (is && is->buffers) {
DPRINTK("IS Suspend\n");
stopstate = is->stopped;
audio_stop_dma(is);
DMA_CLEAR(is);
is->dma_spinref = 0;
is->stopped = stopstate;
}
if (os && os->buffers) {
DPRINTK("OS Suspend\n");
stopstate = os->stopped;
audio_stop_dma(os);
DMA_CLEAR(os);
os->dma_spinref = 0;
os->stopped = stopstate;
}
#endif
}
FN_OUT
(
0
);
return
0
;
}
/*******************************************************************************
*
* audio_ldm_resume(): Resume Operations
*
******************************************************************************/
static
int
audio_ldm_resume
(
void
*
data
)
{
audio_state_t
*
state
=
data
;
FN_IN
;
if
(
AUDIO_ACTIVE
(
state
)
&&
state
->
hw_init
)
{
/* Should never occur - since we never suspend with active state */
BUG
();
return
-
EPERM
;
#if 0
/* NOTE:
* This Piece of code is commented out in hope
* That one day we would need to suspend the device while
* audio operations are in progress and resume the operations
* once the resume is done.
* This is just a sample implementation of how it could be done.
* Currently NOT SUPPORTED
*/
audio_stream_t *is = state->input_stream;
audio_stream_t *os = state->output_stream;
if (os && os->buffers) {
DPRINTK("OS Resume\n");
audio_reset(os);
audio_process_dma(os);
}
if (is && is->buffers) {
DPRINTK("IS Resume\n");
audio_reset(is);
audio_process_dma(is);
}
#endif
}
FN_OUT
(
0
);
return
0
;
}
#endif
/* End of #ifdef CONFIG_PM */
/*******************************************************************************
*
* audio_probe(): The Audio driver probe function
* WARNING!!!! : It is expected that the codec would have registered with us by now
*
******************************************************************************/
static
int
audio_probe
(
struct
platform_device
*
dev
)
{
int
ret
;
FN_IN
;
if
(
!
audio_state
.
hw_probe
)
{
DPRINTK
(
"Probe Function Not Registered
\n
"
);
return
-
ENODEV
;
}
ret
=
audio_state
.
hw_probe
();
FN_OUT
(
ret
);
return
ret
;
}
/*******************************************************************************
*
* audio_remove() Function to handle removal operations
*
******************************************************************************/
static
int
audio_remove
(
struct
platform_device
*
dev
)
{
FN_IN
;
if
(
audio_state
.
hw_remove
)
{
audio_state
.
hw_remove
();
}
FN_OUT
(
0
);
return
0
;
}
/*******************************************************************************
*
* audio_shutdown(): Function to handle shutdown operations
*
******************************************************************************/
static
void
audio_shutdown
(
struct
platform_device
*
dev
)
{
FN_IN
;
if
(
audio_state
.
hw_cleanup
)
{
audio_state
.
hw_cleanup
();
}
FN_OUT
(
0
);
return
;
}
/*******************************************************************************
*
* audio_suspend(): Function to handle suspend operations
*
******************************************************************************/
static
int
audio_suspend
(
struct
platform_device
*
dev
,
pm_message_t
state
)
{
int
ret
=
0
;
#ifdef CONFIG_PM
void
*
data
=
dev
->
dev
.
platform_data
;
FN_IN
;
if
(
audio_state
.
hw_suspend
)
{
ret
=
audio_ldm_suspend
(
&
data
);
if
(
ret
==
0
)
ret
=
audio_state
.
hw_suspend
();
}
if
(
ret
)
{
DPRINTK
(
"Audio suspend failed
\n
"
);
}
else
{
DPRINTK
(
"Audio suspend success
\n
"
);
}
#endif
/* CONFIG_PM */
FN_OUT
(
ret
);
return
ret
;
}
/*******************************************************************************
*
* audio_resume(): Function to handle resume operations
*
******************************************************************************/
static
int
audio_resume
(
struct
platform_device
*
dev
)
{
int
ret
=
0
;
#ifdef CONFIG_PM
void
*
data
=
dev
->
dev
.
platform_data
;
FN_IN
;
if
(
audio_state
.
hw_resume
)
{
ret
=
audio_ldm_resume
(
&
data
);
if
(
ret
==
0
)
ret
=
audio_state
.
hw_resume
();
}
if
(
ret
)
{
DPRINTK
(
"Audio resume failed
\n
"
);
}
else
{
DPRINTK
(
"Audio resume success
\n
"
);
}
#endif
/* CONFIG_PM */
FN_OUT
(
ret
);
return
ret
;
}
/*******************************************************************************
*
* audio_get_fops(): Return the fops required to get the function pointers of
* DAVINCI Audio Driver
*
******************************************************************************/
struct
file_operations
*
audio_get_fops
(
void
)
{
FN_IN
;
FN_OUT
(
0
);
return
&
davinci_audio_fops
;
}
/*******************************************************************************
*
* audio_register_codec(): Register a Codec fn points using this function
* WARNING!!!!! : Codecs should ensure that they do so! no sanity checks
* during runtime is done due to obvious performance
* penalties.
*
******************************************************************************/
int
audio_register_codec
(
audio_state_t
*
codec_state
)
{
int
ret
;
FN_IN
;
/* We dont handle multiple codecs now */
if
(
audio_state
.
hw_init
)
{
DPRINTK
(
" Codec Already registered
\n
"
);
return
-
EPERM
;
}
/* Grab the dma Callback */
audio_dma_callback
=
audio_get_dma_callback
();
if
(
!
audio_dma_callback
)
{
DPRINTK
(
"Unable to get call back function
\n
"
);
return
-
EPERM
;
}
/* Sanity checks */
if
(
!
codec_state
)
{
DPRINTK
(
"NULL ARGUMENT!
\n
"
);
return
-
EPERM
;
}
if
(
!
codec_state
->
hw_probe
||
!
codec_state
->
hw_init
||
!
codec_state
->
hw_shutdown
||
!
codec_state
->
client_ioctl
)
{
DPRINTK
(
"Required Fn Entry point Missing probe=%p init=%p,down=%p,ioctl=%p!
\n
"
,
codec_state
->
hw_probe
,
codec_state
->
hw_init
,
codec_state
->
hw_shutdown
,
codec_state
->
client_ioctl
);
return
-
EPERM
;
}
memcpy
(
&
audio_state
,
codec_state
,
sizeof
(
audio_state_t
));
sema_init
(
&
audio_state
.
sem
,
1
);
ret
=
platform_device_register
(
&
davinci_audio_device
);
if
(
ret
!=
0
)
{
DPRINTK
(
"Platform device register failed =%d
\n
"
,
ret
);
ret
=
-
ENODEV
;
goto
register_out
;
}
ret
=
platform_driver_register
(
&
davinci_audio_driver
);
if
(
ret
!=
0
)
{
DPRINTK
(
"Platform driver register failed =%d
\n
"
,
ret
);
ret
=
-
ENODEV
;
platform_device_unregister
(
&
davinci_audio_device
);
goto
register_out
;
}
DPRINTK
(
"Audio driver register success
\n
"
);
register_out:
FN_OUT
(
ret
);
return
ret
;
}
/*******************************************************************************
*
* audio_unregister_codec(): Un-Register a Codec using this function
*
******************************************************************************/
int
audio_unregister_codec
(
audio_state_t
*
codec_state
)
{
FN_IN
;
/* We dont handle multiple codecs now */
if
(
!
audio_state
.
hw_init
)
{
DPRINTK
(
" No Codec registered
\n
"
);
return
-
EPERM
;
}
/* Security check */
if
(
audio_state
.
hw_init
!=
codec_state
->
hw_init
)
{
DPRINTK
(
"Attempt to unregister codec which was not registered with us
\n
"
);
return
-
EPERM
;
}
platform_driver_unregister
(
&
davinci_audio_driver
);
platform_device_unregister
(
&
davinci_audio_device
);
memset
(
&
audio_state
,
0
,
sizeof
(
audio_state_t
));
FN_OUT
(
0
);
return
0
;
}
/***************************** MODULES SPECIFIC FUNCTION **********************/
/*******************************************************************************
*
* audio_write(): Exposed to write() call
*
******************************************************************************/
static
int
audio_write
(
struct
file
*
file
,
const
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
const
char
*
buffer0
=
buffer
;
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
s
=
state
->
output_stream
;
int
chunksize
,
ret
=
0
;
DPRINTK
(
"audio_write: count=%d
\n
"
,
count
);
if
(
*
ppos
!=
file
->
f_pos
)
{
DPRINTK
(
"FPOS not ppos ppos=0x%x fpos =0x%x
\n
"
,
(
u32
)
*
ppos
,
(
u32
)
file
->
f_pos
);
return
-
ESPIPE
;
}
if
(
s
->
mapped
)
{
DPRINTK
(
"s already mapped
\n
"
);
return
-
ENXIO
;
}
if
(
!
s
->
buffers
&&
audio_setup_buf
(
s
))
{
DPRINTK
(
"NO MEMORY
\n
"
);
return
-
ENOMEM
;
}
while
(
count
>
0
)
{
audio_buf_t
*
b
=
&
s
->
buffers
[
s
->
usr_head
];
/* Wait for a buffer to become free */
if
(
file
->
f_flags
&
O_NONBLOCK
)
{
ret
=
-
EAGAIN
;
if
(
!
s
->
wfc
.
done
)
break
;
}
else
{
ret
=
-
ERESTARTSYS
;
if
(
wait_for_completion_interruptible
(
&
s
->
wfc
))
break
;
}
/* Feed the current buffer */
chunksize
=
s
->
fragsize
-
b
->
offset
;
if
(
chunksize
>
count
)
chunksize
=
count
;
DPRINTK
(
"write %d to %d
\n
"
,
chunksize
,
s
->
usr_head
);
if
(
copy_from_user
(
b
->
data
+
b
->
offset
,
buffer
,
chunksize
))
{
DPRINTK
(
"Audio: CopyFrom User failed
\n
"
);
complete
(
&
s
->
wfc
);
return
-
EFAULT
;
}
buffer
+=
chunksize
;
count
-=
chunksize
;
b
->
offset
+=
chunksize
;
if
(
b
->
offset
<
s
->
fragsize
)
{
complete
(
&
s
->
wfc
);
break
;
}
/* Update pointers and send current fragment to DMA */
b
->
offset
=
0
;
if
(
++
s
->
usr_head
>=
s
->
nbfrags
)
s
->
usr_head
=
0
;
/* Add the num of frags pending */
s
->
pending_frags
++
;
s
->
active
=
1
;
audio_process_dma
(
s
);
}
if
((
buffer
-
buffer0
))
ret
=
buffer
-
buffer0
;
DPRINTK
(
"audio_write: return=%d
\n
"
,
ret
);
return
ret
;
}
/*******************************************************************************
*
* audio_read(): Exposed as read() function
*
******************************************************************************/
static
int
audio_read
(
struct
file
*
file
,
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
char
*
buffer0
=
buffer
;
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
s
=
state
->
input_stream
;
int
chunksize
,
ret
=
0
;
unsigned
long
flags
;
DPRINTK
(
"audio_read: count=%d
\n
"
,
count
);
if
(
*
ppos
!=
file
->
f_pos
)
{
DPRINTK
(
"AudioRead - FPOS not ppos ppos=0x%x fpos =0x%x
\n
"
,
(
u32
)
*
ppos
,
(
u32
)
file
->
f_pos
);
return
-
ESPIPE
;
}
if
(
s
->
mapped
)
{
DPRINTK
(
"AudioRead - s already mapped
\n
"
);
return
-
ENXIO
;
}
if
(
!
s
->
active
)
{
if
(
!
s
->
buffers
&&
audio_setup_buf
(
s
))
{
DPRINTK
(
"AudioRead - No Memory
\n
"
);
return
-
ENOMEM
;
}
audio_prime_rx
(
state
);
}
while
(
count
>
0
)
{
audio_buf_t
*
b
=
&
s
->
buffers
[
s
->
usr_head
];
/* Wait for a buffer to become full */
if
(
file
->
f_flags
&
O_NONBLOCK
)
{
ret
=
-
EAGAIN
;
if
(
!
s
->
wfc
.
done
)
break
;
}
else
{
ret
=
-
ERESTARTSYS
;
if
(
wait_for_completion_interruptible
(
&
s
->
wfc
))
break
;
}
/* Grab data from the current buffer */
chunksize
=
s
->
fragsize
-
b
->
offset
;
if
(
chunksize
>
count
)
chunksize
=
count
;
DPRINTK
(
"read %d from %d
\n
"
,
chunksize
,
s
->
usr_head
);
if
(
copy_to_user
(
buffer
,
b
->
data
+
b
->
offset
,
chunksize
))
{
complete
(
&
s
->
wfc
);
return
-
EFAULT
;
}
buffer
+=
chunksize
;
count
-=
chunksize
;
b
->
offset
+=
chunksize
;
if
(
b
->
offset
<
s
->
fragsize
)
{
complete
(
&
s
->
wfc
);
break
;
}
/* Update pointers and return current fragment to DMA */
local_irq_save
(
flags
);
b
->
offset
=
0
;
if
(
++
s
->
usr_head
>=
s
->
nbfrags
)
s
->
usr_head
=
0
;
s
->
pending_frags
++
;
local_irq_restore
(
flags
);
DPRINTK
(
KERN_INFO
"calling audio_process_dma from audio_read
\n
"
);
audio_process_dma
(
s
);
}
if
((
buffer
-
buffer0
))
ret
=
buffer
-
buffer0
;
DPRINTK
(
"audio_read: return=%d
\n
"
,
ret
);
return
ret
;
}
/*******************************************************************************
*
* audio_mmap(): Exposed as mmap Function
* !!WARNING: Still under development
*
******************************************************************************/
static
int
audio_mmap
(
struct
file
*
file
,
struct
vm_area_struct
*
vma
)
{
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
s
;
unsigned
long
size
,
vma_addr
;
int
i
,
ret
=
0
;
FN_IN
;
if
(
vma
->
vm_pgoff
!=
0
)
return
-
EINVAL
;
if
(
vma
->
vm_flags
&
VM_WRITE
)
{
if
(
!
state
->
wr_ref
)
return
-
EINVAL
;;
s
=
state
->
output_stream
;
}
else
if
(
vma
->
vm_flags
&
VM_READ
)
{
if
(
!
state
->
rd_ref
)
return
-
EINVAL
;
s
=
state
->
input_stream
;
}
else
return
-
EINVAL
;
if
(
s
->
mapped
)
return
-
EINVAL
;
size
=
vma
->
vm_end
-
vma
->
vm_start
;
if
(
size
!=
s
->
fragsize
*
s
->
nbfrags
)
return
-
EINVAL
;
if
(
!
s
->
buffers
&&
audio_setup_buf
(
s
))
return
-
ENOMEM
;
vma_addr
=
vma
->
vm_start
;
for
(
i
=
0
;
i
<
s
->
nbfrags
;
i
++
)
{
audio_buf_t
*
buf
=
&
s
->
buffers
[
i
];
if
(
!
buf
->
master
)
continue
;
#if 0
ret =
remap_pfn_range(vma, vma_addr, buf->dma_addr >> PAGE_SHIFT,
buf->master, vma->vm_page_prot);
#endif
if
(
ret
)
return
ret
;
vma_addr
+=
buf
->
master
;
}
s
->
mapped
=
1
;
FN_OUT
(
0
);
return
0
;
}
/*******************************************************************************
*
* audio_poll(): Exposed as poll function
*
******************************************************************************/
static
unsigned
int
audio_poll
(
struct
file
*
file
,
struct
poll_table_struct
*
wait
)
{
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
is
=
state
->
input_stream
;
audio_stream_t
*
os
=
state
->
output_stream
;
unsigned
int
mask
=
0
;
DPRINTK
(
"audio_poll(): mode=%s%s
\n
"
,
(
file
->
f_mode
&
FMODE_READ
)
?
"r"
:
""
,
(
file
->
f_mode
&
FMODE_WRITE
)
?
"w"
:
""
);
if
(
file
->
f_mode
&
FMODE_READ
)
{
/* Start audio input if not already active */
if
(
!
is
->
active
)
{
if
(
!
is
->
buffers
&&
audio_setup_buf
(
is
))
return
-
ENOMEM
;
audio_prime_rx
(
state
);
}
poll_wait
(
file
,
&
is
->
wq
,
wait
);
}
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
if
(
!
os
->
buffers
&&
audio_setup_buf
(
os
))
return
-
ENOMEM
;
poll_wait
(
file
,
&
os
->
wq
,
wait
);
}
if
(
file
->
f_mode
&
FMODE_READ
)
if
((
is
->
mapped
&&
is
->
bytecount
>
0
)
||
(
!
is
->
mapped
&&
is
->
wfc
.
done
>
0
))
mask
|=
POLLIN
|
POLLRDNORM
;
if
(
file
->
f_mode
&
FMODE_WRITE
)
if
((
os
->
mapped
&&
os
->
bytecount
>
0
)
||
(
!
os
->
mapped
&&
os
->
wfc
.
done
>
0
))
mask
|=
POLLOUT
|
POLLWRNORM
;
DPRINTK
(
"audio_poll() returned mask of %s%s
\n
"
,
(
mask
&
POLLIN
)
?
"r"
:
""
,
(
mask
&
POLLOUT
)
?
"w"
:
""
);
FN_OUT
(
mask
);
return
mask
;
}
/*******************************************************************************
*
* audio_llseek(): Exposed as lseek() function.
*
******************************************************************************/
static
loff_t
audio_llseek
(
struct
file
*
file
,
loff_t
offset
,
int
origin
)
{
FN_IN
;
FN_OUT
(
0
);
return
-
ESPIPE
;
}
/*******************************************************************************
*
* audio_ioctl(): Handles generic ioctls. If there is a request for something this
* fn cannot handle, its then given to client specific ioctl routine, that will take
* up platform specific requests
*
******************************************************************************/
static
int
audio_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
uint
cmd
,
ulong
arg
)
{
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
os
=
state
->
output_stream
;
audio_stream_t
*
is
=
state
->
input_stream
;
long
val
;
DPRINTK
(
__FILE__
" audio_ioctl 0x%08x
\n
"
,
cmd
);
/* dispatch based on command */
switch
(
cmd
)
{
case
OSS_GETVERSION
:
return
put_user
(
SOUND_VERSION
,
(
int
*
)
arg
);
case
SNDCTL_DSP_GETBLKSIZE
:
if
(
file
->
f_mode
&
FMODE_WRITE
)
return
put_user
(
os
->
fragsize
,
(
int
*
)
arg
);
else
return
put_user
(
is
->
fragsize
,
(
int
*
)
arg
);
case
SNDCTL_DSP_GETCAPS
:
val
=
DSP_CAP_REALTIME
|
DSP_CAP_TRIGGER
|
DSP_CAP_MMAP
;
if
(
is
&&
os
)
val
|=
DSP_CAP_DUPLEX
;
FN_OUT
(
1
);
return
put_user
(
val
,
(
int
*
)
arg
);
case
SNDCTL_DSP_SETFRAGMENT
:
if
(
get_user
(
val
,
(
long
*
)
arg
))
{
FN_OUT
(
2
);
return
-
EFAULT
;
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
int
ret
=
audio_set_fragments
(
is
,
val
);
if
(
ret
<
0
)
{
FN_OUT
(
3
);
return
ret
;
}
ret
=
put_user
(
ret
,
(
int
*
)
arg
);
if
(
ret
)
{
FN_OUT
(
4
);
return
ret
;
}
}
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
int
ret
=
audio_set_fragments
(
os
,
val
);
if
(
ret
<
0
)
{
FN_OUT
(
5
);
return
ret
;
}
ret
=
put_user
(
ret
,
(
int
*
)
arg
);
if
(
ret
)
{
FN_OUT
(
6
);
return
ret
;
}
}
FN_OUT
(
7
);
return
0
;
case
SNDCTL_DSP_SYNC
:
FN_OUT
(
8
);
return
audio_sync
(
file
);
case
SNDCTL_DSP_SETDUPLEX
:
FN_OUT
(
9
);
return
0
;
case
SNDCTL_DSP_POST
:
FN_OUT
(
10
);
return
0
;
case
SNDCTL_DSP_GETTRIGGER
:
val
=
0
;
if
(
file
->
f_mode
&
FMODE_READ
&&
is
->
active
&&
!
is
->
stopped
)
val
|=
PCM_ENABLE_INPUT
;
if
(
file
->
f_mode
&
FMODE_WRITE
&&
os
->
active
&&
!
os
->
stopped
)
val
|=
PCM_ENABLE_OUTPUT
;
FN_OUT
(
11
);
return
put_user
(
val
,
(
int
*
)
arg
);
case
SNDCTL_DSP_SETTRIGGER
:
if
(
get_user
(
val
,
(
int
*
)
arg
))
{
FN_OUT
(
12
);
return
-
EFAULT
;
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
if
(
val
&
PCM_ENABLE_INPUT
)
{
unsigned
long
flags
;
if
(
!
is
->
active
)
{
if
(
!
is
->
buffers
&&
audio_setup_buf
(
is
))
{
FN_OUT
(
13
);
return
-
ENOMEM
;
}
audio_prime_rx
(
state
);
}
local_irq_save
(
flags
);
is
->
stopped
=
0
;
local_irq_restore
(
flags
);
audio_process_dma
(
is
);
}
else
{
is
->
stopped
=
1
;
audio_stop_dma
(
is
);
}
}
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
if
(
val
&
PCM_ENABLE_OUTPUT
)
{
unsigned
long
flags
;
if
(
!
os
->
buffers
&&
audio_setup_buf
(
os
))
{
FN_OUT
(
14
);
return
-
ENOMEM
;
}
local_irq_save
(
flags
);
if
(
os
->
mapped
&&
!
os
->
pending_frags
)
{
os
->
pending_frags
=
os
->
nbfrags
;
init_completion
(
&
os
->
wfc
);
os
->
wfc
.
done
=
0
;
os
->
active
=
1
;
}
os
->
stopped
=
0
;
local_irq_restore
(
flags
);
audio_process_dma
(
os
);
}
else
{
os
->
stopped
=
1
;
audio_stop_dma
(
os
);
}
}
FN_OUT
(
15
);
return
0
;
case
SNDCTL_DSP_GETOPTR
:
case
SNDCTL_DSP_GETIPTR
:
{
count_info
inf
=
{
0
,
};
audio_stream_t
*
s
=
(
cmd
==
SNDCTL_DSP_GETOPTR
)
?
os
:
is
;
int
bytecount
,
offset
;
unsigned
long
flags
;
if
((
s
==
is
&&
!
(
file
->
f_mode
&
FMODE_READ
))
||
(
s
==
os
&&
!
(
file
->
f_mode
&
FMODE_WRITE
)))
{
FN_OUT
(
16
);
return
-
EINVAL
;
}
if
(
s
->
active
)
{
local_irq_save
(
flags
);
offset
=
audio_get_dma_pos
(
s
);
inf
.
ptr
=
s
->
dma_tail
*
s
->
fragsize
+
offset
;
bytecount
=
s
->
bytecount
+
offset
;
s
->
bytecount
=
-
offset
;
inf
.
blocks
=
s
->
fragcount
;
s
->
fragcount
=
0
;
local_irq_restore
(
flags
);
if
(
bytecount
<
0
)
bytecount
=
0
;
inf
.
bytes
=
bytecount
;
}
FN_OUT
(
17
);
return
copy_to_user
((
void
*
)
arg
,
&
inf
,
sizeof
(
inf
));
}
case
SNDCTL_DSP_GETOSPACE
:
case
SNDCTL_DSP_GETISPACE
:
{
audio_buf_info
inf
=
{
0
,
};
audio_stream_t
*
s
=
(
cmd
==
SNDCTL_DSP_GETOSPACE
)
?
os
:
is
;
audio_buf_t
*
b
=
NULL
;
if
((
s
==
is
&&
!
(
file
->
f_mode
&
FMODE_READ
))
||
(
s
==
os
&&
!
(
file
->
f_mode
&
FMODE_WRITE
)))
{
FN_OUT
(
18
);
return
-
EINVAL
;
}
if
(
!
s
->
buffers
&&
audio_setup_buf
(
s
))
{
FN_OUT
(
19
);
return
-
ENOMEM
;
}
b
=
&
s
->
buffers
[
s
->
usr_head
];
inf
.
bytes
=
s
->
wfc
.
done
*
s
->
fragsize
;
inf
.
bytes
-=
b
->
offset
;
if
(
inf
.
bytes
<
0
)
inf
.
bytes
=
0
;
inf
.
fragments
=
inf
.
bytes
/
s
->
fragsize
;
inf
.
fragsize
=
s
->
fragsize
;
inf
.
fragstotal
=
s
->
nbfrags
;
FN_OUT
(
20
);
return
copy_to_user
((
void
*
)
arg
,
&
inf
,
sizeof
(
inf
));
}
case
SNDCTL_DSP_NONBLOCK
:
file
->
f_flags
|=
O_NONBLOCK
;
FN_OUT
(
21
);
return
0
;
case
SNDCTL_DSP_RESET
:
if
(
file
->
f_mode
&
FMODE_READ
)
{
audio_reset
(
is
);
if
(
state
->
need_tx_for_rx
)
{
unsigned
long
flags
;
local_irq_save
(
flags
);
os
->
spin_idle
=
0
;
local_irq_restore
(
flags
);
}
}
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
audio_reset
(
os
);
}
FN_OUT
(
22
);
return
0
;
default:
/*
* Let the client of this module handle the
* non generic ioctls
*/
FN_OUT
(
23
);
return
state
->
client_ioctl
(
inode
,
file
,
cmd
,
arg
);
}
FN_OUT
(
0
);
return
0
;
}
/*******************************************************************************
*
* audio_open(): Exposed as open() function
*
******************************************************************************/
static
int
audio_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
audio_state_t
*
state
=
(
&
audio_state
);
audio_stream_t
*
os
=
state
->
output_stream
;
audio_stream_t
*
is
=
state
->
input_stream
;
int
err
,
need_tx_dma
;
static
unsigned
char
aic33_init_flag
=
0
;
FN_IN
;
/* Lock the module */
if
(
!
try_module_get
(
THIS_MODULE
))
{
DPRINTK
(
"Failed to get module
\n
"
);
return
-
ESTALE
;
}
/* Lock the codec module */
if
(
!
try_module_get
(
state
->
owner
))
{
DPRINTK
(
"Failed to get codec module
\n
"
);
module_put
(
THIS_MODULE
);
return
-
ESTALE
;
}
down
(
&
state
->
sem
);
/* access control */
err
=
-
ENODEV
;
if
((
file
->
f_mode
&
FMODE_WRITE
)
&&
!
os
)
goto
out
;
if
((
file
->
f_mode
&
FMODE_READ
)
&&
!
is
)
goto
out
;
err
=
-
EBUSY
;
if
((
file
->
f_mode
&
FMODE_WRITE
)
&&
state
->
wr_ref
)
goto
out
;
if
((
file
->
f_mode
&
FMODE_READ
)
&&
state
->
rd_ref
)
goto
out
;
err
=
-
EINVAL
;
if
((
file
->
f_mode
&
FMODE_READ
)
&&
state
->
need_tx_for_rx
&&
!
os
)
goto
out
;
/* request DMA channels */
need_tx_dma
=
((
file
->
f_mode
&
FMODE_WRITE
)
||
((
file
->
f_mode
&
FMODE_READ
)
&&
state
->
need_tx_for_rx
));
if
(
state
->
wr_ref
||
(
state
->
rd_ref
&&
state
->
need_tx_for_rx
))
need_tx_dma
=
0
;
if
(
need_tx_dma
)
{
DPRINTK
(
"DMA REQUEST FOR playback
\n
"
);
DMA_REQUEST
(
err
,
os
,
audio_dma_callback
);
if
(
err
<
0
)
goto
out
;
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
DPRINTK
(
"DMA REQUEST FOR record
\n
"
);
DMA_REQUEST
(
err
,
is
,
audio_dma_callback
);
if
(
err
<
0
)
{
if
(
need_tx_dma
)
DMA_FREE
(
os
);
goto
out
;
}
}
/* now complete initialisation */
if
(
!
AUDIO_ACTIVE
(
state
))
{
if
(
state
->
hw_init
&&
!
aic33_init_flag
)
{
state
->
hw_init
(
state
->
data
);
aic33_init_flag
=
0
;
}
}
if
((
file
->
f_mode
&
FMODE_WRITE
))
{
DPRINTK
(
"SETUP FOR PLAYBACK
\n
"
);
state
->
wr_ref
=
1
;
audio_reset
(
os
);
os
->
fragsize
=
AUDIO_FRAGSIZE_DEFAULT
;
os
->
nbfrags
=
AUDIO_NBFRAGS_DEFAULT
;
os
->
mapped
=
0
;
init_waitqueue_head
(
&
os
->
wq
);
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
DPRINTK
(
"SETUP FOR RECORD
\n
"
);
state
->
rd_ref
=
1
;
audio_reset
(
is
);
is
->
fragsize
=
AUDIO_FRAGSIZE_DEFAULT
;
is
->
nbfrags
=
AUDIO_NBFRAGS_DEFAULT
;
is
->
mapped
=
0
;
init_waitqueue_head
(
&
is
->
wq
);
}
file
->
private_data
=
state
;
err
=
0
;
out:
up
(
&
state
->
sem
);
if
(
err
)
{
module_put
(
state
->
owner
);
module_put
(
THIS_MODULE
);
}
FN_OUT
(
err
);
return
err
;
}
/*******************************************************************************
*
* audio_release(): Exposed as release function()
*
******************************************************************************/
static
int
audio_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
audio_state_t
*
state
=
file
->
private_data
;
audio_stream_t
*
os
=
state
->
output_stream
;
audio_stream_t
*
is
=
state
->
input_stream
;
FN_IN
;
down
(
&
state
->
sem
);
if
(
file
->
f_mode
&
FMODE_READ
)
{
audio_discard_buf
(
is
);
DMA_FREE
(
is
);
is
->
dma_spinref
=
0
;
if
(
state
->
need_tx_for_rx
)
{
os
->
spin_idle
=
0
;
if
(
!
state
->
wr_ref
)
{
DMA_FREE
(
os
);
os
->
dma_spinref
=
0
;
}
}
state
->
rd_ref
=
0
;
}
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
audio_sync
(
file
);
audio_discard_buf
(
os
);
if
(
!
state
->
need_tx_for_rx
||
!
state
->
rd_ref
)
{
DMA_FREE
(
os
);
os
->
dma_spinref
=
0
;
}
state
->
wr_ref
=
0
;
}
up
(
&
state
->
sem
);
module_put
(
state
->
owner
);
module_put
(
THIS_MODULE
);
FN_OUT
(
0
);
return
0
;
}
EXPORT_SYMBOL
(
audio_register_codec
);
EXPORT_SYMBOL
(
audio_unregister_codec
);
EXPORT_SYMBOL
(
audio_get_fops
);
MODULE_AUTHOR
(
"Texas Instruments"
);
MODULE_DESCRIPTION
(
"Common audio handling for DAVINCI processors"
);
MODULE_LICENSE
(
"GPL"
);
sound/oss/davinci-audio.h
deleted
100644 → 0
View file @
3ccd1e4e
/*
* linux/sound/oss/davinci-audio.h
*
* Common audio handling for the Davinci processors
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
*
* 2005-10-01 Rishi Bhattacharya - Adapted to TI Davinci Family of processors
*/
#ifndef __DAVINCI_AUDIO_H
#define __DAVINCI_AUDIO_H
/* Requires dma.h */
#include <mach/dma.h>
/*
* Buffer Management
*/
typedef
struct
{
int
offset
;
/* current offset */
char
*
data
;
/* points to actual buffer */
dma_addr_t
dma_addr
;
/* physical buffer address */
int
dma_ref
;
/* DMA refcount */
int
master
;
/* owner for buffer allocation, contain size when true */
}
audio_buf_t
;
/*
* Structure describing the data stream related information
*/
typedef
struct
{
char
*
id
;
/* identification string */
audio_buf_t
*
buffers
;
/* pointer to audio buffer structures */
u_int
usr_head
;
/* user fragment index */
u_int
dma_head
;
/* DMA fragment index to go */
u_int
dma_tail
;
/* DMA fragment index to complete */
u_int
fragsize
;
/* fragment i.e. buffer size */
u_int
nbfrags
;
/* nbr of fragments i.e. buffers */
u_int
pending_frags
;
/* Fragments sent to DMA */
int
dma_dev
;
/* device identifier for DMA */
u_int
prevbuf
;
/* Prev pending frag size sent to DMA */
char
started
;
/* to store if the chain was started or not */
int
dma_q_head
;
/* DMA Channel Q Head */
int
dma_q_tail
;
/* DMA Channel Q Tail */
char
dma_q_count
;
/* DMA Channel Q Count */
char
in_use
;
/* Is this is use? */
int
master_ch
;
int
*
lch
;
/* Chain of channels this stream is linked to */
int
input_or_output
;
/* Direction of this data stream */
int
bytecount
;
/* nbr of processed bytes */
int
fragcount
;
/* nbr of fragment transitions */
struct
completion
wfc
;
/* wait for "nbfrags" fragment completion */
wait_queue_head_t
wq
;
/* for poll */
int
dma_spinref
;
/* DMA is spinning */
int
mapped
:
1
;
/* mmap()'ed buffers */
int
active
:
1
;
/* actually in progress */
int
stopped
:
1
;
/* might be active but stopped */
int
spin_idle
:
1
;
/* have DMA spin on zeros when idle */
int
dma_started
;
/* to store if DMA was started or not */
}
audio_stream_t
;
/*
* State structure for one instance
*/
typedef
struct
{
struct
module
*
owner
;
/* Codec module ID */
audio_stream_t
*
output_stream
;
audio_stream_t
*
input_stream
;
int
rd_ref
:
1
;
/* open reference for recording */
int
wr_ref
:
1
;
/* open reference for playback */
int
need_tx_for_rx
:
1
;
/* if data must be sent while receiving */
void
*
data
;
void
(
*
hw_init
)
(
void
*
);
void
(
*
hw_shutdown
)
(
void
*
);
int
(
*
client_ioctl
)
(
struct
inode
*
,
struct
file
*
,
uint
,
ulong
);
int
(
*
hw_probe
)
(
void
);
void
(
*
hw_remove
)
(
void
);
void
(
*
hw_cleanup
)
(
void
);
int
(
*
hw_suspend
)
(
void
);
int
(
*
hw_resume
)
(
void
);
struct
pm_dev
*
pm_dev
;
struct
semaphore
sem
;
/* to protect against races in attach() */
}
audio_state_t
;
#ifdef AUDIO_PM
void
audio_ldm_suspend
(
void
*
data
);
void
audio_ldm_resume
(
void
*
data
);
#endif
/* Register a Codec using this function */
extern
int
audio_register_codec
(
audio_state_t
*
codec_state
);
/* Un-Register a Codec using this function */
extern
int
audio_unregister_codec
(
audio_state_t
*
codec_state
);
/* Function to provide fops of davinci audio driver */
extern
struct
file_operations
*
audio_get_fops
(
void
);
/* Function to initialize the device info for audio driver */
extern
int
audio_dev_init
(
void
);
/* Function to un-initialize the device info for audio driver */
void
audio_dev_uninit
(
void
);
#endif
/* End of #ifndef __DAVINCI_AUDIO_H */
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