Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
cloud-kernel
提交
04877397
cloud-kernel
项目概览
openanolis
/
cloud-kernel
1 年多 前同步成功
通知
160
Star
36
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
cloud-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
04877397
编写于
8月 22, 2013
作者:
M
Mark Brown
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'asoc/topic/atmel' into asoc-next
上级
0b1107b2
f813175a
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
644 addition
and
121 deletion
+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
未找到文件。
Documentation/devicetree/bindings/misc/atmel-ssc.txt
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
浏览文件 @
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
);
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录