Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
3bc32492
Commit
3bc32492
authored
Dec 02, 2012
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/atmel' into asoc-next
parents
9d3493e8
3951e4aa
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
1108 additions
and
611 deletions
+1108
-611
Documentation/devicetree/bindings/misc/atmel-ssc.txt
Documentation/devicetree/bindings/misc/atmel-ssc.txt
+15
-0
Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt
...etree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt
+26
-0
arch/arm/boot/dts/at91sam9260.dtsi
arch/arm/boot/dts/at91sam9260.dtsi
+8
-0
arch/arm/boot/dts/at91sam9263.dtsi
arch/arm/boot/dts/at91sam9263.dtsi
+16
-0
arch/arm/boot/dts/at91sam9g20ek_common.dtsi
arch/arm/boot/dts/at91sam9g20ek_common.dtsi
+31
-1
arch/arm/boot/dts/at91sam9g45.dtsi
arch/arm/boot/dts/at91sam9g45.dtsi
+16
-0
arch/arm/boot/dts/at91sam9x5.dtsi
arch/arm/boot/dts/at91sam9x5.dtsi
+8
-0
arch/arm/mach-at91/at91rm9200.c
arch/arm/mach-at91/at91rm9200.c
+6
-3
arch/arm/mach-at91/at91rm9200_devices.c
arch/arm/mach-at91/at91rm9200_devices.c
+3
-3
arch/arm/mach-at91/at91sam9260.c
arch/arm/mach-at91/at91sam9260.c
+2
-1
arch/arm/mach-at91/at91sam9260_devices.c
arch/arm/mach-at91/at91sam9260_devices.c
+1
-1
arch/arm/mach-at91/at91sam9261.c
arch/arm/mach-at91/at91sam9261.c
+6
-3
arch/arm/mach-at91/at91sam9261_devices.c
arch/arm/mach-at91/at91sam9261_devices.c
+3
-3
arch/arm/mach-at91/at91sam9263.c
arch/arm/mach-at91/at91sam9263.c
+4
-2
arch/arm/mach-at91/at91sam9263_devices.c
arch/arm/mach-at91/at91sam9263_devices.c
+2
-2
arch/arm/mach-at91/at91sam9g45.c
arch/arm/mach-at91/at91sam9g45.c
+4
-2
arch/arm/mach-at91/at91sam9g45_devices.c
arch/arm/mach-at91/at91sam9g45_devices.c
+2
-2
arch/arm/mach-at91/at91sam9rl.c
arch/arm/mach-at91/at91sam9rl.c
+4
-2
arch/arm/mach-at91/at91sam9rl_devices.c
arch/arm/mach-at91/at91sam9rl_devices.c
+2
-2
arch/arm/mach-at91/at91sam9x5.c
arch/arm/mach-at91/at91sam9x5.c
+1
-0
arch/arm/mach-at91/board-sam9g20ek.c
arch/arm/mach-at91/board-sam9g20ek.c
+11
-0
drivers/misc/atmel-ssc.c
drivers/misc/atmel-ssc.c
+88
-47
include/linux/atmel-ssc.h
include/linux/atmel-ssc.h
+6
-0
sound/soc/atmel/Kconfig
sound/soc/atmel/Kconfig
+11
-2
sound/soc/atmel/Makefile
sound/soc/atmel/Makefile
+4
-0
sound/soc/atmel/atmel-pcm-dma.c
sound/soc/atmel/atmel-pcm-dma.c
+240
-0
sound/soc/atmel/atmel-pcm-pdc.c
sound/soc/atmel/atmel-pcm-pdc.c
+401
-0
sound/soc/atmel/atmel-pcm.c
sound/soc/atmel/atmel-pcm.c
+12
-389
sound/soc/atmel/atmel-pcm.h
sound/soc/atmel/atmel-pcm.h
+34
-0
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/atmel_ssc_dai.c
+51
-117
sound/soc/atmel/atmel_ssc_dai.h
sound/soc/atmel/atmel_ssc_dai.h
+2
-1
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/atmel/sam9g20_wm8731.c
+88
-28
No files found.
Documentation/devicetree/bindings/misc/atmel-ssc.txt
0 → 100644
View file @
3bc32492
* Atmel SSC driver.
Required properties:
- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc"
- atmel,at91rm9200-ssc: support pdc transfer
- atmel,at91sam9g45-ssc: support dma transfer
- reg: Should contain SSC registers location and length
- interrupts: Should contain SSC interrupt
Example:
ssc0: ssc@fffbc000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfffbc000 0x4000>;
interrupts = <14 4 5>;
};
Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt
0 → 100644
View file @
3bc32492
* Atmel at91sam9g20ek wm8731 audio complex
Required properties:
- compatible: "atmel,at91sam9g20ek-wm8731-audio"
- atmel,model: The user-visible name of this sound complex.
- atmel,audio-routing: A list of the connections between audio components.
- atmel,ssc-controller: The phandle of the SSC controller
- atmel,audio-codec: The phandle of the WM8731 audio codec
Optional properties:
- pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
Example:
sound {
compatible = "atmel,at91sam9g20ek-wm8731-audio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pck0_as_mck>;
atmel,model = "wm8731 @ AT91SAMG20EK";
atmel,audio-routing =
"Ext Spk", "LHPOUT",
"Int MIC", "MICIN";
atmel,ssc-controller = <&ssc0>;
atmel,audio-codec = <&wm8731>;
};
arch/arm/boot/dts/at91sam9260.dtsi
View file @
3bc32492
...
...
@@ -29,6 +29,7 @@ aliases {
tcb0
=
&
tcb0
;
tcb1
=
&
tcb1
;
i2c0
=
&
i2c0
;
ssc0
=
&
ssc0
;
};
cpus
{
cpu
@
0
{
...
...
@@ -212,6 +213,13 @@ i2c0: i2c@fffac000 {
status
=
"disabled"
;
};
ssc0
:
ssc
@
fffbc000
{
compatible
=
"atmel,at91rm9200-ssc"
;
reg
=
<
0xfffbc000
0x4000
>;
interrupts
=
<
14
4
5
>;
status
=
"disable"
;
};
adc0
:
adc
@
fffe0000
{
compatible
=
"atmel,at91sam9260-adc"
;
reg
=
<
0xfffe0000
0x100
>;
...
...
arch/arm/boot/dts/at91sam9263.dtsi
View file @
3bc32492
...
...
@@ -25,6 +25,8 @@ aliases {
gpio4
=
&
pioE
;
tcb0
=
&
tcb0
;
i2c0
=
&
i2c0
;
ssc0
=
&
ssc0
;
ssc1
=
&
ssc1
;
};
cpus
{
cpu
@
0
{
...
...
@@ -173,6 +175,20 @@ usart2: serial@fff94000 {
status
=
"disabled"
;
};
ssc0
:
ssc
@
fff98000
{
compatible
=
"atmel,at91rm9200-ssc"
;
reg
=
<
0xfff98000
0x4000
>;
interrupts
=
<
16
4
5
>;
status
=
"disable"
;
};
ssc1
:
ssc
@
fff9c000
{
compatible
=
"atmel,at91rm9200-ssc"
;
reg
=
<
0xfff9c000
0x4000
>;
interrupts
=
<
17
4
5
>;
status
=
"disable"
;
};
macb0
:
ethernet
@
fffbc000
{
compatible
=
"cdns,at32ap7000-macb"
,
"cdns,macb"
;
reg
=
<
0xfffbc000
0x100
>;
...
...
arch/arm/boot/dts/at91sam9g20ek_common.dtsi
View file @
3bc32492
...
...
@@ -30,6 +30,16 @@ main_clock: clock@0 {
ahb {
apb {
pinctrl@fffff400 {
board {
pinctrl_pck0_as_mck: pck0_as_mck {
atmel,pins =
<2 1 0x2 0x0>; /* PC1 periph B */
};
};
};
dbgu: serial@fffff200 {
status = "okay";
};
...
...
@@ -51,6 +61,11 @@ usb1: gadget@fffa4000 {
atmel,vbus-gpio = <&pioC 5 0>;
status = "okay";
};
ssc0: ssc@fffbc000 {
status = "okay";
pinctrl-0 = <&pinctrl_ssc0_tx>;
};
};
nand0: nand@40000000 {
...
...
@@ -114,7 +129,7 @@ i2c@0 {
reg = <0x50>;
};
wm8731@1b {
wm8731
: wm8731
@1b {
compatible = "wm8731";
reg = <0x1b>;
};
...
...
@@ -139,4 +154,19 @@ btn4 {
gpio-key,wakeup;
};
};
sound {
compatible = "atmel,at91sam9g20ek-wm8731-audio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pck0_as_mck>;
atmel,model = "wm8731 @ AT91SAMG20EK";
atmel,audio-routing =
"Ext Spk", "LHPOUT",
"Int Mic", "MICIN";
atmel,ssc-controller = <&ssc0>;
atmel,audio-codec = <&wm8731>;
};
};
arch/arm/boot/dts/at91sam9g45.dtsi
View file @
3bc32492
...
...
@@ -31,6 +31,8 @@ aliases {
tcb1
=
&
tcb1
;
i2c0
=
&
i2c0
;
i2c1
=
&
i2c1
;
ssc0
=
&
ssc0
;
ssc1
=
&
ssc1
;
};
cpus
{
cpu
@
0
{
...
...
@@ -226,6 +228,20 @@ i2c1: i2c@fff88000 {
status
=
"disabled"
;
};
ssc0
:
ssc
@
fff9c000
{
compatible
=
"atmel,at91sam9g45-ssc"
;
reg
=
<
0xfff9c000
0x4000
>;
interrupts
=
<
16
4
5
>;
status
=
"disable"
;
};
ssc1
:
ssc
@
fffa0000
{
compatible
=
"atmel,at91sam9g45-ssc"
;
reg
=
<
0xfffa0000
0x4000
>;
interrupts
=
<
17
4
5
>;
status
=
"disable"
;
};
adc0
:
adc
@
fffb0000
{
compatible
=
"atmel,at91sam9260-adc"
;
reg
=
<
0xfffb0000
0x100
>;
...
...
arch/arm/boot/dts/at91sam9x5.dtsi
View file @
3bc32492
...
...
@@ -30,6 +30,7 @@ aliases {
i2c0
=
&
i2c0
;
i2c1
=
&
i2c1
;
i2c2
=
&
i2c2
;
ssc0
=
&
ssc0
;
};
cpus
{
cpu
@
0
{
...
...
@@ -87,6 +88,13 @@ pit: timer@fffffe30 {
interrupts
=
<
1
4
7
>;
};
ssc0
:
ssc
@
f0010000
{
compatible
=
"atmel,at91sam9g45-ssc"
;
reg
=
<
0xf0010000
0x4000
>;
interrupts
=
<
28
4
5
>;
status
=
"disable"
;
};
tcb0
:
timer
@
f8008000
{
compatible
=
"atmel,at91sam9x5-tcb"
;
reg
=
<
0xf8008000
0x100
>;
...
...
arch/arm/mach-at91/at91rm9200.c
View file @
3bc32492
...
...
@@ -184,9 +184,12 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID
(
"t0_clk"
,
"atmel_tcb.1"
,
&
tc3_clk
),
CLKDEV_CON_DEV_ID
(
"t1_clk"
,
"atmel_tcb.1"
,
&
tc4_clk
),
CLKDEV_CON_DEV_ID
(
"t2_clk"
,
"atmel_tcb.1"
,
&
tc5_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.2"
,
&
ssc2_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.2"
,
&
ssc2_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffd0000.ssc"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffd4000.ssc"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffd8000.ssc"
,
&
ssc2_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91rm9200.0"
,
&
twi_clk
),
/* fake hclk clock */
CLKDEV_CON_DEV_ID
(
"hclk"
,
"at91_ohci"
,
&
ohci_clk
),
...
...
arch/arm/mach-at91/at91rm9200_devices.c
View file @
3bc32492
...
...
@@ -752,7 +752,7 @@ static struct resource ssc0_resources[] = {
};
static
struct
platform_device
at91rm9200_ssc0_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
0
,
.
dev
=
{
.
dma_mask
=
&
ssc0_dmamask
,
...
...
@@ -794,7 +794,7 @@ static struct resource ssc1_resources[] = {
};
static
struct
platform_device
at91rm9200_ssc1_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
1
,
.
dev
=
{
.
dma_mask
=
&
ssc1_dmamask
,
...
...
@@ -836,7 +836,7 @@ static struct resource ssc2_resources[] = {
};
static
struct
platform_device
at91rm9200_ssc2_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
2
,
.
dev
=
{
.
dma_mask
=
&
ssc2_dmamask
,
...
...
arch/arm/mach-at91/at91sam9260.c
View file @
3bc32492
...
...
@@ -210,7 +210,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID
(
"t0_clk"
,
"atmel_tcb.1"
,
&
tc3_clk
),
CLKDEV_CON_DEV_ID
(
"t1_clk"
,
"atmel_tcb.1"
,
&
tc4_clk
),
CLKDEV_CON_DEV_ID
(
"t2_clk"
,
"atmel_tcb.1"
,
&
tc5_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.0"
,
&
ssc_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.0"
,
&
ssc_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffbc000.ssc"
,
&
ssc_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9260.0"
,
&
twi_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9g20.0"
,
&
twi_clk
),
/* more usart lookup table for DT entries */
...
...
arch/arm/mach-at91/at91sam9260_devices.c
View file @
3bc32492
...
...
@@ -742,7 +742,7 @@ static struct resource ssc_resources[] = {
};
static
struct
platform_device
at91sam9260_ssc_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
0
,
.
dev
=
{
.
dma_mask
=
&
ssc_dmamask
,
...
...
arch/arm/mach-at91/at91sam9261.c
View file @
3bc32492
...
...
@@ -174,9 +174,12 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID
(
"t0_clk"
,
"atmel_tcb.0"
,
&
tc0_clk
),
CLKDEV_CON_DEV_ID
(
"t1_clk"
,
"atmel_tcb.0"
,
&
tc1_clk
),
CLKDEV_CON_DEV_ID
(
"t2_clk"
,
"atmel_tcb.0"
,
&
tc2_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.2"
,
&
ssc2_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.2"
,
&
ssc2_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffbc000.ssc"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffc0000.ssc"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffc4000.ssc"
,
&
ssc2_clk
),
CLKDEV_CON_DEV_ID
(
"hclk"
,
"at91_ohci"
,
&
hck0
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9261.0"
,
&
twi_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9g10.0"
,
&
twi_clk
),
...
...
arch/arm/mach-at91/at91sam9261_devices.c
View file @
3bc32492
...
...
@@ -706,7 +706,7 @@ static struct resource ssc0_resources[] = {
};
static
struct
platform_device
at91sam9261_ssc0_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
0
,
.
dev
=
{
.
dma_mask
=
&
ssc0_dmamask
,
...
...
@@ -748,7 +748,7 @@ static struct resource ssc1_resources[] = {
};
static
struct
platform_device
at91sam9261_ssc1_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
1
,
.
dev
=
{
.
dma_mask
=
&
ssc1_dmamask
,
...
...
@@ -790,7 +790,7 @@ static struct resource ssc2_resources[] = {
};
static
struct
platform_device
at91sam9261_ssc2_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
2
,
.
dev
=
{
.
dma_mask
=
&
ssc2_dmamask
,
...
...
arch/arm/mach-at91/at91sam9263.c
View file @
3bc32492
...
...
@@ -186,8 +186,10 @@ static struct clk *periph_clocks[] __initdata = {
static
struct
clk_lookup
periph_clocks_lookups
[]
=
{
/* One additional fake clock for macb_hclk */
CLKDEV_CON_ID
(
"hclk"
,
&
macb_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fff98000.ssc"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fff9c000.ssc"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"mci_clk"
,
"atmel_mci.0"
,
&
mmc0_clk
),
CLKDEV_CON_DEV_ID
(
"mci_clk"
,
"atmel_mci.1"
,
&
mmc1_clk
),
CLKDEV_CON_DEV_ID
(
"spi_clk"
,
"atmel_spi.0"
,
&
spi0_clk
),
...
...
arch/arm/mach-at91/at91sam9263_devices.c
View file @
3bc32492
...
...
@@ -1199,7 +1199,7 @@ static struct resource ssc0_resources[] = {
};
static
struct
platform_device
at91sam9263_ssc0_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
0
,
.
dev
=
{
.
dma_mask
=
&
ssc0_dmamask
,
...
...
@@ -1241,7 +1241,7 @@ static struct resource ssc1_resources[] = {
};
static
struct
platform_device
at91sam9263_ssc1_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
1
,
.
dev
=
{
.
dma_mask
=
&
ssc1_dmamask
,
...
...
arch/arm/mach-at91/at91sam9g45.c
View file @
3bc32492
...
...
@@ -239,8 +239,10 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID
(
"t0_clk"
,
"atmel_tcb.1"
,
&
tcb0_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9g10.0"
,
&
twi0_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9g10.1"
,
&
twi1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91sam9g45_ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91sam9g45_ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fff9c000.ssc"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffa0000.ssc"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"atmel-trng"
,
&
trng_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"atmel_sha"
,
&
aestdessha_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"atmel_tdes"
,
&
aestdessha_clk
),
...
...
arch/arm/mach-at91/at91sam9g45_devices.c
View file @
3bc32492
...
...
@@ -1459,7 +1459,7 @@ static struct resource ssc0_resources[] = {
};
static
struct
platform_device
at91sam9g45_ssc0_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91sam9g45_
ssc"
,
.
id
=
0
,
.
dev
=
{
.
dma_mask
=
&
ssc0_dmamask
,
...
...
@@ -1501,7 +1501,7 @@ static struct resource ssc1_resources[] = {
};
static
struct
platform_device
at91sam9g45_ssc1_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91sam9g45_
ssc"
,
.
id
=
1
,
.
dev
=
{
.
dma_mask
=
&
ssc1_dmamask
,
...
...
arch/arm/mach-at91/at91sam9rl.c
View file @
3bc32492
...
...
@@ -184,8 +184,10 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID
(
"t0_clk"
,
"atmel_tcb.0"
,
&
tc0_clk
),
CLKDEV_CON_DEV_ID
(
"t1_clk"
,
"atmel_tcb.0"
,
&
tc1_clk
),
CLKDEV_CON_DEV_ID
(
"t2_clk"
,
"atmel_tcb.0"
,
&
tc2_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.0"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"at91rm9200_ssc.1"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffc0000.ssc"
,
&
ssc0_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"fffc4000.ssc"
,
&
ssc1_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9g20.0"
,
&
twi0_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"i2c-at91sam9g20.1"
,
&
twi1_clk
),
CLKDEV_CON_ID
(
"pioA"
,
&
pioA_clk
),
...
...
arch/arm/mach-at91/at91sam9rl_devices.c
View file @
3bc32492
...
...
@@ -832,7 +832,7 @@ static struct resource ssc0_resources[] = {
};
static
struct
platform_device
at91sam9rl_ssc0_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
0
,
.
dev
=
{
.
dma_mask
=
&
ssc0_dmamask
,
...
...
@@ -874,7 +874,7 @@ static struct resource ssc1_resources[] = {
};
static
struct
platform_device
at91sam9rl_ssc1_device
=
{
.
name
=
"ssc"
,
.
name
=
"
at91rm9200_
ssc"
,
.
id
=
1
,
.
dev
=
{
.
dma_mask
=
&
ssc1_dmamask
,
...
...
arch/arm/mach-at91/at91sam9x5.c
View file @
3bc32492
...
...
@@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID
(
"t0_clk"
,
"f800c000.timer"
,
&
tcb0_clk
),
CLKDEV_CON_DEV_ID
(
"dma_clk"
,
"ffffec00.dma-controller"
,
&
dma0_clk
),
CLKDEV_CON_DEV_ID
(
"dma_clk"
,
"ffffee00.dma-controller"
,
&
dma1_clk
),
CLKDEV_CON_DEV_ID
(
"pclk"
,
"f0010000.ssc"
,
&
ssc_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"f8010000.i2c"
,
&
twi0_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"f8014000.i2c"
,
&
twi1_clk
),
CLKDEV_CON_DEV_ID
(
NULL
,
"f8018000.i2c"
,
&
twi2_clk
),
...
...
arch/arm/mach-at91/board-sam9g20ek.c
View file @
3bc32492
...
...
@@ -353,6 +353,16 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = {
},
};
static
struct
platform_device
sam9g20ek_audio_device
=
{
.
name
=
"at91sam9g20ek-audio"
,
.
id
=
-
1
,
};
static
void
__init
ek_add_device_audio
(
void
)
{
platform_device_register
(
&
sam9g20ek_audio_device
);
}
static
void
__init
ek_board_init
(
void
)
{
...
...
@@ -394,6 +404,7 @@ static void __init ek_board_init(void)
at91_set_B_periph
(
AT91_PIN_PC1
,
0
);
/* SSC (for WM8731) */
at91_add_device_ssc
(
AT91SAM9260_ID_SSC
,
ATMEL_SSC_TX
);
ek_add_device_audio
();
}
MACHINE_START
(
AT91SAM9G20EK
,
"Atmel AT91SAM9G20-EK"
)
...
...
drivers/misc/atmel-ssc.c
View file @
3bc32492
...
...
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>
/* Serialize access to ssc_list and user count */
static
DEFINE_SPINLOCK
(
user_lock
);
static
LIST_HEAD
(
ssc_list
);
...
...
@@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
spin_lock
(
&
user_lock
);
list_for_each_entry
(
ssc
,
&
ssc_list
,
list
)
{
if
(
ssc
->
pdev
->
id
==
ssc_num
)
{
if
(
ssc
->
pdev
->
dev
.
of_node
)
{
if
(
of_alias_get_id
(
ssc
->
pdev
->
dev
.
of_node
,
"ssc"
)
==
ssc_num
)
{
ssc_valid
=
1
;
break
;
}
}
else
if
(
ssc
->
pdev
->
id
==
ssc_num
)
{
ssc_valid
=
1
;
break
;
}
...
...
@@ -68,39 +76,93 @@ void ssc_free(struct ssc_device *ssc)
}
EXPORT_SYMBOL
(
ssc_free
);
static
int
__init
ssc_probe
(
struct
platform_device
*
pdev
)
static
struct
atmel_ssc_platform_data
at91rm9200_config
=
{
.
use_dma
=
0
,
};
static
struct
atmel_ssc_platform_data
at91sam9g45_config
=
{
.
use_dma
=
1
,
};
static
const
struct
platform_device_id
atmel_ssc_devtypes
[]
=
{
{
.
name
=
"at91rm9200_ssc"
,
.
driver_data
=
(
unsigned
long
)
&
at91rm9200_config
,
},
{
.
name
=
"at91sam9g45_ssc"
,
.
driver_data
=
(
unsigned
long
)
&
at91sam9g45_config
,
},
{
/* sentinel */
}
};
#ifdef CONFIG_OF
static
const
struct
of_device_id
atmel_ssc_dt_ids
[]
=
{
{
.
compatible
=
"atmel,at91rm9200-ssc"
,
.
data
=
&
at91rm9200_config
,
},
{
.
compatible
=
"atmel,at91sam9g45-ssc"
,
.
data
=
&
at91sam9g45_config
,
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
atmel_ssc_dt_ids
);
#endif
static
inline
const
struct
atmel_ssc_platform_data
*
__init
atmel_ssc_get_driver_data
(
struct
platform_device
*
pdev
)
{
if
(
pdev
->
dev
.
of_node
)
{
const
struct
of_device_id
*
match
;
match
=
of_match_node
(
atmel_ssc_dt_ids
,
pdev
->
dev
.
of_node
);
if
(
match
==
NULL
)
return
NULL
;
return
match
->
data
;
}
return
(
struct
atmel_ssc_platform_data
*
)
platform_get_device_id
(
pdev
)
->
driver_data
;
}
static
int
ssc_probe
(
struct
platform_device
*
pdev
)
{
int
retval
=
0
;
struct
resource
*
regs
;
struct
ssc_device
*
ssc
;
const
struct
atmel_ssc_platform_data
*
plat_dat
;
ssc
=
kzalloc
(
sizeof
(
struct
ssc_device
),
GFP_KERNEL
);
ssc
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
struct
ssc_device
),
GFP_KERNEL
);
if
(
!
ssc
)
{
dev_dbg
(
&
pdev
->
dev
,
"out of memory
\n
"
);
retval
=
-
ENOMEM
;
goto
out
;
return
-
ENOMEM
;
}
ssc
->
pdev
=
pdev
;
plat_dat
=
atmel_ssc_get_driver_data
(
pdev
);
if
(
!
plat_dat
)
return
-
ENODEV
;
ssc
->
pdata
=
(
struct
atmel_ssc_platform_data
*
)
plat_dat
;
regs
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
regs
)
{
dev_dbg
(
&
pdev
->
dev
,
"no mmio resource defined
\n
"
);
retval
=
-
ENXIO
;
goto
out_free
;
return
-
ENXIO
;
}
ssc
->
clk
=
clk_get
(
&
pdev
->
dev
,
"pclk"
);
if
(
IS_ERR
(
ssc
->
clk
))
{
dev_dbg
(
&
pdev
->
dev
,
"no pclk clock defined
\n
"
);
retval
=
-
ENXIO
;
goto
out_free
;
}
ssc
->
pdev
=
pdev
;
ssc
->
regs
=
ioremap
(
regs
->
start
,
resource_size
(
regs
));
ssc
->
regs
=
devm_request_and_ioremap
(
&
pdev
->
dev
,
regs
);
if
(
!
ssc
->
regs
)
{
dev_dbg
(
&
pdev
->
dev
,
"ioremap failed
\n
"
);
retval
=
-
EINVAL
;
goto
out_clk
;
return
-
EINVAL
;
}
ssc
->
phybase
=
regs
->
start
;
ssc
->
clk
=
devm_clk_get
(
&
pdev
->
dev
,
"pclk"
);
if
(
IS_ERR
(
ssc
->
clk
))
{
dev_dbg
(
&
pdev
->
dev
,
"no pclk clock defined
\n
"
);
return
-
ENXIO
;
}
/* disable all interrupts */
...
...
@@ -112,8 +174,7 @@ static int __init ssc_probe(struct platform_device *pdev)
ssc
->
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
!
ssc
->
irq
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not get irq
\n
"
);
retval
=
-
ENXIO
;
goto
out_unmap
;
return
-
ENXIO
;
}
spin_lock
(
&
user_lock
);
...
...
@@ -125,16 +186,7 @@ static int __init ssc_probe(struct platform_device *pdev)
dev_info
(
&
pdev
->
dev
,
"Atmel SSC device at 0x%p (irq %d)
\n
"
,
ssc
->
regs
,
ssc
->
irq
);
goto
out
;
out_unmap:
iounmap
(
ssc
->
regs
);
out_clk:
clk_put
(
ssc
->
clk
);
out_free:
kfree
(
ssc
);
out:
return
retval
;
return
0
;
}
static
int
__devexit
ssc_remove
(
struct
platform_device
*
pdev
)
...
...
@@ -142,34 +194,23 @@ static int __devexit ssc_remove(struct platform_device *pdev)
struct
ssc_device
*
ssc
=
platform_get_drvdata
(
pdev
);
spin_lock
(
&
user_lock
);
iounmap
(
ssc
->
regs
);
clk_put
(
ssc
->
clk
);
list_del
(
&
ssc
->
list
);
kfree
(
ssc
);
spin_unlock
(
&
user_lock
);
return
0
;
}
static
struct
platform_driver
ssc_driver
=
{
.
remove
=
__devexit_p
(
ssc_remove
),
.
driver
=
{
.
name
=
"ssc"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
of_match_ptr
(
atmel_ssc_dt_ids
),
},
.
id_table
=
atmel_ssc_devtypes
,
.
probe
=
ssc_probe
,
.
remove
=
__devexit_p
(
ssc_remove
),
};
static
int
__init
ssc_init
(
void
)
{
return
platform_driver_probe
(
&
ssc_driver
,
ssc_probe
);
}
module_init
(
ssc_init
);
static
void
__exit
ssc_exit
(
void
)
{
platform_driver_unregister
(
&
ssc_driver
);
}
module_exit
(
ssc_exit
);
module_platform_driver
(
ssc_driver
);
MODULE_AUTHOR
(
"Hans-Christian Egtvedt <hcegtvedt@atmel.com>"
);
MODULE_DESCRIPTION
(
"SSC driver for Atmel AVR32 and AT91"
);
...
...
include/linux/atmel-ssc.h
View file @
3bc32492
...
...
@@ -5,10 +5,16 @@
#include <linux/list.h>
#include <linux/io.h>
struct
atmel_ssc_platform_data
{
int
use_dma
;
};
struct
ssc_device
{
struct
list_head
list
;
resource_size_t
phybase
;
void
__iomem
*
regs
;
struct
platform_device
*
pdev
;
struct
atmel_ssc_platform_data
*
pdata
;
struct
clk
*
clk
;
int
user
;
int
irq
;
...
...
sound/soc/atmel/Kconfig
View file @
3bc32492
...
...
@@ -6,6 +6,14 @@ config SND_ATMEL_SOC
the ATMEL SSC interface. You will also need
to select the audio interfaces to support below.
config SND_ATMEL_SOC_PDC
tristate
depends on SND_ATMEL_SOC
config SND_ATMEL_SOC_DMA
tristate
depends on SND_ATMEL_SOC
config SND_ATMEL_SOC_SSC
tristate
depends on SND_ATMEL_SOC
...
...
@@ -16,8 +24,8 @@ config SND_ATMEL_SOC_SSC
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
depends on ATMEL_SSC &&
ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \
AT91_PROGRAMMABLE_CLOCKS
depends on ATMEL_SSC &&
SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_WM8731
help
...
...
@@ -27,6 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
config SND_AT91_SOC_AFEB9260
tristate "SoC Audio support for AFEB9260 board"
depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_TLV320AIC23
help
...
...
sound/soc/atmel/Makefile
View file @
3bc32492
# AT91 Platform Support
snd-soc-atmel-pcm-objs
:=
atmel-pcm.o
snd-soc-atmel-pcm-pdc-objs
:=
atmel-pcm-pdc.o
snd-soc-atmel-pcm-dma-objs
:=
atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs
:=
atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC)
+=
snd-soc-atmel-pcm.o
obj-$(CONFIG_SND_ATMEL_SOC_PDC)
+=
snd-soc-atmel-pcm-pdc.o
obj-$(CONFIG_SND_ATMEL_SOC_DMA)
+=
snd-soc-atmel-pcm-dma.o
obj-$(CONFIG_SND_ATMEL_SOC_SSC)
+=
snd-soc-atmel_ssc_dai.o
# AT91 Machine Support
...
...
sound/soc/atmel/atmel-pcm-dma.c
0 → 100644
View file @
3bc32492
/*
* atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC.
*
* Copyright (C) 2012 Atmel
*
* Author: Bo Shen <voice.shen@atmel.com>
*
* Based on atmel-pcm by:
* Sedji Gaouaou <sedji.gaouaou@atmel.com>
* Copyright 2008 Atmel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/atmel-ssc.h>
#include <linux/platform_data/dma-atmel.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include "atmel-pcm.h"
/*--------------------------------------------------------------------------*\
* Hardware definition
\*--------------------------------------------------------------------------*/
static
const
struct
snd_pcm_hardware
atmel_pcm_dma_hardware
=
{
.
info
=
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_RESUME
|
SNDRV_PCM_INFO_PAUSE
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
.
period_bytes_min
=
256
,
/* lighting DMA overhead */
.
period_bytes_max
=
2
*
0xffff
,
/* if 2 bytes format */
.
periods_min
=
8
,
.
periods_max
=
1024
,
/* no limit */
.
buffer_bytes_max
=
ATMEL_SSC_DMABUF_SIZE
,
};
/**
* atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
*
* We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
* check if any overrun occured.
*/
static
void
atmel_pcm_dma_irq
(
u32
ssc_sr
,
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_pcm_dma_params
*
prtd
;
prtd
=
snd_dmaengine_pcm_get_data
(
substream
);
if
(
ssc_sr
&
prtd
->
mask
->
ssc_error
)
{
if
(
snd_pcm_running
(
substream
))
pr_warn
(
"atmel-pcm: buffer %s on %s (SSC_SR=%#x)
\n
"
,
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
?
"underrun"
:
"overrun"
,
prtd
->
name
,
ssc_sr
);
/* stop RX and capture: will be enabled again at restart */
ssc_writex
(
prtd
->
ssc
->
regs
,
SSC_CR
,
prtd
->
mask
->
ssc_disable
);
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_XRUN
);
/* now drain RHR and read status to remove xrun condition */
ssc_readx
(
prtd
->
ssc
->
regs
,
SSC_RHR
);
ssc_readx
(
prtd
->
ssc
->
regs
,
SSC_SR
);
}
}
/*--------------------------------------------------------------------------*\
* DMAENGINE operations
\*--------------------------------------------------------------------------*/
static
bool
filter
(
struct
dma_chan
*
chan
,
void
*
slave
)
{
struct
at_dma_slave
*
sl
=
slave
;
if
(
sl
->
dma_dev
==
chan
->
device
->
dev
)
{
chan
->
private
=
sl
;
return
true
;
}
else
{
return
false
;
}
}
static
int
atmel_pcm_configure_dma
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
atmel_pcm_dma_params
*
prtd
;
struct
ssc_device
*
ssc
;
struct
dma_chan
*
dma_chan
;
struct
dma_slave_config
slave_config
;
int
ret
;
prtd
=
snd_dmaengine_pcm_get_data
(
substream
);
ssc
=
prtd
->
ssc
;
ret
=
snd_hwparams_to_dma_slave_config
(
substream
,
params
,
&
slave_config
);
if
(
ret
)
{
pr_err
(
"atmel-pcm: hwparams to dma slave configure failed
\n
"
);
return
ret
;
}
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
{
slave_config
.
dst_addr
=
(
dma_addr_t
)
ssc
->
phybase
+
SSC_THR
;
slave_config
.
dst_maxburst
=
1
;
}
else
{
slave_config
.
src_addr
=
(
dma_addr_t
)
ssc
->
phybase
+
SSC_RHR
;
slave_config
.
src_maxburst
=
1
;
}
slave_config
.
device_fc
=
false
;
dma_chan
=
snd_dmaengine_pcm_get_chan
(
substream
);
if
(
dmaengine_slave_config
(
dma_chan
,
&
slave_config
))
{
pr_err
(
"atmel-pcm: failed to configure dma channel
\n
"
);
ret
=
-
EBUSY
;
return
ret
;
}
return
0
;
}
static
int
atmel_pcm_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_pcm_dma_params
*
prtd
;
struct
ssc_device
*
ssc
;
struct
at_dma_slave
*
sdata
=
NULL
;
int
ret
;
snd_pcm_set_runtime_buffer
(
substream
,
&
substream
->
dma_buffer
);
prtd
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
ssc
=
prtd
->
ssc
;
if
(
ssc
->
pdev
)
sdata
=
ssc
->
pdev
->
dev
.
platform_data
;
ret
=
snd_dmaengine_pcm_open
(
substream
,
filter
,
sdata
);
if
(
ret
)
{
pr_err
(
"atmel-pcm: dmaengine pcm open failed
\n
"
);
return
-
EINVAL
;
}
snd_dmaengine_pcm_set_data
(
substream
,
prtd
);
ret
=
atmel_pcm_configure_dma
(
substream
,
params
);
if
(
ret
)
{
pr_err
(
"atmel-pcm: failed to configure dmai
\n
"
);
goto
err
;
}
prtd
->
dma_intr_handler
=
atmel_pcm_dma_irq
;
return
0
;
err:
snd_dmaengine_pcm_close
(
substream
);
return
ret
;
}
static
int
atmel_pcm_dma_prepare
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_pcm_dma_params
*
prtd
;
prtd
=
snd_dmaengine_pcm_get_data
(
substream
);
ssc_writex
(
prtd
->
ssc
->
regs
,
SSC_IER
,
prtd
->
mask
->
ssc_error
);
ssc_writex
(
prtd
->
ssc
->
regs
,
SSC_CR
,
prtd
->
mask
->
ssc_enable
);
return
0
;
}
static
int
atmel_pcm_open
(
struct
snd_pcm_substream
*
substream
)
{
snd_soc_set_runtime_hwparams
(
substream
,
&
atmel_pcm_dma_hardware
);
return
0
;
}
static
int
atmel_pcm_close
(
struct
snd_pcm_substream
*
substream
)
{
snd_dmaengine_pcm_close
(
substream
);
return
0
;
}
static
struct
snd_pcm_ops
atmel_pcm_ops
=
{
.
open
=
atmel_pcm_open
,
.
close
=
atmel_pcm_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
atmel_pcm_hw_params
,
.
prepare
=
atmel_pcm_dma_prepare
,
.
trigger
=
snd_dmaengine_pcm_trigger
,
.
pointer
=
snd_dmaengine_pcm_pointer_no_residue
,
.
mmap
=
atmel_pcm_mmap
,
};
static
struct
snd_soc_platform_driver
atmel_soc_platform
=
{
.
ops
=
&
atmel_pcm_ops
,
.
pcm_new
=
atmel_pcm_new
,
.
pcm_free
=
atmel_pcm_free
,
};
int
atmel_pcm_dma_platform_register
(
struct
device
*
dev
)
{
return
snd_soc_register_platform
(
dev
,
&
atmel_soc_platform
);
}
EXPORT_SYMBOL
(
atmel_pcm_dma_platform_register
);
void
atmel_pcm_dma_platform_unregister
(
struct
device
*
dev
)
{
snd_soc_unregister_platform
(
dev
);
}
EXPORT_SYMBOL
(
atmel_pcm_dma_platform_unregister
);
MODULE_AUTHOR
(
"Bo Shen <voice.shen@atmel.com>"
);
MODULE_DESCRIPTION
(
"Atmel DMA based PCM module"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/atmel/atmel-pcm-pdc.c
0 → 100644
View file @
3bc32492
/*
* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
*
* Copyright (C) 2005 SAN People
* Copyright (C) 2008 Atmel
*
* Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
*
* Based on at91-pcm. by:
* Frank Mandarino <fmandarino@endrelia.com>
* Copyright 2006 Endrelia Technologies Inc.
*
* Based on pxa2xx-pcm.c by:
*
* Author: Nicolas Pitre
* Created: Nov 30, 2004
* Copyright: (C) 2004 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel-ssc.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "atmel-pcm.h"
/*--------------------------------------------------------------------------*\
* Hardware definition
\*--------------------------------------------------------------------------*/
/* TODO: These values were taken from the AT91 platform driver, check
* them against real values for AT32
*/
static
const
struct
snd_pcm_hardware
atmel_pcm_hardware
=
{
.
info
=
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_PAUSE
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8192
,
.
periods_min
=
2
,
.
periods_max
=
1024
,
.
buffer_bytes_max
=
ATMEL_SSC_DMABUF_SIZE
,
};
/*--------------------------------------------------------------------------*\
* Data types
\*--------------------------------------------------------------------------*/
struct
atmel_runtime_data
{
struct
atmel_pcm_dma_params
*
params
;
dma_addr_t
dma_buffer
;
/* physical address of dma buffer */
dma_addr_t
dma_buffer_end
;
/* first address beyond DMA buffer */
size_t
period_size
;
dma_addr_t
period_ptr
;
/* physical address of next period */
/* PDC register save */
u32
pdc_xpr_save
;
u32
pdc_xcr_save
;
u32
pdc_xnpr_save
;
u32
pdc_xncr_save
;
};
/*--------------------------------------------------------------------------*\
* ISR
\*--------------------------------------------------------------------------*/
static
void
atmel_pcm_dma_irq
(
u32
ssc_sr
,
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
static
int
count
;
count
++
;
if
(
ssc_sr
&
params
->
mask
->
ssc_endbuf
)
{
pr_warn
(
"atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)
\n
"
,
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
?
"underrun"
:
"overrun"
,
params
->
name
,
ssc_sr
,
count
);
/* re-start the PDC */
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
prtd
->
period_ptr
+=
prtd
->
period_size
;
if
(
prtd
->
period_ptr
>=
prtd
->
dma_buffer_end
)
prtd
->
period_ptr
=
prtd
->
dma_buffer
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_enable
);
}
if
(
ssc_sr
&
params
->
mask
->
ssc_endx
)
{
/* Load the PDC next pointer and counter registers */
prtd
->
period_ptr
+=
prtd
->
period_size
;
if
(
prtd
->
period_ptr
>=
prtd
->
dma_buffer_end
)
prtd
->
period_ptr
=
prtd
->
dma_buffer
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
}
snd_pcm_period_elapsed
(
substream
);
}
/*--------------------------------------------------------------------------*\
* PCM operations
\*--------------------------------------------------------------------------*/
static
int
atmel_pcm_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
=
runtime
->
private_data
;
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
/* this may get called several times by oss emulation
* with different params */
snd_pcm_set_runtime_buffer
(
substream
,
&
substream
->
dma_buffer
);
runtime
->
dma_bytes
=
params_buffer_bytes
(
params
);
prtd
->
params
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
prtd
->
params
->
dma_intr_handler
=
atmel_pcm_dma_irq
;
prtd
->
dma_buffer
=
runtime
->
dma_addr
;
prtd
->
dma_buffer_end
=
runtime
->
dma_addr
+
runtime
->
dma_bytes
;
prtd
->
period_size
=
params_period_bytes
(
params
);
pr_debug
(
"atmel-pcm: "
"hw_params: DMA for %s initialized "
"(dma_bytes=%u, period_size=%u)
\n
"
,
prtd
->
params
->
name
,
runtime
->
dma_bytes
,
prtd
->
period_size
);
return
0
;
}
static
int
atmel_pcm_hw_free
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
if
(
params
!=
NULL
)
{
ssc_writex
(
params
->
ssc
->
regs
,
SSC_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
prtd
->
params
->
dma_intr_handler
=
NULL
;
}
return
0
;
}
static
int
atmel_pcm_prepare
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
ssc_writex
(
params
->
ssc
->
regs
,
SSC_IDR
,
params
->
mask
->
ssc_endx
|
params
->
mask
->
ssc_endbuf
);
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
return
0
;
}
static
int
atmel_pcm_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
snd_pcm_runtime
*
rtd
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
=
rtd
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
int
ret
=
0
;
pr_debug
(
"atmel-pcm:buffer_size = %ld,"
"dma_area = %p, dma_bytes = %u
\n
"
,
rtd
->
buffer_size
,
rtd
->
dma_area
,
rtd
->
dma_bytes
);
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
prtd
->
period_ptr
=
prtd
->
dma_buffer
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
prtd
->
period_ptr
+=
prtd
->
period_size
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
pr_debug
(
"atmel-pcm: trigger: "
"period_ptr=%lx, xpr=%u, "
"xcr=%u, xnpr=%u, xncr=%u
\n
"
,
(
unsigned
long
)
prtd
->
period_ptr
,
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
),
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
),
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
),
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
));
ssc_writex
(
params
->
ssc
->
regs
,
SSC_IER
,
params
->
mask
->
ssc_endx
|
params
->
mask
->
ssc_endbuf
);
ssc_writex
(
params
->
ssc
->
regs
,
SSC_PDC_PTCR
,
params
->
mask
->
pdc_enable
);
pr_debug
(
"sr=%u imr=%u
\n
"
,
ssc_readx
(
params
->
ssc
->
regs
,
SSC_SR
),
ssc_readx
(
params
->
ssc
->
regs
,
SSC_IER
));
break
;
/* SNDRV_PCM_TRIGGER_START */
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
break
;
case
SNDRV_PCM_TRIGGER_RESUME
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_enable
);
break
;
default:
ret
=
-
EINVAL
;
}
return
ret
;
}
static
snd_pcm_uframes_t
atmel_pcm_pointer
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
=
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
dma_addr_t
ptr
;
snd_pcm_uframes_t
x
;
ptr
=
(
dma_addr_t
)
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
);
x
=
bytes_to_frames
(
runtime
,
ptr
-
prtd
->
dma_buffer
);
if
(
x
==
runtime
->
buffer_size
)
x
=
0
;
return
x
;
}
static
int
atmel_pcm_open
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
;
int
ret
=
0
;
snd_soc_set_runtime_hwparams
(
substream
,
&
atmel_pcm_hardware
);
/* ensure that buffer size is a multiple of period size */
ret
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
);
if
(
ret
<
0
)
goto
out
;
prtd
=
kzalloc
(
sizeof
(
struct
atmel_runtime_data
),
GFP_KERNEL
);
if
(
prtd
==
NULL
)
{
ret
=
-
ENOMEM
;
goto
out
;
}
runtime
->
private_data
=
prtd
;
out:
return
ret
;
}
static
int
atmel_pcm_close
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
kfree
(
prtd
);
return
0
;
}
static
struct
snd_pcm_ops
atmel_pcm_ops
=
{
.
open
=
atmel_pcm_open
,
.
close
=
atmel_pcm_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
atmel_pcm_hw_params
,
.
hw_free
=
atmel_pcm_hw_free
,
.
prepare
=
atmel_pcm_prepare
,
.
trigger
=
atmel_pcm_trigger
,
.
pointer
=
atmel_pcm_pointer
,
.
mmap
=
atmel_pcm_mmap
,
};
/*--------------------------------------------------------------------------*\
* ASoC platform driver
\*--------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static
int
atmel_pcm_suspend
(
struct
snd_soc_dai
*
dai
)
{
struct
snd_pcm_runtime
*
runtime
=
dai
->
runtime
;
struct
atmel_runtime_data
*
prtd
;
struct
atmel_pcm_dma_params
*
params
;
if
(
!
runtime
)
return
0
;
prtd
=
runtime
->
private_data
;
params
=
prtd
->
params
;
/* disable the PDC and save the PDC registers */
ssc_writel
(
params
->
ssc
->
regs
,
PDC_PTCR
,
params
->
mask
->
pdc_disable
);
prtd
->
pdc_xpr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
);
prtd
->
pdc_xcr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
);
prtd
->
pdc_xnpr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
);
prtd
->
pdc_xncr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
);
return
0
;
}
static
int
atmel_pcm_resume
(
struct
snd_soc_dai
*
dai
)
{
struct
snd_pcm_runtime
*
runtime
=
dai
->
runtime
;
struct
atmel_runtime_data
*
prtd
;
struct
atmel_pcm_dma_params
*
params
;
if
(
!
runtime
)
return
0
;
prtd
=
runtime
->
private_data
;
params
=
prtd
->
params
;
/* restore the PDC registers and enable the PDC */
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
,
prtd
->
pdc_xpr_save
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
,
prtd
->
pdc_xcr_save
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
,
prtd
->
pdc_xnpr_save
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
,
prtd
->
pdc_xncr_save
);
ssc_writel
(
params
->
ssc
->
regs
,
PDC_PTCR
,
params
->
mask
->
pdc_enable
);
return
0
;
}
#else
#define atmel_pcm_suspend NULL
#define atmel_pcm_resume NULL
#endif
static
struct
snd_soc_platform_driver
atmel_soc_platform
=
{
.
ops
=
&
atmel_pcm_ops
,
.
pcm_new
=
atmel_pcm_new
,
.
pcm_free
=
atmel_pcm_free
,
.
suspend
=
atmel_pcm_suspend
,
.
resume
=
atmel_pcm_resume
,
};
int
atmel_pcm_pdc_platform_register
(
struct
device
*
dev
)
{
return
snd_soc_register_platform
(
dev
,
&
atmel_soc_platform
);
}
EXPORT_SYMBOL
(
atmel_pcm_pdc_platform_register
);
void
atmel_pcm_pdc_platform_unregister
(
struct
device
*
dev
)
{
snd_soc_unregister_platform
(
dev
);
}
EXPORT_SYMBOL
(
atmel_pcm_pdc_platform_unregister
);
MODULE_AUTHOR
(
"Sedji Gaouaou <sedji.gaouaou@atmel.com>"
);
MODULE_DESCRIPTION
(
"Atmel PCM module"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/atmel/atmel-pcm.c
View file @
3bc32492
...
...
@@ -32,80 +32,25 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
#include <linux/atmel-ssc.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "atmel-pcm.h"
/*--------------------------------------------------------------------------*\
* Hardware definition
\*--------------------------------------------------------------------------*/
/* TODO: These values were taken from the AT91 platform driver, check
* them against real values for AT32
*/
static
const
struct
snd_pcm_hardware
atmel_pcm_hardware
=
{
.
info
=
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_PAUSE
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
,
.
period_bytes_min
=
32
,
.
period_bytes_max
=
8192
,
.
periods_min
=
2
,
.
periods_max
=
1024
,
.
buffer_bytes_max
=
32
*
1024
,
};
/*--------------------------------------------------------------------------*\
* Data types
\*--------------------------------------------------------------------------*/
struct
atmel_runtime_data
{
struct
atmel_pcm_dma_params
*
params
;
dma_addr_t
dma_buffer
;
/* physical address of dma buffer */
dma_addr_t
dma_buffer_end
;
/* first address beyond DMA buffer */
size_t
period_size
;
dma_addr_t
period_ptr
;
/* physical address of next period */
/* PDC register save */
u32
pdc_xpr_save
;
u32
pdc_xcr_save
;
u32
pdc_xnpr_save
;
u32
pdc_xncr_save
;
};
/*--------------------------------------------------------------------------*\
* Helper functions
\*--------------------------------------------------------------------------*/
static
int
atmel_pcm_preallocate_dma_buffer
(
struct
snd_pcm
*
pcm
,
int
stream
)
{
struct
snd_pcm_substream
*
substream
=
pcm
->
streams
[
stream
].
substream
;
struct
snd_dma_buffer
*
buf
=
&
substream
->
dma_buffer
;
size_t
size
=
atmel_pcm_hardware
.
buffer_bytes_max
;
size_t
size
=
ATMEL_SSC_DMABUF_SIZE
;
buf
->
dev
.
type
=
SNDRV_DMA_TYPE_DEV
;
buf
->
dev
.
dev
=
pcm
->
card
->
dev
;
buf
->
private_data
=
NULL
;
buf
->
area
=
dma_alloc_coherent
(
pcm
->
card
->
dev
,
size
,
&
buf
->
addr
,
GFP_KERNEL
);
pr_debug
(
"atmel-pcm:"
"preallocate_dma_buffer: area=%p, addr=%p, size=%d
\n
"
,
(
void
*
)
buf
->
area
,
(
void
*
)
buf
->
addr
,
size
);
&
buf
->
addr
,
GFP_KERNEL
);
pr_debug
(
"atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d
\n
"
,
(
void
*
)
buf
->
area
,
(
void
*
)
buf
->
addr
,
size
);
if
(
!
buf
->
area
)
return
-
ENOMEM
;
...
...
@@ -113,258 +58,19 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
buf
->
bytes
=
size
;
return
0
;
}
/*--------------------------------------------------------------------------*\
* ISR
\*--------------------------------------------------------------------------*/
static
void
atmel_pcm_dma_irq
(
u32
ssc_sr
,
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
static
int
count
;
count
++
;
if
(
ssc_sr
&
params
->
mask
->
ssc_endbuf
)
{
pr_warning
(
"atmel-pcm: buffer %s on %s"
" (SSC_SR=%#x, count=%d)
\n
"
,
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
?
"underrun"
:
"overrun"
,
params
->
name
,
ssc_sr
,
count
);
/* re-start the PDC */
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
prtd
->
period_ptr
+=
prtd
->
period_size
;
if
(
prtd
->
period_ptr
>=
prtd
->
dma_buffer_end
)
prtd
->
period_ptr
=
prtd
->
dma_buffer
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_enable
);
}
if
(
ssc_sr
&
params
->
mask
->
ssc_endx
)
{
/* Load the PDC next pointer and counter registers */
prtd
->
period_ptr
+=
prtd
->
period_size
;
if
(
prtd
->
period_ptr
>=
prtd
->
dma_buffer_end
)
prtd
->
period_ptr
=
prtd
->
dma_buffer
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
}
snd_pcm_period_elapsed
(
substream
);
}
/*--------------------------------------------------------------------------*\
* PCM operations
\*--------------------------------------------------------------------------*/
static
int
atmel_pcm_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
=
runtime
->
private_data
;
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
/* this may get called several times by oss emulation
* with different params */
snd_pcm_set_runtime_buffer
(
substream
,
&
substream
->
dma_buffer
);
runtime
->
dma_bytes
=
params_buffer_bytes
(
params
);
prtd
->
params
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
prtd
->
params
->
dma_intr_handler
=
atmel_pcm_dma_irq
;
prtd
->
dma_buffer
=
runtime
->
dma_addr
;
prtd
->
dma_buffer_end
=
runtime
->
dma_addr
+
runtime
->
dma_bytes
;
prtd
->
period_size
=
params_period_bytes
(
params
);
pr_debug
(
"atmel-pcm: "
"hw_params: DMA for %s initialized "
"(dma_bytes=%u, period_size=%u)
\n
"
,
prtd
->
params
->
name
,
runtime
->
dma_bytes
,
prtd
->
period_size
);
return
0
;
}
static
int
atmel_pcm_hw_free
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
if
(
params
!=
NULL
)
{
ssc_writex
(
params
->
ssc
->
regs
,
SSC_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
prtd
->
params
->
dma_intr_handler
=
NULL
;
}
return
0
;
}
static
int
atmel_pcm_prepare
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
ssc_writex
(
params
->
ssc
->
regs
,
SSC_IDR
,
params
->
mask
->
ssc_endx
|
params
->
mask
->
ssc_endbuf
);
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
return
0
;
}
static
int
atmel_pcm_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
snd_pcm_runtime
*
rtd
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
=
rtd
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
int
ret
=
0
;
pr_debug
(
"atmel-pcm:buffer_size = %ld,"
"dma_area = %p, dma_bytes = %u
\n
"
,
rtd
->
buffer_size
,
rtd
->
dma_area
,
rtd
->
dma_bytes
);
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
prtd
->
period_ptr
=
prtd
->
dma_buffer
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
prtd
->
period_ptr
+=
prtd
->
period_size
;
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
,
prtd
->
period_ptr
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
,
prtd
->
period_size
/
params
->
pdc_xfer_size
);
pr_debug
(
"atmel-pcm: trigger: "
"period_ptr=%lx, xpr=%u, "
"xcr=%u, xnpr=%u, xncr=%u
\n
"
,
(
unsigned
long
)
prtd
->
period_ptr
,
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
),
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
),
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
),
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
));
ssc_writex
(
params
->
ssc
->
regs
,
SSC_IER
,
params
->
mask
->
ssc_endx
|
params
->
mask
->
ssc_endbuf
);
ssc_writex
(
params
->
ssc
->
regs
,
SSC_PDC_PTCR
,
params
->
mask
->
pdc_enable
);
pr_debug
(
"sr=%u imr=%u
\n
"
,
ssc_readx
(
params
->
ssc
->
regs
,
SSC_SR
),
ssc_readx
(
params
->
ssc
->
regs
,
SSC_IER
));
break
;
/* SNDRV_PCM_TRIGGER_START */
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_disable
);
break
;
case
SNDRV_PCM_TRIGGER_RESUME
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
ssc_writex
(
params
->
ssc
->
regs
,
ATMEL_PDC_PTCR
,
params
->
mask
->
pdc_enable
);
break
;
default:
ret
=
-
EINVAL
;
}
return
ret
;
}
static
snd_pcm_uframes_t
atmel_pcm_pointer
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
=
runtime
->
private_data
;
struct
atmel_pcm_dma_params
*
params
=
prtd
->
params
;
dma_addr_t
ptr
;
snd_pcm_uframes_t
x
;
ptr
=
(
dma_addr_t
)
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
);
x
=
bytes_to_frames
(
runtime
,
ptr
-
prtd
->
dma_buffer
);
if
(
x
==
runtime
->
buffer_size
)
x
=
0
;
return
x
;
}
static
int
atmel_pcm_open
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
struct
atmel_runtime_data
*
prtd
;
int
ret
=
0
;
snd_soc_set_runtime_hwparams
(
substream
,
&
atmel_pcm_hardware
);
/* ensure that buffer size is a multiple of period size */
ret
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
);
if
(
ret
<
0
)
goto
out
;
prtd
=
kzalloc
(
sizeof
(
struct
atmel_runtime_data
),
GFP_KERNEL
);
if
(
prtd
==
NULL
)
{
ret
=
-
ENOMEM
;
goto
out
;
}
runtime
->
private_data
=
prtd
;
out:
return
ret
;
}
static
int
atmel_pcm_close
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_runtime_data
*
prtd
=
substream
->
runtime
->
private_data
;
kfree
(
prtd
);
return
0
;
}
static
int
atmel_pcm_mmap
(
struct
snd_pcm_substream
*
substream
,
int
atmel_pcm_mmap
(
struct
snd_pcm_substream
*
substream
,
struct
vm_area_struct
*
vma
)
{
return
remap_pfn_range
(
vma
,
vma
->
vm_start
,
substream
->
dma_buffer
.
addr
>>
PAGE_SHIFT
,
vma
->
vm_end
-
vma
->
vm_start
,
vma
->
vm_page_prot
);
}
EXPORT_SYMBOL_GPL
(
atmel_pcm_mmap
);
static
struct
snd_pcm_ops
atmel_pcm_ops
=
{
.
open
=
atmel_pcm_open
,
.
close
=
atmel_pcm_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
atmel_pcm_hw_params
,
.
hw_free
=
atmel_pcm_hw_free
,
.
prepare
=
atmel_pcm_prepare
,
.
trigger
=
atmel_pcm_trigger
,
.
pointer
=
atmel_pcm_pointer
,
.
mmap
=
atmel_pcm_mmap
,
};
/*--------------------------------------------------------------------------*\
* ASoC platform driver
\*--------------------------------------------------------------------------*/
static
u64
atmel_pcm_dmamask
=
DMA_BIT_MASK
(
32
);
static
int
atmel_pcm_new
(
struct
snd_soc_pcm_runtime
*
rtd
)
int
atmel_pcm_new
(
struct
snd_soc_pcm_runtime
*
rtd
)
{
struct
snd_card
*
card
=
rtd
->
card
->
snd_card
;
struct
snd_pcm
*
pcm
=
rtd
->
pcm
;
...
...
@@ -376,6 +82,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
card
->
dev
->
coherent_dma_mask
=
DMA_BIT_MASK
(
32
);
if
(
pcm
->
streams
[
SNDRV_PCM_STREAM_PLAYBACK
].
substream
)
{
pr_debug
(
"atmel-pcm: allocating PCM playback DMA buffer
\n
"
);
ret
=
atmel_pcm_preallocate_dma_buffer
(
pcm
,
SNDRV_PCM_STREAM_PLAYBACK
);
if
(
ret
)
...
...
@@ -383,8 +90,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
if
(
pcm
->
streams
[
SNDRV_PCM_STREAM_CAPTURE
].
substream
)
{
pr_debug
(
"atmel-pcm:"
"Allocating PCM capture DMA buffer
\n
"
);
pr_debug
(
"atmel-pcm: allocating PCM capture DMA buffer
\n
"
);
ret
=
atmel_pcm_preallocate_dma_buffer
(
pcm
,
SNDRV_PCM_STREAM_CAPTURE
);
if
(
ret
)
...
...
@@ -393,8 +99,9 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
out:
return
ret
;
}
EXPORT_SYMBOL_GPL
(
atmel_pcm_new
);
static
void
atmel_pcm_free_dma_buffers
(
struct
snd_pcm
*
pcm
)
void
atmel_pcm_free
(
struct
snd_pcm
*
pcm
)
{
struct
snd_pcm_substream
*
substream
;
struct
snd_dma_buffer
*
buf
;
...
...
@@ -413,89 +120,5 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
buf
->
area
=
NULL
;
}
}
EXPORT_SYMBOL_GPL
(
atmel_pcm_free
);
#ifdef CONFIG_PM
static
int
atmel_pcm_suspend
(
struct
snd_soc_dai
*
dai
)
{
struct
snd_pcm_runtime
*
runtime
=
dai
->
runtime
;
struct
atmel_runtime_data
*
prtd
;
struct
atmel_pcm_dma_params
*
params
;
if
(
!
runtime
)
return
0
;
prtd
=
runtime
->
private_data
;
params
=
prtd
->
params
;
/* disable the PDC and save the PDC registers */
ssc_writel
(
params
->
ssc
->
regs
,
PDC_PTCR
,
params
->
mask
->
pdc_disable
);
prtd
->
pdc_xpr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
);
prtd
->
pdc_xcr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
);
prtd
->
pdc_xnpr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
);
prtd
->
pdc_xncr_save
=
ssc_readx
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
);
return
0
;
}
static
int
atmel_pcm_resume
(
struct
snd_soc_dai
*
dai
)
{
struct
snd_pcm_runtime
*
runtime
=
dai
->
runtime
;
struct
atmel_runtime_data
*
prtd
;
struct
atmel_pcm_dma_params
*
params
;
if
(
!
runtime
)
return
0
;
prtd
=
runtime
->
private_data
;
params
=
prtd
->
params
;
/* restore the PDC registers and enable the PDC */
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xpr
,
prtd
->
pdc_xpr_save
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xcr
,
prtd
->
pdc_xcr_save
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xnpr
,
prtd
->
pdc_xnpr_save
);
ssc_writex
(
params
->
ssc
->
regs
,
params
->
pdc
->
xncr
,
prtd
->
pdc_xncr_save
);
ssc_writel
(
params
->
ssc
->
regs
,
PDC_PTCR
,
params
->
mask
->
pdc_enable
);
return
0
;
}
#else
#define atmel_pcm_suspend NULL
#define atmel_pcm_resume NULL
#endif
static
struct
snd_soc_platform_driver
atmel_soc_platform
=
{
.
ops
=
&
atmel_pcm_ops
,
.
pcm_new
=
atmel_pcm_new
,
.
pcm_free
=
atmel_pcm_free_dma_buffers
,
.
suspend
=
atmel_pcm_suspend
,
.
resume
=
atmel_pcm_resume
,
};
static
int
__devinit
atmel_soc_platform_probe
(
struct
platform_device
*
pdev
)
{
return
snd_soc_register_platform
(
&
pdev
->
dev
,
&
atmel_soc_platform
);
}
static
int
__devexit
atmel_soc_platform_remove
(
struct
platform_device
*
pdev
)
{
snd_soc_unregister_platform
(
&
pdev
->
dev
);
return
0
;
}
static
struct
platform_driver
atmel_pcm_driver
=
{
.
driver
=
{
.
name
=
"atmel-pcm-audio"
,
.
owner
=
THIS_MODULE
,
},
.
probe
=
atmel_soc_platform_probe
,
.
remove
=
__devexit_p
(
atmel_soc_platform_remove
),
};
module_platform_driver
(
atmel_pcm_driver
);
MODULE_AUTHOR
(
"Sedji Gaouaou <sedji.gaouaou@atmel.com>"
);
MODULE_DESCRIPTION
(
"Atmel PCM module"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/atmel/atmel-pcm.h
View file @
3bc32492
...
...
@@ -36,6 +36,8 @@
#include <linux/atmel-ssc.h>
#define ATMEL_SSC_DMABUF_SIZE (64 * 1024)
/*
* Registers and status bits that are required by the PCM driver.
*/
...
...
@@ -50,6 +52,7 @@ struct atmel_pdc_regs {
struct
atmel_ssc_mask
{
u32
ssc_enable
;
/* SSC recv/trans enable */
u32
ssc_disable
;
/* SSC recv/trans disable */
u32
ssc_error
;
/* SSC error conditions */
u32
ssc_endx
;
/* SSC ENDTX or ENDRX */
u32
ssc_endbuf
;
/* SSC TXBUFE or RXBUFF */
u32
pdc_enable
;
/* PDC recv/trans enable */
...
...
@@ -80,4 +83,35 @@ struct atmel_pcm_dma_params {
#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
int
atmel_pcm_new
(
struct
snd_soc_pcm_runtime
*
rtd
);
void
atmel_pcm_free
(
struct
snd_pcm
*
pcm
);
int
atmel_pcm_mmap
(
struct
snd_pcm_substream
*
substream
,
struct
vm_area_struct
*
vma
);
#ifdef CONFIG_SND_ATMEL_SOC_PDC
int
atmel_pcm_pdc_platform_register
(
struct
device
*
dev
);
void
atmel_pcm_pdc_platform_unregister
(
struct
device
*
dev
);
#else
static
inline
int
atmel_pcm_pdc_platform_register
(
struct
device
*
dev
)
{
return
0
;
}
static
inline
void
atmel_pcm_pdc_platform_unregister
(
struct
device
*
dev
)
{
}
#endif
#ifdef CONFIG_SND_ATMEL_SOC_DMA
int
atmel_pcm_dma_platform_register
(
struct
device
*
dev
);
void
atmel_pcm_dma_platform_unregister
(
struct
device
*
dev
);
#else
static
inline
int
atmel_pcm_dma_platform_register
(
struct
device
*
dev
)
{
return
0
;
}
static
inline
void
atmel_pcm_dma_platform_unregister
(
struct
device
*
dev
)
{
}
#endif
#endif
/* _ATMEL_PCM_H */
sound/soc/atmel/atmel_ssc_dai.c
View file @
3bc32492
...
...
@@ -48,11 +48,7 @@
#include "atmel_ssc_dai.h"
#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
#define NUM_SSC_DEVICES 1
#else
#define NUM_SSC_DEVICES 3
#endif
/*
* SSC PDC registers required by the PCM DMA engine.
...
...
@@ -107,7 +103,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.
pdc
=
&
pdc_rx_reg
,
.
mask
=
&
ssc_rx_mask
,
}
},
#if NUM_SSC_DEVICES == 3
{{
.
name
=
"SSC1 PCM out"
,
.
pdc
=
&
pdc_tx_reg
,
...
...
@@ -128,7 +123,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.
pdc
=
&
pdc_rx_reg
,
.
mask
=
&
ssc_rx_mask
,
}
},
#endif
};
...
...
@@ -139,7 +133,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
.
dir_mask
=
SSC_DIR_MASK_UNUSED
,
.
initialized
=
0
,
},
#if NUM_SSC_DEVICES == 3
{
.
name
=
"ssc1"
,
.
lock
=
__SPIN_LOCK_UNLOCKED
(
ssc_info
[
1
].
lock
),
...
...
@@ -152,7 +145,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
.
dir_mask
=
SSC_DIR_MASK_UNUSED
,
.
initialized
=
0
,
},
#endif
};
...
...
@@ -690,27 +682,9 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
static
int
atmel_ssc_probe
(
struct
snd_soc_dai
*
dai
)
{
struct
atmel_ssc_info
*
ssc_p
=
&
ssc_info
[
dai
->
id
];
int
ret
=
0
;
snd_soc_dai_set_drvdata
(
dai
,
ssc_p
);
/*
* Request SSC device
*/
ssc_p
->
ssc
=
ssc_request
(
dai
->
id
);
if
(
IS_ERR
(
ssc_p
->
ssc
))
{
printk
(
KERN_ERR
"ASoC: Failed to request SSC %d
\n
"
,
dai
->
id
);
ret
=
PTR_ERR
(
ssc_p
->
ssc
);
}
return
ret
;
}
static
int
atmel_ssc_remove
(
struct
snd_soc_dai
*
dai
)
{
struct
atmel_ssc_info
*
ssc_p
=
snd_soc_dai_get_drvdata
(
dai
);
ssc_free
(
ssc_p
->
ssc
);
return
0
;
}
...
...
@@ -728,30 +702,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.
set_clkdiv
=
atmel_ssc_set_dai_clkdiv
,
};
static
struct
snd_soc_dai_driver
atmel_ssc_dai
[
NUM_SSC_DEVICES
]
=
{
{
.
name
=
"atmel-ssc-dai.0"
,
.
probe
=
atmel_ssc_probe
,
.
remove
=
atmel_ssc_remove
,
.
suspend
=
atmel_ssc_suspend
,
.
resume
=
atmel_ssc_resume
,
.
playback
=
{
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
ATMEL_SSC_RATES
,
.
formats
=
ATMEL_SSC_FORMATS
,},
.
capture
=
{
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
ATMEL_SSC_RATES
,
.
formats
=
ATMEL_SSC_FORMATS
,},
.
ops
=
&
atmel_ssc_dai_ops
,
},
#if NUM_SSC_DEVICES == 3
{
.
name
=
"atmel-ssc-dai.1"
,
static
struct
snd_soc_dai_driver
atmel_ssc_dai
=
{
.
probe
=
atmel_ssc_probe
,
.
remove
=
atmel_ssc_remove
,
.
suspend
=
atmel_ssc_suspend
,
.
resume
=
atmel_ssc_resume
,
.
playback
=
{
...
...
@@ -765,50 +717,50 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
.
rates
=
ATMEL_SSC_RATES
,
.
formats
=
ATMEL_SSC_FORMATS
,},
.
ops
=
&
atmel_ssc_dai_ops
,
},
{
.
name
=
"atmel-ssc-dai.2"
,
.
probe
=
atmel_ssc_probe
,
.
remove
=
atmel_ssc_remove
,
.
suspend
=
atmel_ssc_suspend
,
.
resume
=
atmel_ssc_resume
,
.
playback
=
{
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
ATMEL_SSC_RATES
,
.
formats
=
ATMEL_SSC_FORMATS
,},
.
capture
=
{
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
ATMEL_SSC_RATES
,
.
formats
=
ATMEL_SSC_FORMATS
,},
.
ops
=
&
atmel_ssc_dai_ops
,
},
#endif
};
static
__devinit
int
asoc_ssc_probe
(
struct
platform_device
*
p
dev
)
static
int
asoc_ssc_init
(
struct
device
*
dev
)
{
BUG_ON
(
pdev
->
id
<
0
);
BUG_ON
(
pdev
->
id
>=
ARRAY_SIZE
(
atmel_ssc_dai
));
return
snd_soc_register_dai
(
&
pdev
->
dev
,
&
atmel_ssc_dai
[
pdev
->
id
]);
}
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
ssc_device
*
ssc
=
platform_get_drvdata
(
pdev
);
int
ret
;
ret
=
snd_soc_register_dai
(
dev
,
&
atmel_ssc_dai
);
if
(
ret
)
{
dev_err
(
dev
,
"Could not register DAI: %d
\n
"
,
ret
);
goto
err
;
}
if
(
ssc
->
pdata
->
use_dma
)
ret
=
atmel_pcm_dma_platform_register
(
dev
);
else
ret
=
atmel_pcm_pdc_platform_register
(
dev
);
if
(
ret
)
{
dev_err
(
dev
,
"Could not register PCM: %d
\n
"
,
ret
);
goto
err_unregister_dai
;
};
static
int
__devexit
asoc_ssc_remove
(
struct
platform_device
*
pdev
)
{
snd_soc_unregister_dai
(
&
pdev
->
dev
);
return
0
;
err_unregister_dai:
snd_soc_unregister_dai
(
dev
);
err:
return
ret
;
}
static
struct
platform_driver
asoc_ssc_driver
=
{
.
driver
=
{
.
name
=
"atmel-ssc-dai"
,
.
owner
=
THIS_MODULE
,
},
static
void
asoc_ssc_exit
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
ssc_device
*
ssc
=
platform_get_drvdata
(
pdev
);
.
probe
=
asoc_ssc_probe
,
.
remove
=
__devexit_p
(
asoc_ssc_remove
),
};
if
(
ssc
->
pdata
->
use_dma
)
atmel_pcm_dma_platform_unregister
(
dev
);
else
atmel_pcm_pdc_platform_unregister
(
dev
);
snd_soc_unregister_dai
(
dev
);
}
/**
* atmel_ssc_set_audio - Allocate the specified SSC for audio use.
...
...
@@ -816,50 +768,32 @@ static struct platform_driver asoc_ssc_driver = {
int
atmel_ssc_set_audio
(
int
ssc_id
)
{
struct
ssc_device
*
ssc
;
static
struct
platform_device
*
dma_pdev
;
struct
platform_device
*
ssc_pdev
;
int
ret
;
if
(
ssc_id
<
0
||
ssc_id
>=
ARRAY_SIZE
(
atmel_ssc_dai
))
return
-
EINVAL
;
/* Allocate a dummy device for DMA if we don't have one already */
if
(
!
dma_pdev
)
{
dma_pdev
=
platform_device_alloc
(
"atmel-pcm-audio"
,
-
1
);
if
(
!
dma_pdev
)
return
-
ENOMEM
;
ret
=
platform_device_add
(
dma_pdev
);
if
(
ret
<
0
)
{
platform_device_put
(
dma_pdev
);
dma_pdev
=
NULL
;
return
ret
;
}
}
ssc_pdev
=
platform_device_alloc
(
"atmel-ssc-dai"
,
ssc_id
);
if
(
!
ssc_pdev
)
return
-
ENOMEM
;
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc
=
ssc_request
(
ssc_id
);
if
(
IS_ERR
(
ssc
))
pr_
warn
(
"Unable to parent ASoC SSC DAI on SSC: %ld
\n
"
,
if
(
IS_ERR
(
ssc
))
{
pr_
err
(
"Unable to parent ASoC SSC DAI on SSC: %ld
\n
"
,
PTR_ERR
(
ssc
));
else
{
ssc_pdev
->
dev
.
parent
=
&
(
ssc
->
pdev
->
dev
);
ssc_
free
(
ssc
)
;
return
PTR_ERR
(
ssc
);
}
else
{
ssc_
info
[
ssc_id
].
ssc
=
ssc
;
}
ret
=
platform_device_add
(
ssc_pdev
);
if
(
ret
<
0
)
platform_device_put
(
ssc_pdev
);
ret
=
asoc_ssc_init
(
&
ssc
->
pdev
->
dev
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
atmel_ssc_set_audio
);
module_platform_driver
(
asoc_ssc_driver
);
void
atmel_ssc_put_audio
(
int
ssc_id
)
{
struct
ssc_device
*
ssc
=
ssc_info
[
ssc_id
].
ssc
;
ssc_free
(
ssc
);
asoc_ssc_exit
(
&
ssc
->
pdev
->
dev
);
}
EXPORT_SYMBOL_GPL
(
atmel_ssc_put_audio
);
/* Module information */
MODULE_AUTHOR
(
"Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"
);
...
...
sound/soc/atmel/atmel_ssc_dai.h
View file @
3bc32492
...
...
@@ -117,6 +117,7 @@ struct atmel_ssc_info {
struct
atmel_ssc_state
ssc_state
;
};
int
atmel_ssc_set_audio
(
int
ssc
);
int
atmel_ssc_set_audio
(
int
ssc_id
);
void
atmel_ssc_put_audio
(
int
ssc_id
);
#endif
/* _AT91_SSC_DAI_H */
sound/soc/atmel/sam9g20_wm8731.c
View file @
3bc32492
...
...
@@ -38,6 +38,8 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/pinctrl/consumer.h>
#include <linux/atmel-ssc.h>
#include <sound/core.h>
...
...
@@ -179,10 +181,10 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
static
struct
snd_soc_dai_link
at91sam9g20ek_dai
=
{
.
name
=
"WM8731"
,
.
stream_name
=
"WM8731 PCM"
,
.
cpu_dai_name
=
"at
mel-ssc-dai
.0"
,
.
cpu_dai_name
=
"at
91rm9200_ssc
.0"
,
.
codec_dai_name
=
"wm8731-hifi"
,
.
init
=
at91sam9g20ek_wm8731_init
,
.
platform_name
=
"at
mel-pcm-audio
"
,
.
platform_name
=
"at
91rm9200_ssc.0
"
,
.
codec_name
=
"wm8731.0-001b"
,
.
ops
=
&
at91sam9g20ek_ops
,
};
...
...
@@ -195,20 +197,31 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.
set_bias_level
=
at91sam9g20ek_set_bias_level
,
};
static
struct
platform_device
*
at91sam9g20ek_snd_device
;
static
int
__init
at91sam9g20ek_init
(
void
)
static
int
__devinit
at91sam9g20ek_audio_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
struct
device_node
*
codec_np
,
*
cpu_np
;
struct
clk
*
pllb
;
struct
snd_soc_card
*
card
=
&
snd_soc_at91sam9g20ek
;
struct
pinctrl
*
pinctrl
;
int
ret
;
if
(
!
(
machine_is_at91sam9g20ek
()
||
machine_is_at91sam9g20ek_2mmc
()))
return
-
ENODEV
;
pinctrl
=
devm_pinctrl_get_select_default
(
&
pdev
->
dev
);
if
(
IS_ERR
(
pinctrl
))
{
dev_err
(
&
pdev
->
dev
,
"Failed to request pinctrl for mck
\n
"
);
return
PTR_ERR
(
pinctrl
);
}
if
(
!
np
)
{
if
(
!
(
machine_is_at91sam9g20ek
()
||
machine_is_at91sam9g20ek_2mmc
()))
return
-
ENODEV
;
}
ret
=
atmel_ssc_set_audio
(
0
);
if
(
ret
!=
0
)
{
pr_err
(
"Failed to set SSC 0 for audio: %d
\n
"
,
ret
);
return
ret
;
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"ssc channel is not valid
\n
"
);
return
-
EINVAL
;
}
/*
...
...
@@ -236,45 +249,92 @@ static int __init at91sam9g20ek_init(void)
clk_set_rate
(
mclk
,
MCLK_RATE
);
at91sam9g20ek_snd_device
=
platform_device_alloc
(
"soc-audio"
,
-
1
);
if
(
!
at91sam9g20ek_snd_device
)
{
printk
(
KERN_ERR
"ASoC: Platform device allocation failed
\n
"
);
ret
=
-
ENOMEM
;
goto
err_mclk
;
card
->
dev
=
&
pdev
->
dev
;
/* Parse device node info */
if
(
np
)
{
ret
=
snd_soc_of_parse_card_name
(
card
,
"atmel,model"
);
if
(
ret
)
goto
err
;
ret
=
snd_soc_of_parse_audio_routing
(
card
,
"atmel,audio-routing"
);
if
(
ret
)
goto
err
;
/* Parse codec info */
at91sam9g20ek_dai
.
codec_name
=
NULL
;
codec_np
=
of_parse_phandle
(
np
,
"atmel,audio-codec"
,
0
);
if
(
!
codec_np
)
{
dev_err
(
&
pdev
->
dev
,
"codec info missing
\n
"
);
return
-
EINVAL
;
}
at91sam9g20ek_dai
.
codec_of_node
=
codec_np
;
/* Parse dai and platform info */
at91sam9g20ek_dai
.
cpu_dai_name
=
NULL
;
at91sam9g20ek_dai
.
platform_name
=
NULL
;
cpu_np
=
of_parse_phandle
(
np
,
"atmel,ssc-controller"
,
0
);
if
(
!
cpu_np
)
{
dev_err
(
&
pdev
->
dev
,
"dai and pcm info missing
\n
"
);
return
-
EINVAL
;
}
at91sam9g20ek_dai
.
cpu_of_node
=
cpu_np
;
at91sam9g20ek_dai
.
platform_of_node
=
cpu_np
;
of_node_put
(
codec_np
);
of_node_put
(
cpu_np
);
}
platform_set_drvdata
(
at91sam9g20ek_snd_device
,
&
snd_soc_at91sam9g20ek
);
ret
=
platform_device_add
(
at91sam9g20ek_snd_device
);
ret
=
snd_soc_register_card
(
card
);
if
(
ret
)
{
printk
(
KERN_ERR
"ASoC: Platform device allocation failed
\n
"
);
goto
err_device_add
;
printk
(
KERN_ERR
"ASoC: snd_soc_register_card() failed
\n
"
);
}
return
ret
;
err_device_add:
platform_device_put
(
at91sam9g20ek_snd_device
);
err_mclk:
clk_put
(
mclk
);
mclk
=
NULL
;
err:
atmel_ssc_put_audio
(
0
);
return
ret
;
}
static
void
__exit
at91sam9g20ek_exit
(
void
)
static
int
__devexit
at91sam9g20ek_audio_remove
(
struct
platform_device
*
pdev
)
{
platform_device_unregister
(
at91sam9g20ek_snd_device
);
at91sam9g20ek_snd_device
=
NULL
;
struct
snd_soc_card
*
card
=
platform_get_drvdata
(
pdev
);
atmel_ssc_put_audio
(
0
);
snd_soc_unregister_card
(
card
);
clk_put
(
mclk
);
mclk
=
NULL
;
return
0
;
}
module_init
(
at91sam9g20ek_init
);
module_exit
(
at91sam9g20ek_exit
);
#ifdef CONFIG_OF
static
const
struct
of_device_id
at91sam9g20ek_wm8731_dt_ids
[]
=
{
{
.
compatible
=
"atmel,at91sam9g20ek-wm8731-audio"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
at91sam9g20ek_wm8731_dt_ids
);
#endif
static
struct
platform_driver
at91sam9g20ek_audio_driver
=
{
.
driver
=
{
.
name
=
"at91sam9g20ek-audio"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
of_match_ptr
(
at91sam9g20ek_wm8731_dt_ids
),
},
.
probe
=
at91sam9g20ek_audio_probe
,
.
remove
=
__devexit_p
(
at91sam9g20ek_audio_remove
),
};
module_platform_driver
(
at91sam9g20ek_audio_driver
);
/* Module information */
MODULE_AUTHOR
(
"Sedji Gaouaou <sedji.gaouaou@atmel.com>"
);
MODULE_DESCRIPTION
(
"ALSA SoC AT91SAM9G20EK_WM8731"
);
MODULE_ALIAS
(
"platform:at91sam9g20ek-audio"
);
MODULE_LICENSE
(
"GPL"
);
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