Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
a96801b5
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
a96801b5
编写于
5月 17, 2017
作者:
T
Takashi Iwai
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'topic/avr32-removal' into for-next
上级
29b59b35
14bebd01
变更
8
展开全部
隐藏空白更改
内联
并排
Showing
8 changed file
with
88 addition
and
1394 deletion
+88
-1394
drivers/dma/dw/Kconfig
drivers/dma/dw/Kconfig
+1
-6
drivers/dma/dw/core.c
drivers/dma/dw/core.c
+2
-330
drivers/dma/dw/regs.h
drivers/dma/dw/regs.h
+11
-39
include/linux/dma/dw.h
include/linux/dma/dw.h
+0
-21
sound/atmel/Kconfig
sound/atmel/Kconfig
+3
-10
sound/atmel/Makefile
sound/atmel/Makefile
+0
-2
sound/atmel/abdac.c
sound/atmel/abdac.c
+0
-610
sound/atmel/ac97c.c
sound/atmel/ac97c.c
+71
-376
未找到文件。
drivers/dma/dw/Kconfig
浏览文件 @
a96801b5
...
...
@@ -6,17 +6,12 @@ config DW_DMAC_CORE
tristate
select DMA_ENGINE
config DW_DMAC_BIG_ENDIAN_IO
bool
config DW_DMAC
tristate "Synopsys DesignWare AHB DMA platform driver"
select DW_DMAC_CORE
select DW_DMAC_BIG_ENDIAN_IO if AVR32
default y if CPU_AT32AP7000
help
Support the Synopsys DesignWare AHB DMA controller. This
can be integrated in chips such as the
Atmel AT32ap7000
.
can be integrated in chips such as the
Intel Cherrytrail
.
config DW_DMAC_PCI
tristate "Synopsys DesignWare AHB DMA PCI driver"
...
...
drivers/dma/dw/core.c
浏览文件 @
a96801b5
...
...
@@ -561,92 +561,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc)
dwc_descriptor_complete
(
dwc
,
bad_desc
,
true
);
}
/* --------------------- Cyclic DMA API extensions -------------------- */
dma_addr_t
dw_dma_get_src_addr
(
struct
dma_chan
*
chan
)
{
struct
dw_dma_chan
*
dwc
=
to_dw_dma_chan
(
chan
);
return
channel_readl
(
dwc
,
SAR
);
}
EXPORT_SYMBOL
(
dw_dma_get_src_addr
);
dma_addr_t
dw_dma_get_dst_addr
(
struct
dma_chan
*
chan
)
{
struct
dw_dma_chan
*
dwc
=
to_dw_dma_chan
(
chan
);
return
channel_readl
(
dwc
,
DAR
);
}
EXPORT_SYMBOL
(
dw_dma_get_dst_addr
);
/* Called with dwc->lock held and all DMAC interrupts disabled */
static
void
dwc_handle_cyclic
(
struct
dw_dma
*
dw
,
struct
dw_dma_chan
*
dwc
,
u32
status_block
,
u32
status_err
,
u32
status_xfer
)
{
unsigned
long
flags
;
if
(
status_block
&
dwc
->
mask
)
{
void
(
*
callback
)(
void
*
param
);
void
*
callback_param
;
dev_vdbg
(
chan2dev
(
&
dwc
->
chan
),
"new cyclic period llp 0x%08x
\n
"
,
channel_readl
(
dwc
,
LLP
));
dma_writel
(
dw
,
CLEAR
.
BLOCK
,
dwc
->
mask
);
callback
=
dwc
->
cdesc
->
period_callback
;
callback_param
=
dwc
->
cdesc
->
period_callback_param
;
if
(
callback
)
callback
(
callback_param
);
}
/*
* Error and transfer complete are highly unlikely, and will most
* likely be due to a configuration error by the user.
*/
if
(
unlikely
(
status_err
&
dwc
->
mask
)
||
unlikely
(
status_xfer
&
dwc
->
mask
))
{
unsigned
int
i
;
dev_err
(
chan2dev
(
&
dwc
->
chan
),
"cyclic DMA unexpected %s interrupt, stopping DMA transfer
\n
"
,
status_xfer
?
"xfer"
:
"error"
);
spin_lock_irqsave
(
&
dwc
->
lock
,
flags
);
dwc_dump_chan_regs
(
dwc
);
dwc_chan_disable
(
dw
,
dwc
);
/* Make sure DMA does not restart by loading a new list */
channel_writel
(
dwc
,
LLP
,
0
);
channel_writel
(
dwc
,
CTL_LO
,
0
);
channel_writel
(
dwc
,
CTL_HI
,
0
);
dma_writel
(
dw
,
CLEAR
.
BLOCK
,
dwc
->
mask
);
dma_writel
(
dw
,
CLEAR
.
ERROR
,
dwc
->
mask
);
dma_writel
(
dw
,
CLEAR
.
XFER
,
dwc
->
mask
);
for
(
i
=
0
;
i
<
dwc
->
cdesc
->
periods
;
i
++
)
dwc_dump_lli
(
dwc
,
dwc
->
cdesc
->
desc
[
i
]);
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
}
/* Re-enable interrupts */
channel_set_bit
(
dw
,
MASK
.
BLOCK
,
dwc
->
mask
);
}
/* ------------------------------------------------------------------------- */
static
void
dw_dma_tasklet
(
unsigned
long
data
)
{
struct
dw_dma
*
dw
=
(
struct
dw_dma
*
)
data
;
struct
dw_dma_chan
*
dwc
;
u32
status_block
;
u32
status_xfer
;
u32
status_err
;
unsigned
int
i
;
status_block
=
dma_readl
(
dw
,
RAW
.
BLOCK
);
status_xfer
=
dma_readl
(
dw
,
RAW
.
XFER
);
status_err
=
dma_readl
(
dw
,
RAW
.
ERROR
);
...
...
@@ -655,8 +577,7 @@ static void dw_dma_tasklet(unsigned long data)
for
(
i
=
0
;
i
<
dw
->
dma
.
chancnt
;
i
++
)
{
dwc
=
&
dw
->
chan
[
i
];
if
(
test_bit
(
DW_DMA_IS_CYCLIC
,
&
dwc
->
flags
))
dwc_handle_cyclic
(
dw
,
dwc
,
status_block
,
status_err
,
status_xfer
);
dev_vdbg
(
dw
->
dma
.
dev
,
"Cyclic xfer is not implemented
\n
"
);
else
if
(
status_err
&
(
1
<<
i
))
dwc_handle_error
(
dw
,
dwc
);
else
if
(
status_xfer
&
(
1
<<
i
))
...
...
@@ -1264,255 +1185,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
dev_vdbg
(
chan2dev
(
chan
),
"%s: done
\n
"
,
__func__
);
}
/* --------------------- Cyclic DMA API extensions -------------------- */
/**
* dw_dma_cyclic_start - start the cyclic DMA transfer
* @chan: the DMA channel to start
*
* Must be called with soft interrupts disabled. Returns zero on success or
* -errno on failure.
*/
int
dw_dma_cyclic_start
(
struct
dma_chan
*
chan
)
{
struct
dw_dma_chan
*
dwc
=
to_dw_dma_chan
(
chan
);
struct
dw_dma
*
dw
=
to_dw_dma
(
chan
->
device
);
unsigned
long
flags
;
if
(
!
test_bit
(
DW_DMA_IS_CYCLIC
,
&
dwc
->
flags
))
{
dev_err
(
chan2dev
(
&
dwc
->
chan
),
"missing prep for cyclic DMA
\n
"
);
return
-
ENODEV
;
}
spin_lock_irqsave
(
&
dwc
->
lock
,
flags
);
/* Enable interrupts to perform cyclic transfer */
channel_set_bit
(
dw
,
MASK
.
BLOCK
,
dwc
->
mask
);
dwc_dostart
(
dwc
,
dwc
->
cdesc
->
desc
[
0
]);
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
return
0
;
}
EXPORT_SYMBOL
(
dw_dma_cyclic_start
);
/**
* dw_dma_cyclic_stop - stop the cyclic DMA transfer
* @chan: the DMA channel to stop
*
* Must be called with soft interrupts disabled.
*/
void
dw_dma_cyclic_stop
(
struct
dma_chan
*
chan
)
{
struct
dw_dma_chan
*
dwc
=
to_dw_dma_chan
(
chan
);
struct
dw_dma
*
dw
=
to_dw_dma
(
dwc
->
chan
.
device
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
dwc
->
lock
,
flags
);
dwc_chan_disable
(
dw
,
dwc
);
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
}
EXPORT_SYMBOL
(
dw_dma_cyclic_stop
);
/**
* dw_dma_cyclic_prep - prepare the cyclic DMA transfer
* @chan: the DMA channel to prepare
* @buf_addr: physical DMA address where the buffer starts
* @buf_len: total number of bytes for the entire buffer
* @period_len: number of bytes for each period
* @direction: transfer direction, to or from device
*
* Must be called before trying to start the transfer. Returns a valid struct
* dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful.
*/
struct
dw_cyclic_desc
*
dw_dma_cyclic_prep
(
struct
dma_chan
*
chan
,
dma_addr_t
buf_addr
,
size_t
buf_len
,
size_t
period_len
,
enum
dma_transfer_direction
direction
)
{
struct
dw_dma_chan
*
dwc
=
to_dw_dma_chan
(
chan
);
struct
dma_slave_config
*
sconfig
=
&
dwc
->
dma_sconfig
;
struct
dw_cyclic_desc
*
cdesc
;
struct
dw_cyclic_desc
*
retval
=
NULL
;
struct
dw_desc
*
desc
;
struct
dw_desc
*
last
=
NULL
;
u8
lms
=
DWC_LLP_LMS
(
dwc
->
dws
.
m_master
);
unsigned
long
was_cyclic
;
unsigned
int
reg_width
;
unsigned
int
periods
;
unsigned
int
i
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
dwc
->
lock
,
flags
);
if
(
dwc
->
nollp
)
{
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
dev_dbg
(
chan2dev
(
&
dwc
->
chan
),
"channel doesn't support LLP transfers
\n
"
);
return
ERR_PTR
(
-
EINVAL
);
}
if
(
!
list_empty
(
&
dwc
->
queue
)
||
!
list_empty
(
&
dwc
->
active_list
))
{
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
dev_dbg
(
chan2dev
(
&
dwc
->
chan
),
"queue and/or active list are not empty
\n
"
);
return
ERR_PTR
(
-
EBUSY
);
}
was_cyclic
=
test_and_set_bit
(
DW_DMA_IS_CYCLIC
,
&
dwc
->
flags
);
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
if
(
was_cyclic
)
{
dev_dbg
(
chan2dev
(
&
dwc
->
chan
),
"channel already prepared for cyclic DMA
\n
"
);
return
ERR_PTR
(
-
EBUSY
);
}
retval
=
ERR_PTR
(
-
EINVAL
);
if
(
unlikely
(
!
is_slave_direction
(
direction
)))
goto
out_err
;
dwc
->
direction
=
direction
;
if
(
direction
==
DMA_MEM_TO_DEV
)
reg_width
=
__ffs
(
sconfig
->
dst_addr_width
);
else
reg_width
=
__ffs
(
sconfig
->
src_addr_width
);
periods
=
buf_len
/
period_len
;
/* Check for too big/unaligned periods and unaligned DMA buffer. */
if
(
period_len
>
(
dwc
->
block_size
<<
reg_width
))
goto
out_err
;
if
(
unlikely
(
period_len
&
((
1
<<
reg_width
)
-
1
)))
goto
out_err
;
if
(
unlikely
(
buf_addr
&
((
1
<<
reg_width
)
-
1
)))
goto
out_err
;
retval
=
ERR_PTR
(
-
ENOMEM
);
cdesc
=
kzalloc
(
sizeof
(
struct
dw_cyclic_desc
),
GFP_KERNEL
);
if
(
!
cdesc
)
goto
out_err
;
cdesc
->
desc
=
kzalloc
(
sizeof
(
struct
dw_desc
*
)
*
periods
,
GFP_KERNEL
);
if
(
!
cdesc
->
desc
)
goto
out_err_alloc
;
for
(
i
=
0
;
i
<
periods
;
i
++
)
{
desc
=
dwc_desc_get
(
dwc
);
if
(
!
desc
)
goto
out_err_desc_get
;
switch
(
direction
)
{
case
DMA_MEM_TO_DEV
:
lli_write
(
desc
,
dar
,
sconfig
->
dst_addr
);
lli_write
(
desc
,
sar
,
buf_addr
+
period_len
*
i
);
lli_write
(
desc
,
ctllo
,
(
DWC_DEFAULT_CTLLO
(
chan
)
|
DWC_CTLL_DST_WIDTH
(
reg_width
)
|
DWC_CTLL_SRC_WIDTH
(
reg_width
)
|
DWC_CTLL_DST_FIX
|
DWC_CTLL_SRC_INC
|
DWC_CTLL_INT_EN
));
lli_set
(
desc
,
ctllo
,
sconfig
->
device_fc
?
DWC_CTLL_FC
(
DW_DMA_FC_P_M2P
)
:
DWC_CTLL_FC
(
DW_DMA_FC_D_M2P
));
break
;
case
DMA_DEV_TO_MEM
:
lli_write
(
desc
,
dar
,
buf_addr
+
period_len
*
i
);
lli_write
(
desc
,
sar
,
sconfig
->
src_addr
);
lli_write
(
desc
,
ctllo
,
(
DWC_DEFAULT_CTLLO
(
chan
)
|
DWC_CTLL_SRC_WIDTH
(
reg_width
)
|
DWC_CTLL_DST_WIDTH
(
reg_width
)
|
DWC_CTLL_DST_INC
|
DWC_CTLL_SRC_FIX
|
DWC_CTLL_INT_EN
));
lli_set
(
desc
,
ctllo
,
sconfig
->
device_fc
?
DWC_CTLL_FC
(
DW_DMA_FC_P_P2M
)
:
DWC_CTLL_FC
(
DW_DMA_FC_D_P2M
));
break
;
default:
break
;
}
lli_write
(
desc
,
ctlhi
,
period_len
>>
reg_width
);
cdesc
->
desc
[
i
]
=
desc
;
if
(
last
)
lli_write
(
last
,
llp
,
desc
->
txd
.
phys
|
lms
);
last
=
desc
;
}
/* Let's make a cyclic list */
lli_write
(
last
,
llp
,
cdesc
->
desc
[
0
]
->
txd
.
phys
|
lms
);
dev_dbg
(
chan2dev
(
&
dwc
->
chan
),
"cyclic prepared buf %pad len %zu period %zu periods %d
\n
"
,
&
buf_addr
,
buf_len
,
period_len
,
periods
);
cdesc
->
periods
=
periods
;
dwc
->
cdesc
=
cdesc
;
return
cdesc
;
out_err_desc_get:
while
(
i
--
)
dwc_desc_put
(
dwc
,
cdesc
->
desc
[
i
]);
out_err_alloc:
kfree
(
cdesc
);
out_err:
clear_bit
(
DW_DMA_IS_CYCLIC
,
&
dwc
->
flags
);
return
(
struct
dw_cyclic_desc
*
)
retval
;
}
EXPORT_SYMBOL
(
dw_dma_cyclic_prep
);
/**
* dw_dma_cyclic_free - free a prepared cyclic DMA transfer
* @chan: the DMA channel to free
*/
void
dw_dma_cyclic_free
(
struct
dma_chan
*
chan
)
{
struct
dw_dma_chan
*
dwc
=
to_dw_dma_chan
(
chan
);
struct
dw_dma
*
dw
=
to_dw_dma
(
dwc
->
chan
.
device
);
struct
dw_cyclic_desc
*
cdesc
=
dwc
->
cdesc
;
unsigned
int
i
;
unsigned
long
flags
;
dev_dbg
(
chan2dev
(
&
dwc
->
chan
),
"%s
\n
"
,
__func__
);
if
(
!
cdesc
)
return
;
spin_lock_irqsave
(
&
dwc
->
lock
,
flags
);
dwc_chan_disable
(
dw
,
dwc
);
dma_writel
(
dw
,
CLEAR
.
BLOCK
,
dwc
->
mask
);
dma_writel
(
dw
,
CLEAR
.
ERROR
,
dwc
->
mask
);
dma_writel
(
dw
,
CLEAR
.
XFER
,
dwc
->
mask
);
spin_unlock_irqrestore
(
&
dwc
->
lock
,
flags
);
for
(
i
=
0
;
i
<
cdesc
->
periods
;
i
++
)
dwc_desc_put
(
dwc
,
cdesc
->
desc
[
i
]);
kfree
(
cdesc
->
desc
);
kfree
(
cdesc
);
dwc
->
cdesc
=
NULL
;
clear_bit
(
DW_DMA_IS_CYCLIC
,
&
dwc
->
flags
);
}
EXPORT_SYMBOL
(
dw_dma_cyclic_free
);
/*----------------------------------------------------------------------*/
int
dw_dma_probe
(
struct
dw_dma_chip
*
chip
)
{
struct
dw_dma_platform_data
*
pdata
;
...
...
@@ -1642,7 +1314,7 @@ int dw_dma_probe(struct dw_dma_chip *chip)
if
(
autocfg
)
{
unsigned
int
r
=
DW_DMA_MAX_NR_CHANNELS
-
i
-
1
;
void
__iomem
*
addr
=
&
__dw_regs
(
dw
)
->
DWC_PARAMS
[
r
];
unsigned
int
dwc_params
=
dma_readl_native
(
addr
);
unsigned
int
dwc_params
=
readl
(
addr
);
dev_dbg
(
chip
->
dev
,
"DWC_PARAMS[%d]: 0x%08x
\n
"
,
i
,
dwc_params
);
...
...
drivers/dma/dw/regs.h
浏览文件 @
a96801b5
...
...
@@ -116,20 +116,6 @@ struct dw_dma_regs {
DW_REG
(
GLOBAL_CFG
);
};
/*
* Big endian I/O access when reading and writing to the DMA controller
* registers. This is needed on some platforms, like the Atmel AVR32
* architecture.
*/
#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
#define dma_readl_native ioread32be
#define dma_writel_native iowrite32be
#else
#define dma_readl_native readl
#define dma_writel_native writel
#endif
/* Bitfields in DW_PARAMS */
#define DW_PARAMS_NR_CHAN 8
/* number of channels */
#define DW_PARAMS_NR_MASTER 11
/* number of AHB masters */
...
...
@@ -280,7 +266,6 @@ struct dw_dma_chan {
unsigned
long
flags
;
struct
list_head
active_list
;
struct
list_head
queue
;
struct
dw_cyclic_desc
*
cdesc
;
unsigned
int
descs_allocated
;
...
...
@@ -302,9 +287,9 @@ __dwc_regs(struct dw_dma_chan *dwc)
}
#define channel_readl(dwc, name) \
dma_readl_native
(&(__dwc_regs(dwc)->name))
readl
(&(__dwc_regs(dwc)->name))
#define channel_writel(dwc, name, val) \
dma_writel_native
((val), &(__dwc_regs(dwc)->name))
writel
((val), &(__dwc_regs(dwc)->name))
static
inline
struct
dw_dma_chan
*
to_dw_dma_chan
(
struct
dma_chan
*
chan
)
{
...
...
@@ -333,9 +318,9 @@ static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw)
}
#define dma_readl(dw, name) \
dma_readl_native
(&(__dw_regs(dw)->name))
readl
(&(__dw_regs(dw)->name))
#define dma_writel(dw, name, val) \
dma_writel_native
((val), &(__dw_regs(dw)->name))
writel
((val), &(__dw_regs(dw)->name))
#define idma32_readq(dw, name) \
hi_lo_readq(&(__dw_regs(dw)->name))
...
...
@@ -352,43 +337,30 @@ static inline struct dw_dma *to_dw_dma(struct dma_device *ddev)
return
container_of
(
ddev
,
struct
dw_dma
,
dma
);
}
#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
typedef
__be32
__dw32
;
#else
typedef
__le32
__dw32
;
#endif
/* LLI == Linked List Item; a.k.a. DMA block descriptor */
struct
dw_lli
{
/* values that are not changed by hardware */
__
dw
32
sar
;
__
dw
32
dar
;
__
dw
32
llp
;
/* chain to next lli */
__
dw
32
ctllo
;
__
le
32
sar
;
__
le
32
dar
;
__
le
32
llp
;
/* chain to next lli */
__
le
32
ctllo
;
/* values that may get written back: */
__
dw
32
ctlhi
;
__
le
32
ctlhi
;
/* sstat and dstat can snapshot peripheral register state.
* silicon config may discard either or both...
*/
__
dw
32
sstat
;
__
dw
32
dstat
;
__
le
32
sstat
;
__
le
32
dstat
;
};
struct
dw_desc
{
/* FIRST values the hardware uses */
struct
dw_lli
lli
;
#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO
#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_be32(v))
#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_be32(v))
#define lli_read(d, reg) be32_to_cpu((d)->lli.reg)
#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_be32(v))
#else
#define lli_set(d, reg, v) ((d)->lli.reg |= cpu_to_le32(v))
#define lli_clear(d, reg, v) ((d)->lli.reg &= ~cpu_to_le32(v))
#define lli_read(d, reg) le32_to_cpu((d)->lli.reg)
#define lli_write(d, reg, v) ((d)->lli.reg = cpu_to_le32(v))
#endif
/* THEN values for driver housekeeping */
struct
list_head
desc_node
;
...
...
include/linux/dma/dw.h
浏览文件 @
a96801b5
...
...
@@ -50,25 +50,4 @@ static inline int dw_dma_probe(struct dw_dma_chip *chip) { return -ENODEV; }
static
inline
int
dw_dma_remove
(
struct
dw_dma_chip
*
chip
)
{
return
0
;
}
#endif
/* CONFIG_DW_DMAC_CORE */
/* DMA API extensions */
struct
dw_desc
;
struct
dw_cyclic_desc
{
struct
dw_desc
**
desc
;
unsigned
long
periods
;
void
(
*
period_callback
)(
void
*
param
);
void
*
period_callback_param
;
};
struct
dw_cyclic_desc
*
dw_dma_cyclic_prep
(
struct
dma_chan
*
chan
,
dma_addr_t
buf_addr
,
size_t
buf_len
,
size_t
period_len
,
enum
dma_transfer_direction
direction
);
void
dw_dma_cyclic_free
(
struct
dma_chan
*
chan
);
int
dw_dma_cyclic_start
(
struct
dma_chan
*
chan
);
void
dw_dma_cyclic_stop
(
struct
dma_chan
*
chan
);
dma_addr_t
dw_dma_get_src_addr
(
struct
dma_chan
*
chan
);
dma_addr_t
dw_dma_get_dst_addr
(
struct
dma_chan
*
chan
);
#endif
/* _DMA_DW_H */
sound/atmel/Kconfig
浏览文件 @
a96801b5
menu "Atmel devices (AVR32 and AT91)"
depends on AVR32 || ARCH_AT91
config SND_ATMEL_ABDAC
tristate "Atmel Audio Bitstream DAC (ABDAC) driver"
select SND_PCM
depends on DW_DMAC && AVR32
help
ALSA sound driver for the Atmel Audio Bitstream DAC (ABDAC).
menu "Atmel devices (AT91)"
depends on ARCH_AT91
config SND_ATMEL_AC97C
tristate "Atmel AC97 Controller (AC97C) driver"
select SND_PCM
select SND_AC97_CODEC
depends on
(DW_DMAC && AVR32) ||
ARCH_AT91
depends on ARCH_AT91
help
ALSA sound driver for the Atmel AC97 controller.
...
...
sound/atmel/Makefile
浏览文件 @
a96801b5
snd-atmel-abdac-objs
:=
abdac.o
snd-atmel-ac97c-objs
:=
ac97c.o
obj-$(CONFIG_SND_ATMEL_ABDAC)
+=
snd-atmel-abdac.o
obj-$(CONFIG_SND_ATMEL_AC97C)
+=
snd-atmel-ac97c.o
sound/atmel/abdac.c
已删除
100644 → 0
浏览文件 @
29b59b35
/*
* Driver for the Atmel on-chip Audio Bitstream DAC (ABDAC)
*
* Copyright (C) 2006-2009 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/bitmap.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/atmel-abdac.h>
#include <linux/platform_data/dma-dw.h>
#include <linux/dma/dw.h>
/* DAC register offsets */
#define DAC_DATA 0x0000
#define DAC_CTRL 0x0008
#define DAC_INT_MASK 0x000c
#define DAC_INT_EN 0x0010
#define DAC_INT_DIS 0x0014
#define DAC_INT_CLR 0x0018
#define DAC_INT_STATUS 0x001c
/* Bitfields in CTRL */
#define DAC_SWAP_OFFSET 30
#define DAC_SWAP_SIZE 1
#define DAC_EN_OFFSET 31
#define DAC_EN_SIZE 1
/* Bitfields in INT_MASK/INT_EN/INT_DIS/INT_STATUS/INT_CLR */
#define DAC_UNDERRUN_OFFSET 28
#define DAC_UNDERRUN_SIZE 1
#define DAC_TX_READY_OFFSET 29
#define DAC_TX_READY_SIZE 1
/* Bit manipulation macros */
#define DAC_BIT(name) \
(1 << DAC_##name##_OFFSET)
#define DAC_BF(name, value) \
(((value) & ((1 << DAC_##name##_SIZE) - 1)) \
<< DAC_##name##_OFFSET)
#define DAC_BFEXT(name, value) \
(((value) >> DAC_##name##_OFFSET) \
& ((1 << DAC_##name##_SIZE) - 1))
#define DAC_BFINS(name, value, old) \
(((old) & ~(((1 << DAC_##name##_SIZE) - 1) \
<< DAC_##name##_OFFSET)) \
| DAC_BF(name, value))
/* Register access macros */
#define dac_readl(port, reg) \
__raw_readl((port)->regs + DAC_##reg)
#define dac_writel(port, reg, value) \
__raw_writel((value), (port)->regs + DAC_##reg)
/*
* ABDAC supports a maximum of 6 different rates from a generic clock. The
* generic clock has a power of two divider, which gives 6 steps from 192 kHz
* to 5112 Hz.
*/
#define MAX_NUM_RATES 6
/* ALSA seems to use rates between 192000 Hz and 5112 Hz. */
#define RATE_MAX 192000
#define RATE_MIN 5112
enum
{
DMA_READY
=
0
,
};
struct
atmel_abdac_dma
{
struct
dma_chan
*
chan
;
struct
dw_cyclic_desc
*
cdesc
;
};
struct
atmel_abdac
{
struct
clk
*
pclk
;
struct
clk
*
sample_clk
;
struct
platform_device
*
pdev
;
struct
atmel_abdac_dma
dma
;
struct
snd_pcm_hw_constraint_list
constraints_rates
;
struct
snd_pcm_substream
*
substream
;
struct
snd_card
*
card
;
struct
snd_pcm
*
pcm
;
void
__iomem
*
regs
;
unsigned
long
flags
;
unsigned
int
rates
[
MAX_NUM_RATES
];
unsigned
int
rates_num
;
int
irq
;
};
#define get_dac(card) ((struct atmel_abdac *)(card)->private_data)
/* This function is called by the DMA driver. */
static
void
atmel_abdac_dma_period_done
(
void
*
arg
)
{
struct
atmel_abdac
*
dac
=
arg
;
snd_pcm_period_elapsed
(
dac
->
substream
);
}
static
int
atmel_abdac_prepare_dma
(
struct
atmel_abdac
*
dac
,
struct
snd_pcm_substream
*
substream
,
enum
dma_data_direction
direction
)
{
struct
dma_chan
*
chan
=
dac
->
dma
.
chan
;
struct
dw_cyclic_desc
*
cdesc
;
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
unsigned
long
buffer_len
,
period_len
;
/*
* We don't do DMA on "complex" transfers, i.e. with
* non-halfword-aligned buffers or lengths.
*/
if
(
runtime
->
dma_addr
&
1
||
runtime
->
buffer_size
&
1
)
{
dev_dbg
(
&
dac
->
pdev
->
dev
,
"too complex transfer
\n
"
);
return
-
EINVAL
;
}
buffer_len
=
frames_to_bytes
(
runtime
,
runtime
->
buffer_size
);
period_len
=
frames_to_bytes
(
runtime
,
runtime
->
period_size
);
cdesc
=
dw_dma_cyclic_prep
(
chan
,
runtime
->
dma_addr
,
buffer_len
,
period_len
,
DMA_MEM_TO_DEV
);
if
(
IS_ERR
(
cdesc
))
{
dev_dbg
(
&
dac
->
pdev
->
dev
,
"could not prepare cyclic DMA
\n
"
);
return
PTR_ERR
(
cdesc
);
}
cdesc
->
period_callback
=
atmel_abdac_dma_period_done
;
cdesc
->
period_callback_param
=
dac
;
dac
->
dma
.
cdesc
=
cdesc
;
set_bit
(
DMA_READY
,
&
dac
->
flags
);
return
0
;
}
static
struct
snd_pcm_hardware
atmel_abdac_hw
=
{
.
info
=
(
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_RESUME
|
SNDRV_PCM_INFO_PAUSE
),
.
formats
=
(
SNDRV_PCM_FMTBIT_S16_BE
),
.
rates
=
(
SNDRV_PCM_RATE_KNOT
),
.
rate_min
=
RATE_MIN
,
.
rate_max
=
RATE_MAX
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
buffer_bytes_max
=
64
*
4096
,
.
period_bytes_min
=
4096
,
.
period_bytes_max
=
4096
,
.
periods_min
=
6
,
.
periods_max
=
64
,
};
static
int
atmel_abdac_open
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
dac
->
substream
=
substream
;
atmel_abdac_hw
.
rate_max
=
dac
->
rates
[
dac
->
rates_num
-
1
];
atmel_abdac_hw
.
rate_min
=
dac
->
rates
[
0
];
substream
->
runtime
->
hw
=
atmel_abdac_hw
;
return
snd_pcm_hw_constraint_list
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_RATE
,
&
dac
->
constraints_rates
);
}
static
int
atmel_abdac_close
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
dac
->
substream
=
NULL
;
return
0
;
}
static
int
atmel_abdac_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
hw_params
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
int
retval
;
retval
=
snd_pcm_lib_malloc_pages
(
substream
,
params_buffer_bytes
(
hw_params
));
if
(
retval
<
0
)
return
retval
;
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
if
(
retval
==
1
)
if
(
test_and_clear_bit
(
DMA_READY
,
&
dac
->
flags
))
dw_dma_cyclic_free
(
dac
->
dma
.
chan
);
return
retval
;
}
static
int
atmel_abdac_hw_free
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
if
(
test_and_clear_bit
(
DMA_READY
,
&
dac
->
flags
))
dw_dma_cyclic_free
(
dac
->
dma
.
chan
);
return
snd_pcm_lib_free_pages
(
substream
);
}
static
int
atmel_abdac_prepare
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
int
retval
;
retval
=
clk_set_rate
(
dac
->
sample_clk
,
256
*
substream
->
runtime
->
rate
);
if
(
retval
)
return
retval
;
if
(
!
test_bit
(
DMA_READY
,
&
dac
->
flags
))
retval
=
atmel_abdac_prepare_dma
(
dac
,
substream
,
DMA_TO_DEVICE
);
return
retval
;
}
static
int
atmel_abdac_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
int
retval
=
0
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
/* fall through */
case
SNDRV_PCM_TRIGGER_RESUME
:
/* fall through */
case
SNDRV_PCM_TRIGGER_START
:
clk_prepare_enable
(
dac
->
sample_clk
);
retval
=
dw_dma_cyclic_start
(
dac
->
dma
.
chan
);
if
(
retval
)
goto
out
;
dac_writel
(
dac
,
CTRL
,
DAC_BIT
(
EN
));
break
;
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
/* fall through */
case
SNDRV_PCM_TRIGGER_SUSPEND
:
/* fall through */
case
SNDRV_PCM_TRIGGER_STOP
:
dw_dma_cyclic_stop
(
dac
->
dma
.
chan
);
dac_writel
(
dac
,
DATA
,
0
);
dac_writel
(
dac
,
CTRL
,
0
);
clk_disable_unprepare
(
dac
->
sample_clk
);
break
;
default:
retval
=
-
EINVAL
;
break
;
}
out:
return
retval
;
}
static
snd_pcm_uframes_t
atmel_abdac_pointer
(
struct
snd_pcm_substream
*
substream
)
{
struct
atmel_abdac
*
dac
=
snd_pcm_substream_chip
(
substream
);
struct
snd_pcm_runtime
*
runtime
=
substream
->
runtime
;
snd_pcm_uframes_t
frames
;
unsigned
long
bytes
;
bytes
=
dw_dma_get_src_addr
(
dac
->
dma
.
chan
);
bytes
-=
runtime
->
dma_addr
;
frames
=
bytes_to_frames
(
runtime
,
bytes
);
if
(
frames
>=
runtime
->
buffer_size
)
frames
-=
runtime
->
buffer_size
;
return
frames
;
}
static
irqreturn_t
abdac_interrupt
(
int
irq
,
void
*
dev_id
)
{
struct
atmel_abdac
*
dac
=
dev_id
;
u32
status
;
status
=
dac_readl
(
dac
,
INT_STATUS
);
if
(
status
&
DAC_BIT
(
UNDERRUN
))
{
dev_err
(
&
dac
->
pdev
->
dev
,
"underrun detected
\n
"
);
dac_writel
(
dac
,
INT_CLR
,
DAC_BIT
(
UNDERRUN
));
}
else
{
dev_err
(
&
dac
->
pdev
->
dev
,
"spurious interrupt (status=0x%x)
\n
"
,
status
);
dac_writel
(
dac
,
INT_CLR
,
status
);
}
return
IRQ_HANDLED
;
}
static
struct
snd_pcm_ops
atmel_abdac_ops
=
{
.
open
=
atmel_abdac_open
,
.
close
=
atmel_abdac_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
atmel_abdac_hw_params
,
.
hw_free
=
atmel_abdac_hw_free
,
.
prepare
=
atmel_abdac_prepare
,
.
trigger
=
atmel_abdac_trigger
,
.
pointer
=
atmel_abdac_pointer
,
};
static
int
atmel_abdac_pcm_new
(
struct
atmel_abdac
*
dac
)
{
struct
snd_pcm_hardware
hw
=
atmel_abdac_hw
;
struct
snd_pcm
*
pcm
;
int
retval
;
retval
=
snd_pcm_new
(
dac
->
card
,
dac
->
card
->
shortname
,
dac
->
pdev
->
id
,
1
,
0
,
&
pcm
);
if
(
retval
)
return
retval
;
strcpy
(
pcm
->
name
,
dac
->
card
->
shortname
);
pcm
->
private_data
=
dac
;
pcm
->
info_flags
=
0
;
dac
->
pcm
=
pcm
;
snd_pcm_set_ops
(
pcm
,
SNDRV_PCM_STREAM_PLAYBACK
,
&
atmel_abdac_ops
);
retval
=
snd_pcm_lib_preallocate_pages_for_all
(
pcm
,
SNDRV_DMA_TYPE_DEV
,
&
dac
->
pdev
->
dev
,
hw
.
periods_min
*
hw
.
period_bytes_min
,
hw
.
buffer_bytes_max
);
return
retval
;
}
static
bool
filter
(
struct
dma_chan
*
chan
,
void
*
slave
)
{
struct
dw_dma_slave
*
dws
=
slave
;
if
(
dws
->
dma_dev
==
chan
->
device
->
dev
)
{
chan
->
private
=
dws
;
return
true
;
}
else
return
false
;
}
static
int
set_sample_rates
(
struct
atmel_abdac
*
dac
)
{
long
new_rate
=
RATE_MAX
;
int
retval
=
-
EINVAL
;
int
index
=
0
;
/* we start at 192 kHz and work our way down to 5112 Hz */
while
(
new_rate
>=
RATE_MIN
&&
index
<
(
MAX_NUM_RATES
+
1
))
{
new_rate
=
clk_round_rate
(
dac
->
sample_clk
,
256
*
new_rate
);
if
(
new_rate
<=
0
)
break
;
/* make sure we are below the ABDAC clock */
if
(
index
<
MAX_NUM_RATES
&&
new_rate
<=
clk_get_rate
(
dac
->
pclk
))
{
dac
->
rates
[
index
]
=
new_rate
/
256
;
index
++
;
}
/* divide by 256 and then by two to get next rate */
new_rate
/=
256
*
2
;
}
if
(
index
)
{
int
i
;
/* reverse array, smallest go first */
for
(
i
=
0
;
i
<
(
index
/
2
);
i
++
)
{
unsigned
int
tmp
=
dac
->
rates
[
index
-
1
-
i
];
dac
->
rates
[
index
-
1
-
i
]
=
dac
->
rates
[
i
];
dac
->
rates
[
i
]
=
tmp
;
}
dac
->
constraints_rates
.
count
=
index
;
dac
->
constraints_rates
.
list
=
dac
->
rates
;
dac
->
constraints_rates
.
mask
=
0
;
dac
->
rates_num
=
index
;
retval
=
0
;
}
return
retval
;
}
static
int
atmel_abdac_probe
(
struct
platform_device
*
pdev
)
{
struct
snd_card
*
card
;
struct
atmel_abdac
*
dac
;
struct
resource
*
regs
;
struct
atmel_abdac_pdata
*
pdata
;
struct
clk
*
pclk
;
struct
clk
*
sample_clk
;
int
retval
;
int
irq
;
regs
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
regs
)
{
dev_dbg
(
&
pdev
->
dev
,
"no memory resource
\n
"
);
return
-
ENXIO
;
}
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
<
0
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not get IRQ number
\n
"
);
return
irq
;
}
pdata
=
pdev
->
dev
.
platform_data
;
if
(
!
pdata
)
{
dev_dbg
(
&
pdev
->
dev
,
"no platform data
\n
"
);
return
-
ENXIO
;
}
pclk
=
clk_get
(
&
pdev
->
dev
,
"pclk"
);
if
(
IS_ERR
(
pclk
))
{
dev_dbg
(
&
pdev
->
dev
,
"no peripheral clock
\n
"
);
return
PTR_ERR
(
pclk
);
}
sample_clk
=
clk_get
(
&
pdev
->
dev
,
"sample_clk"
);
if
(
IS_ERR
(
sample_clk
))
{
dev_dbg
(
&
pdev
->
dev
,
"no sample clock
\n
"
);
retval
=
PTR_ERR
(
sample_clk
);
goto
out_put_pclk
;
}
clk_prepare_enable
(
pclk
);
retval
=
snd_card_new
(
&
pdev
->
dev
,
SNDRV_DEFAULT_IDX1
,
SNDRV_DEFAULT_STR1
,
THIS_MODULE
,
sizeof
(
struct
atmel_abdac
),
&
card
);
if
(
retval
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not create sound card device
\n
"
);
goto
out_put_sample_clk
;
}
dac
=
get_dac
(
card
);
dac
->
irq
=
irq
;
dac
->
card
=
card
;
dac
->
pclk
=
pclk
;
dac
->
sample_clk
=
sample_clk
;
dac
->
pdev
=
pdev
;
retval
=
set_sample_rates
(
dac
);
if
(
retval
<
0
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not set supported rates
\n
"
);
goto
out_free_card
;
}
dac
->
regs
=
ioremap
(
regs
->
start
,
resource_size
(
regs
));
if
(
!
dac
->
regs
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not remap register memory
\n
"
);
retval
=
-
ENOMEM
;
goto
out_free_card
;
}
/* make sure the DAC is silent and disabled */
dac_writel
(
dac
,
DATA
,
0
);
dac_writel
(
dac
,
CTRL
,
0
);
retval
=
request_irq
(
irq
,
abdac_interrupt
,
0
,
"abdac"
,
dac
);
if
(
retval
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not request irq
\n
"
);
goto
out_unmap_regs
;
}
if
(
pdata
->
dws
.
dma_dev
)
{
dma_cap_mask_t
mask
;
dma_cap_zero
(
mask
);
dma_cap_set
(
DMA_SLAVE
,
mask
);
dac
->
dma
.
chan
=
dma_request_channel
(
mask
,
filter
,
&
pdata
->
dws
);
if
(
dac
->
dma
.
chan
)
{
struct
dma_slave_config
dma_conf
=
{
.
dst_addr
=
regs
->
start
+
DAC_DATA
,
.
dst_addr_width
=
DMA_SLAVE_BUSWIDTH_4_BYTES
,
.
src_maxburst
=
1
,
.
dst_maxburst
=
1
,
.
direction
=
DMA_MEM_TO_DEV
,
.
device_fc
=
false
,
};
dmaengine_slave_config
(
dac
->
dma
.
chan
,
&
dma_conf
);
}
}
if
(
!
pdata
->
dws
.
dma_dev
||
!
dac
->
dma
.
chan
)
{
dev_dbg
(
&
pdev
->
dev
,
"DMA not available
\n
"
);
retval
=
-
ENODEV
;
goto
out_unmap_regs
;
}
strcpy
(
card
->
driver
,
"Atmel ABDAC"
);
strcpy
(
card
->
shortname
,
"Atmel ABDAC"
);
sprintf
(
card
->
longname
,
"Atmel Audio Bitstream DAC"
);
retval
=
atmel_abdac_pcm_new
(
dac
);
if
(
retval
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not register ABDAC pcm device
\n
"
);
goto
out_release_dma
;
}
retval
=
snd_card_register
(
card
);
if
(
retval
)
{
dev_dbg
(
&
pdev
->
dev
,
"could not register sound card
\n
"
);
goto
out_release_dma
;
}
platform_set_drvdata
(
pdev
,
card
);
dev_info
(
&
pdev
->
dev
,
"Atmel ABDAC at 0x%p using %s
\n
"
,
dac
->
regs
,
dev_name
(
&
dac
->
dma
.
chan
->
dev
->
device
));
return
retval
;
out_release_dma:
dma_release_channel
(
dac
->
dma
.
chan
);
dac
->
dma
.
chan
=
NULL
;
out_unmap_regs:
iounmap
(
dac
->
regs
);
out_free_card:
snd_card_free
(
card
);
out_put_sample_clk:
clk_put
(
sample_clk
);
clk_disable_unprepare
(
pclk
);
out_put_pclk:
clk_put
(
pclk
);
return
retval
;
}
#ifdef CONFIG_PM_SLEEP
static
int
atmel_abdac_suspend
(
struct
device
*
pdev
)
{
struct
snd_card
*
card
=
dev_get_drvdata
(
pdev
);
struct
atmel_abdac
*
dac
=
card
->
private_data
;
dw_dma_cyclic_stop
(
dac
->
dma
.
chan
);
clk_disable_unprepare
(
dac
->
sample_clk
);
clk_disable_unprepare
(
dac
->
pclk
);
return
0
;
}
static
int
atmel_abdac_resume
(
struct
device
*
pdev
)
{
struct
snd_card
*
card
=
dev_get_drvdata
(
pdev
);
struct
atmel_abdac
*
dac
=
card
->
private_data
;
clk_prepare_enable
(
dac
->
pclk
);
clk_prepare_enable
(
dac
->
sample_clk
);
if
(
test_bit
(
DMA_READY
,
&
dac
->
flags
))
dw_dma_cyclic_start
(
dac
->
dma
.
chan
);
return
0
;
}
static
SIMPLE_DEV_PM_OPS
(
atmel_abdac_pm
,
atmel_abdac_suspend
,
atmel_abdac_resume
);
#define ATMEL_ABDAC_PM_OPS &atmel_abdac_pm
#else
#define ATMEL_ABDAC_PM_OPS NULL
#endif
static
int
atmel_abdac_remove
(
struct
platform_device
*
pdev
)
{
struct
snd_card
*
card
=
platform_get_drvdata
(
pdev
);
struct
atmel_abdac
*
dac
=
get_dac
(
card
);
clk_put
(
dac
->
sample_clk
);
clk_disable_unprepare
(
dac
->
pclk
);
clk_put
(
dac
->
pclk
);
dma_release_channel
(
dac
->
dma
.
chan
);
dac
->
dma
.
chan
=
NULL
;
iounmap
(
dac
->
regs
);
free_irq
(
dac
->
irq
,
dac
);
snd_card_free
(
card
);
return
0
;
}
static
struct
platform_driver
atmel_abdac_driver
=
{
.
remove
=
atmel_abdac_remove
,
.
driver
=
{
.
name
=
"atmel_abdac"
,
.
pm
=
ATMEL_ABDAC_PM_OPS
,
},
};
static
int
__init
atmel_abdac_init
(
void
)
{
return
platform_driver_probe
(
&
atmel_abdac_driver
,
atmel_abdac_probe
);
}
module_init
(
atmel_abdac_init
);
static
void
__exit
atmel_abdac_exit
(
void
)
{
platform_driver_unregister
(
&
atmel_abdac_driver
);
}
module_exit
(
atmel_abdac_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_DESCRIPTION
(
"Driver for Atmel Audio Bitstream DAC (ABDAC)"
);
MODULE_AUTHOR
(
"Hans-Christian Egtvedt <egtvedt@samfundet.no>"
);
sound/atmel/ac97c.c
浏览文件 @
a96801b5
此差异已折叠。
点击以展开。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录