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
04877397
Commit
04877397
authored
Aug 22, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/atmel' into asoc-next
parents
0b1107b2
f813175a
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
644 additions
and
121 deletions
+644
-121
Documentation/devicetree/bindings/misc/atmel-ssc.txt
Documentation/devicetree/bindings/misc/atmel-ssc.txt
+22
-1
Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt
...n/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt
+35
-0
Documentation/devicetree/bindings/sound/atmel-wm8904.txt
Documentation/devicetree/bindings/sound/atmel-wm8904.txt
+55
-0
Documentation/devicetree/bindings/sound/wm8731.txt
Documentation/devicetree/bindings/sound/wm8731.txt
+9
-0
include/linux/atmel-ssc.h
include/linux/atmel-ssc.h
+1
-1
sound/soc/atmel/Kconfig
sound/soc/atmel/Kconfig
+21
-0
sound/soc/atmel/Makefile
sound/soc/atmel/Makefile
+4
-0
sound/soc/atmel/atmel-pcm-dma.c
sound/soc/atmel/atmel-pcm-dma.c
+16
-102
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/atmel_ssc_dai.c
+19
-17
sound/soc/atmel/atmel_wm8904.c
sound/soc/atmel/atmel_wm8904.c
+254
-0
sound/soc/atmel/sam9x5_wm8731.c
sound/soc/atmel/sam9x5_wm8731.c
+208
-0
No files found.
Documentation/devicetree/bindings/misc/atmel-ssc.txt
View file @
04877397
...
...
@@ -7,9 +7,30 @@ Required properties:
- reg: Should contain SSC registers location and length
- interrupts: Should contain SSC interrupt
Example:
Required properties for devices compatible with "atmel,at91sam9g45-ssc":
- dmas: DMA specifier, consisting of a phandle to DMA controller node,
the memory interface and SSC DMA channel ID (for tx and rx).
See Documentation/devicetree/bindings/dma/atmel-dma.txt for details.
- dma-names: Must be "tx", "rx".
Examples:
- PDC transfer:
ssc0: ssc@fffbc000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfffbc000 0x4000>;
interrupts = <14 4 5>;
};
- DMA transfer:
ssc0: ssc@f0010000 {
compatible = "atmel,at91sam9g45-ssc";
reg = <0xf0010000 0x4000>;
interrupts = <28 4 5>;
dmas = <&dma0 1 13>,
<&dma0 1 14>;
dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
status = "disabled";
};
Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt
0 → 100644
View file @
04877397
* Atmel at91sam9x5ek wm8731 audio complex
Required properties:
- compatible: "atmel,sam9x5-wm8731-audio"
- atmel,model: The user-visible name of this sound complex.
- atmel,ssc-controller: The phandle of the SSC controller
- atmel,audio-codec: The phandle of the WM8731 audio codec
- atmel,audio-routing: A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source.
Available audio endpoints for the audio-routing table:
Board connectors:
* Headphone Jack
* Line In Jack
wm8731 pins:
cf Documentation/devicetree/bindings/sound/wm8731.txt
Example:
sound {
compatible = "atmel,sam9x5-wm8731-audio";
atmel,model = "wm8731 @ AT91SAM9X5EK";
atmel,audio-routing =
"Headphone Jack", "RHPOUT",
"Headphone Jack", "LHPOUT",
"LLINEIN", "Line In Jack",
"RLINEIN", "Line In Jack";
atmel,ssc-controller = <&ssc0>;
atmel,audio-codec = <&wm8731>;
};
Documentation/devicetree/bindings/sound/atmel-wm8904.txt
0 → 100644
View file @
04877397
Atmel ASoC driver with wm8904 audio codec complex
Required properties:
- compatible: "atmel,asoc-wm8904"
- atmel,model: The user-visible name of this sound complex.
- atmel,audio-routing: A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources and
sinks are the WM8904's pins, and the jacks on the board:
WM8904 pins:
* IN1L
* IN1R
* IN2L
* IN2R
* IN3L
* IN3R
* HPOUTL
* HPOUTR
* LINEOUTL
* LINEOUTR
* MICBIAS
Board connectors:
* Headphone Jack
* Line In Jack
* Mic
- atmel,ssc-controller: The phandle of the SSC controller
- atmel,audio-codec: The phandle of the WM8904 audio codec
Optional properties:
- pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
Example:
sound {
compatible = "atmel,asoc-wm8904";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pck0_as_mck>;
atmel,model = "wm8904 @ AT91SAM9N12EK";
atmel,audio-routing =
"Headphone Jack", "HPOUTL",
"Headphone Jack", "HPOUTR",
"IN2L", "Line In Jack",
"IN2R", "Line In Jack",
"Mic", "MICBIAS",
"IN1L", "Mic";
atmel,ssc-controller = <&ssc0>;
atmel,audio-codec = <&wm8904>;
};
Documentation/devicetree/bindings/sound/wm8731.txt
View file @
04877397
...
...
@@ -16,3 +16,12 @@ codec: wm8731@1a {
compatible = "wlf,wm8731";
reg = <0x1a>;
};
Available audio endpoints for an audio-routing table:
* LOUT: Left Channel Line Output
* ROUT: Right Channel Line Output
* LHPOUT: Left Channel Headphone Output
* RHPOUT: Right Channel Headphone Output
* LLINEIN: Left Channel Line Input
* RLINEIN: Right Channel Line Input
* MICIN: Microphone Input
include/linux/atmel-ssc.h
View file @
04877397
...
...
@@ -11,7 +11,7 @@ struct atmel_ssc_platform_data {
struct
ssc_device
{
struct
list_head
list
;
resource_size
_t
phybase
;
dma_addr
_t
phybase
;
void
__iomem
*
regs
;
struct
platform_device
*
pdev
;
struct
atmel_ssc_platform_data
*
pdata
;
...
...
sound/soc/atmel/Kconfig
View file @
04877397
...
...
@@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC
config SND_ATMEL_SOC_DMA
tristate
depends on SND_ATMEL_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
config SND_ATMEL_SOC_SSC
tristate
...
...
@@ -32,6 +33,26 @@ config SND_AT91_SOC_SAM9G20_WM8731
Say Y if you want to add support for SoC audio on WM8731-based
AT91sam9g20 evaluation board.
config SND_ATMEL_SOC_WM8904
tristate "Atmel ASoC driver for boards using WM8904 codec"
depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC
select SND_ATMEL_SOC_SSC
select SND_ATMEL_SOC_DMA
select SND_SOC_WM8904
help
Say Y if you want to add support for Atmel ASoC driver for boards using
WM8904 codec.
config SND_AT91_SOC_SAM9X5_WM8731
tristate "SoC Audio support for WM8731-based at91sam9x5 board"
depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5
select SND_ATMEL_SOC_SSC
select SND_ATMEL_SOC_DMA
select SND_SOC_WM8731
help
Say Y if you want to add support for audio SoC on an
at91sam9x5 based board that is using WM8731 codec.
config SND_AT91_SOC_AFEB9260
tristate "SoC Audio support for AFEB9260 board"
depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
...
...
sound/soc/atmel/Makefile
View file @
04877397
...
...
@@ -11,6 +11,10 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
# AT91 Machine Support
snd-soc-sam9g20-wm8731-objs
:=
sam9g20_wm8731.o
snd-atmel-soc-wm8904-objs
:=
atmel_wm8904.o
snd-soc-sam9x5-wm8731-objs
:=
sam9x5_wm8731.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731)
+=
snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904)
+=
snd-atmel-soc-wm8904.o
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731)
+=
snd-soc-sam9x5-wm8731.o
obj-$(CONFIG_SND_AT91_SOC_AFEB9260)
+=
snd-soc-afeb9260.o
sound/soc/atmel/atmel-pcm-dma.c
View file @
04877397
...
...
@@ -91,138 +91,52 @@ static void atmel_pcm_dma_irq(u32 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
snd_pcm_hw_params
*
params
,
struct
dma_slave_config
*
slave_config
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
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_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
substream
);
ssc
=
prtd
->
ssc
;
ret
=
snd_hwparams_to_dma_slave_config
(
substream
,
params
,
&
slave_config
);
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
;
slave_config
->
dst_addr
=
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
;
}
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_request_chan
(
substream
,
filter
,
sdata
);
if
(
ret
)
{
pr_err
(
"atmel-pcm: dmaengine pcm open failed
\n
"
);
return
-
EINVAL
;
}
ret
=
atmel_pcm_configure_dma
(
substream
,
params
,
prtd
);
if
(
ret
)
{
pr_err
(
"atmel-pcm: failed to configure dmai
\n
"
);
goto
err
;
slave_config
->
src_addr
=
ssc
->
phybase
+
SSC_RHR
;
slave_config
->
src_maxburst
=
1
;
}
prtd
->
dma_intr_handler
=
atmel_pcm_dma_irq
;
return
0
;
err:
snd_dmaengine_pcm_close_release_chan
(
substream
);
return
ret
;
}
static
int
atmel_pcm_dma_prepare
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
atmel_pcm_dma_params
*
prtd
;
prtd
=
snd_soc_dai_get_dma_data
(
rtd
->
cpu_dai
,
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
struct
snd_pcm_ops
atmel_pcm_ops
=
{
.
open
=
atmel_pcm_open
,
.
close
=
snd_dmaengine_pcm_close_release_chan
,
.
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
,
static
const
struct
snd_dmaengine_pcm_config
atmel_dmaengine_pcm_config
=
{
.
prepare_slave_config
=
atmel_pcm_configure_dma
,
.
pcm_hardware
=
&
atmel_pcm_dma_hardware
,
.
prealloc_buffer_size
=
ATMEL_SSC_DMABUF_SIZE
,
};
int
atmel_pcm_dma_platform_register
(
struct
device
*
dev
)
{
return
snd_soc_register_platform
(
dev
,
&
atmel_soc_platform
);
return
snd_dmaengine_pcm_register
(
dev
,
&
atmel_dmaengine_pcm_config
,
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE
);
}
EXPORT_SYMBOL
(
atmel_pcm_dma_platform_register
);
void
atmel_pcm_dma_platform_unregister
(
struct
device
*
dev
)
{
snd_
soc_unregister_platform
(
dev
);
snd_
dmaengine_pcm_unregister
(
dev
);
}
EXPORT_SYMBOL
(
atmel_pcm_dma_platform_unregister
);
...
...
sound/soc/atmel/atmel_ssc_dai.c
View file @
04877397
...
...
@@ -73,6 +73,7 @@ static struct atmel_ssc_mask ssc_tx_mask = {
.
ssc_disable
=
SSC_BIT
(
CR_TXDIS
),
.
ssc_endx
=
SSC_BIT
(
SR_ENDTX
),
.
ssc_endbuf
=
SSC_BIT
(
SR_TXBUFE
),
.
ssc_error
=
SSC_BIT
(
SR_OVRUN
),
.
pdc_enable
=
ATMEL_PDC_TXTEN
,
.
pdc_disable
=
ATMEL_PDC_TXTDIS
,
};
...
...
@@ -82,6 +83,7 @@ static struct atmel_ssc_mask ssc_rx_mask = {
.
ssc_disable
=
SSC_BIT
(
CR_RXDIS
),
.
ssc_endx
=
SSC_BIT
(
SR_ENDRX
),
.
ssc_endbuf
=
SSC_BIT
(
SR_RXBUFF
),
.
ssc_error
=
SSC_BIT
(
SR_OVRUN
),
.
pdc_enable
=
ATMEL_PDC_RXTEN
,
.
pdc_disable
=
ATMEL_PDC_RXTDIS
,
};
...
...
@@ -196,15 +198,27 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct
snd_soc_dai
*
dai
)
{
struct
atmel_ssc_info
*
ssc_p
=
&
ssc_info
[
dai
->
id
];
int
dir_mask
;
struct
atmel_pcm_dma_params
*
dma_params
;
int
dir
,
dir_mask
;
pr_debug
(
"atmel_ssc_startup: SSC_SR=0x%u
\n
"
,
ssc_readl
(
ssc_p
->
ssc
->
regs
,
SR
));
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
{
dir
=
0
;
dir_mask
=
SSC_DIR_MASK_PLAYBACK
;
else
}
else
{
dir
=
1
;
dir_mask
=
SSC_DIR_MASK_CAPTURE
;
}
dma_params
=
&
ssc_dma_params
[
dai
->
id
][
dir
];
dma_params
->
ssc
=
ssc_p
->
ssc
;
dma_params
->
substream
=
substream
;
ssc_p
->
dma_params
[
dir
]
=
dma_params
;
snd_soc_dai_set_dma_data
(
dai
,
substream
,
dma_params
);
spin_lock_irq
(
&
ssc_p
->
lock
);
if
(
ssc_p
->
dir_mask
&
dir_mask
)
{
...
...
@@ -325,7 +339,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
snd_pcm_substream_chip
(
substream
);
int
id
=
dai
->
id
;
struct
atmel_ssc_info
*
ssc_p
=
&
ssc_info
[
id
];
struct
atmel_pcm_dma_params
*
dma_params
;
...
...
@@ -344,19 +357,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
else
dir
=
1
;
dma_params
=
&
ssc_dma_params
[
id
][
dir
];
dma_params
->
ssc
=
ssc_p
->
ssc
;
dma_params
->
substream
=
substream
;
ssc_p
->
dma_params
[
dir
]
=
dma_params
;
/*
* The snd_soc_pcm_stream->dma_data field is only used to communicate
* the appropriate DMA parameters to the pcm driver hw_params()
* function. It should not be used for other purposes
* as it is common to all substreams.
*/
snd_soc_dai_set_dma_data
(
rtd
->
cpu_dai
,
substream
,
dma_params
);
dma_params
=
ssc_p
->
dma_params
[
dir
];
channels
=
params_channels
(
params
);
...
...
@@ -648,6 +649,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
dma_params
=
ssc_p
->
dma_params
[
dir
];
ssc_writel
(
ssc_p
->
ssc
->
regs
,
CR
,
dma_params
->
mask
->
ssc_enable
);
ssc_writel
(
ssc_p
->
ssc
->
regs
,
IER
,
dma_params
->
mask
->
ssc_error
);
pr_debug
(
"%s enabled SSC_SR=0x%08x
\n
"
,
dir
?
"receive"
:
"transmit"
,
...
...
sound/soc/atmel/atmel_wm8904.c
0 → 100644
View file @
04877397
/*
* atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec.
*
* Copyright (C) 2012 Atmel
*
* Author: Bo Shen <voice.shen@atmel.com>
*
* GPLv2 or later
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <sound/soc.h>
#include "../codecs/wm8904.h"
#include "atmel_ssc_dai.h"
#define MCLK_RATE 32768
static
struct
clk
*
mclk
;
static
const
struct
snd_soc_dapm_widget
atmel_asoc_wm8904_dapm_widgets
[]
=
{
SND_SOC_DAPM_HP
(
"Headphone Jack"
,
NULL
),
SND_SOC_DAPM_MIC
(
"Mic"
,
NULL
),
SND_SOC_DAPM_LINE
(
"Line In Jack"
,
NULL
),
};
static
int
atmel_asoc_wm8904_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
snd_soc_dai
*
codec_dai
=
rtd
->
codec_dai
;
int
ret
;
ret
=
snd_soc_dai_set_pll
(
codec_dai
,
WM8904_FLL_MCLK
,
WM8904_FLL_MCLK
,
32768
,
params_rate
(
params
)
*
256
);
if
(
ret
<
0
)
{
pr_err
(
"%s - failed to set wm8904 codec PLL."
,
__func__
);
return
ret
;
}
/*
* As here wm8904 use FLL output as its system clock
* so calling set_sysclk won't care freq parameter
* then we pass 0
*/
ret
=
snd_soc_dai_set_sysclk
(
codec_dai
,
WM8904_CLK_FLL
,
0
,
SND_SOC_CLOCK_IN
);
if
(
ret
<
0
)
{
pr_err
(
"%s -failed to set wm8904 SYSCLK
\n
"
,
__func__
);
return
ret
;
}
return
0
;
}
static
struct
snd_soc_ops
atmel_asoc_wm8904_ops
=
{
.
hw_params
=
atmel_asoc_wm8904_hw_params
,
};
static
int
atmel_set_bias_level
(
struct
snd_soc_card
*
card
,
struct
snd_soc_dapm_context
*
dapm
,
enum
snd_soc_bias_level
level
)
{
if
(
dapm
->
bias_level
==
SND_SOC_BIAS_STANDBY
)
{
switch
(
level
)
{
case
SND_SOC_BIAS_PREPARE
:
clk_prepare_enable
(
mclk
);
break
;
case
SND_SOC_BIAS_OFF
:
clk_disable_unprepare
(
mclk
);
break
;
default:
break
;
}
}
return
0
;
};
static
struct
snd_soc_dai_link
atmel_asoc_wm8904_dailink
=
{
.
name
=
"WM8904"
,
.
stream_name
=
"WM8904 PCM"
,
.
codec_dai_name
=
"wm8904-hifi"
,
.
dai_fmt
=
SND_SOC_DAIFMT_I2S
|
SND_SOC_DAIFMT_NB_NF
|
SND_SOC_DAIFMT_CBM_CFM
,
.
ops
=
&
atmel_asoc_wm8904_ops
,
};
static
struct
snd_soc_card
atmel_asoc_wm8904_card
=
{
.
name
=
"atmel_asoc_wm8904"
,
.
owner
=
THIS_MODULE
,
.
set_bias_level
=
atmel_set_bias_level
,
.
dai_link
=
&
atmel_asoc_wm8904_dailink
,
.
num_links
=
1
,
.
dapm_widgets
=
atmel_asoc_wm8904_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
atmel_asoc_wm8904_dapm_widgets
),
.
fully_routed
=
true
,
};
static
int
atmel_asoc_wm8904_dt_init
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
struct
device_node
*
codec_np
,
*
cpu_np
;
struct
snd_soc_card
*
card
=
&
atmel_asoc_wm8904_card
;
struct
snd_soc_dai_link
*
dailink
=
&
atmel_asoc_wm8904_dailink
;
int
ret
;
if
(
!
np
)
{
dev_err
(
&
pdev
->
dev
,
"only device tree supported
\n
"
);
return
-
EINVAL
;
}
ret
=
snd_soc_of_parse_card_name
(
card
,
"atmel,model"
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"failed to parse card name
\n
"
);
return
ret
;
}
ret
=
snd_soc_of_parse_audio_routing
(
card
,
"atmel,audio-routing"
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"failed to parse audio routing
\n
"
);
return
ret
;
}
cpu_np
=
of_parse_phandle
(
np
,
"atmel,ssc-controller"
,
0
);
if
(
!
cpu_np
)
{
dev_err
(
&
pdev
->
dev
,
"failed to get dai and pcm info
\n
"
);
ret
=
-
EINVAL
;
return
ret
;
}
dailink
->
cpu_of_node
=
cpu_np
;
dailink
->
platform_of_node
=
cpu_np
;
of_node_put
(
cpu_np
);
codec_np
=
of_parse_phandle
(
np
,
"atmel,audio-codec"
,
0
);
if
(
!
codec_np
)
{
dev_err
(
&
pdev
->
dev
,
"failed to get codec info
\n
"
);
ret
=
-
EINVAL
;
return
ret
;
}
dailink
->
codec_of_node
=
codec_np
;
of_node_put
(
codec_np
);
return
0
;
}
static
int
atmel_asoc_wm8904_probe
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_card
*
card
=
&
atmel_asoc_wm8904_card
;
struct
snd_soc_dai_link
*
dailink
=
&
atmel_asoc_wm8904_dailink
;
struct
clk
*
clk_src
;
struct
pinctrl
*
pinctrl
;
int
id
,
ret
;
pinctrl
=
devm_pinctrl_get_select_default
(
&
pdev
->
dev
);
if
(
IS_ERR
(
pinctrl
))
{
dev_err
(
&
pdev
->
dev
,
"failed to request pinctrl
\n
"
);
return
PTR_ERR
(
pinctrl
);
}
card
->
dev
=
&
pdev
->
dev
;
ret
=
atmel_asoc_wm8904_dt_init
(
pdev
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"failed to init dt info
\n
"
);
return
ret
;
}
id
=
of_alias_get_id
((
struct
device_node
*
)
dailink
->
cpu_of_node
,
"ssc"
);
ret
=
atmel_ssc_set_audio
(
id
);
if
(
ret
!=
0
)
{
dev_err
(
&
pdev
->
dev
,
"failed to set SSC %d for audio
\n
"
,
id
);
return
ret
;
}
mclk
=
clk_get
(
NULL
,
"pck0"
);
if
(
IS_ERR
(
mclk
))
{
dev_err
(
&
pdev
->
dev
,
"failed to get pck0
\n
"
);
ret
=
PTR_ERR
(
mclk
);
goto
err_set_audio
;
}
clk_src
=
clk_get
(
NULL
,
"clk32k"
);
if
(
IS_ERR
(
clk_src
))
{
dev_err
(
&
pdev
->
dev
,
"failed to get clk32k
\n
"
);
ret
=
PTR_ERR
(
clk_src
);
goto
err_set_audio
;
}
ret
=
clk_set_parent
(
mclk
,
clk_src
);
clk_put
(
clk_src
);
if
(
ret
!=
0
)
{
dev_err
(
&
pdev
->
dev
,
"failed to set MCLK parent
\n
"
);
goto
err_set_audio
;
}
dev_info
(
&
pdev
->
dev
,
"setting pck0 to %dHz
\n
"
,
MCLK_RATE
);
clk_set_rate
(
mclk
,
MCLK_RATE
);
ret
=
snd_soc_register_card
(
card
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"snd_soc_register_card failed
\n
"
);
goto
err_set_audio
;
}
return
0
;
err_set_audio:
atmel_ssc_put_audio
(
id
);
return
ret
;
}
static
int
atmel_asoc_wm8904_remove
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_card
*
card
=
platform_get_drvdata
(
pdev
);
struct
snd_soc_dai_link
*
dailink
=
&
atmel_asoc_wm8904_dailink
;
int
id
;
id
=
of_alias_get_id
((
struct
device_node
*
)
dailink
->
cpu_of_node
,
"ssc"
);
snd_soc_unregister_card
(
card
);
atmel_ssc_put_audio
(
id
);
return
0
;
}
#ifdef CONFIG_OF
static
const
struct
of_device_id
atmel_asoc_wm8904_dt_ids
[]
=
{
{
.
compatible
=
"atmel,asoc-wm8904"
,
},
{
}
};
#endif
static
struct
platform_driver
atmel_asoc_wm8904_driver
=
{
.
driver
=
{
.
name
=
"atmel-wm8904-audio"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
of_match_ptr
(
atmel_asoc_wm8904_dt_ids
),
},
.
probe
=
atmel_asoc_wm8904_probe
,
.
remove
=
atmel_asoc_wm8904_remove
,
};
module_platform_driver
(
atmel_asoc_wm8904_driver
);
/* Module information */
MODULE_AUTHOR
(
"Bo Shen <voice.shen@atmel.com>"
);
MODULE_DESCRIPTION
(
"ALSA SoC machine driver for Atmel EK with WM8904 codec"
);
MODULE_LICENSE
(
"GPL"
);
sound/soc/atmel/sam9x5_wm8731.c
0 → 100644
View file @
04877397
/*
* sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards
* that are using WM8731 as codec.
*
* Copyright (C) 2011 Atmel,
* Nicolas Ferre <nicolas.ferre@atmel.com>
*
* Copyright (C) 2013 Paratronic,
* Richard Genoud <richard.genoud@gmail.com>
*
* Based on sam9g20_wm8731.c by:
* Sedji Gaouaou <sedji.gaouaou@atmel.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/of.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/soc-dapm.h>
#include "../codecs/wm8731.h"
#include "atmel_ssc_dai.h"
#define MCLK_RATE 12288000
#define DRV_NAME "sam9x5-snd-wm8731"
struct
sam9x5_drvdata
{
int
ssc_id
;
};
/*
* Logic for a wm8731 as connected on a at91sam9x5ek based board.
*/
static
int
sam9x5_wm8731_init
(
struct
snd_soc_pcm_runtime
*
rtd
)
{
struct
snd_soc_dai
*
codec_dai
=
rtd
->
codec_dai
;
struct
device
*
dev
=
rtd
->
dev
;
int
ret
;
dev_dbg
(
dev
,
"ASoC: %s called
\n
"
,
__func__
);
/* set the codec system clock for DAC and ADC */
ret
=
snd_soc_dai_set_sysclk
(
codec_dai
,
WM8731_SYSCLK_XTAL
,
MCLK_RATE
,
SND_SOC_CLOCK_IN
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"ASoC: Failed to set WM8731 SYSCLK: %d
\n
"
,
ret
);
return
ret
;
}
return
0
;
}
/*
* Audio paths on at91sam9x5ek board:
*
* |A| ------------> | | ---R----> Headphone Jack
* |T| <----\ | WM | ---L--/
* |9| ---> CLK <--> | 8731 | <--R----- Line In Jack
* |1| <------------ | | <--L--/
*/
static
const
struct
snd_soc_dapm_widget
sam9x5_dapm_widgets
[]
=
{
SND_SOC_DAPM_HP
(
"Headphone Jack"
,
NULL
),
SND_SOC_DAPM_LINE
(
"Line In Jack"
,
NULL
),
};
static
int
sam9x5_wm8731_driver_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
struct
device_node
*
codec_np
,
*
cpu_np
;
struct
snd_soc_card
*
card
;
struct
snd_soc_dai_link
*
dai
;
struct
sam9x5_drvdata
*
priv
;
int
ret
;
if
(
!
np
)
{
dev_err
(
&
pdev
->
dev
,
"No device node supplied
\n
"
);
return
-
EINVAL
;
}
card
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
card
),
GFP_KERNEL
);
priv
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
priv
),
GFP_KERNEL
);
dai
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
dai
),
GFP_KERNEL
);
if
(
!
dai
||
!
card
||
!
priv
)
{
ret
=
-
ENOMEM
;
goto
out
;
}
card
->
dev
=
&
pdev
->
dev
;
card
->
owner
=
THIS_MODULE
;
card
->
dai_link
=
dai
;
card
->
num_links
=
1
;
card
->
dapm_widgets
=
sam9x5_dapm_widgets
;
card
->
num_dapm_widgets
=
ARRAY_SIZE
(
sam9x5_dapm_widgets
);
dai
->
name
=
"WM8731"
;
dai
->
stream_name
=
"WM8731 PCM"
;
dai
->
codec_dai_name
=
"wm8731-hifi"
;
dai
->
init
=
sam9x5_wm8731_init
;
dai
->
dai_fmt
=
SND_SOC_DAIFMT_I2S
|
SND_SOC_DAIFMT_NB_NF
|
SND_SOC_DAIFMT_CBM_CFM
;
ret
=
snd_soc_of_parse_card_name
(
card
,
"atmel,model"
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"atmel,model node missing
\n
"
);
goto
out
;
}
ret
=
snd_soc_of_parse_audio_routing
(
card
,
"atmel,audio-routing"
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"atmel,audio-routing node missing
\n
"
);
goto
out
;
}
codec_np
=
of_parse_phandle
(
np
,
"atmel,audio-codec"
,
0
);
if
(
!
codec_np
)
{
dev_err
(
&
pdev
->
dev
,
"atmel,audio-codec node missing
\n
"
);
ret
=
-
EINVAL
;
goto
out
;
}
dai
->
codec_of_node
=
codec_np
;
cpu_np
=
of_parse_phandle
(
np
,
"atmel,ssc-controller"
,
0
);
if
(
!
cpu_np
)
{
dev_err
(
&
pdev
->
dev
,
"atmel,ssc-controller node missing
\n
"
);
ret
=
-
EINVAL
;
goto
out
;
}
dai
->
cpu_of_node
=
cpu_np
;
dai
->
platform_of_node
=
cpu_np
;
priv
->
ssc_id
=
of_alias_get_id
(
cpu_np
,
"ssc"
);
ret
=
atmel_ssc_set_audio
(
priv
->
ssc_id
);
if
(
ret
!=
0
)
{
dev_err
(
&
pdev
->
dev
,
"ASoC: Failed to set SSC %d for audio: %d
\n
"
,
ret
,
priv
->
ssc_id
);
goto
out
;
}
of_node_put
(
codec_np
);
of_node_put
(
cpu_np
);
platform_set_drvdata
(
pdev
,
card
);
ret
=
snd_soc_register_card
(
card
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"ASoC: Platform device allocation failed
\n
"
);
goto
out_put_audio
;
}
dev_dbg
(
&
pdev
->
dev
,
"ASoC: %s ok
\n
"
,
__func__
);
return
ret
;
out_put_audio:
atmel_ssc_put_audio
(
priv
->
ssc_id
);
out:
return
ret
;
}
static
int
sam9x5_wm8731_driver_remove
(
struct
platform_device
*
pdev
)
{
struct
snd_soc_card
*
card
=
platform_get_drvdata
(
pdev
);
struct
sam9x5_drvdata
*
priv
=
card
->
drvdata
;
snd_soc_unregister_card
(
card
);
atmel_ssc_put_audio
(
priv
->
ssc_id
);
return
0
;
}
static
const
struct
of_device_id
sam9x5_wm8731_of_match
[]
=
{
{
.
compatible
=
"atmel,sam9x5-wm8731-audio"
,
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
sam9x5_wm8731_of_match
);
static
struct
platform_driver
sam9x5_wm8731_driver
=
{
.
driver
=
{
.
name
=
DRV_NAME
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
of_match_ptr
(
sam9x5_wm8731_of_match
),
},
.
probe
=
sam9x5_wm8731_driver_probe
,
.
remove
=
sam9x5_wm8731_driver_remove
,
};
module_platform_driver
(
sam9x5_wm8731_driver
);
/* Module information */
MODULE_AUTHOR
(
"Nicolas Ferre <nicolas.ferre@atmel.com>"
);
MODULE_AUTHOR
(
"Richard Genoud <richard.genoud@gmail.com>"
);
MODULE_DESCRIPTION
(
"ALSA SoC machine driver for AT91SAM9x5 - WM8731"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_ALIAS
(
"platform:"
DRV_NAME
);
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