Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
raspberrypi-kernel
提交
c331a23b
R
raspberrypi-kernel
项目概览
openeuler
/
raspberrypi-kernel
通知
13
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
raspberrypi-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
c331a23b
编写于
2月 11, 2013
作者:
M
Mark Brown
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'asoc/topic/adsp' into asoc-next
上级
708558d3
bdaacea3
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
861 addition
and
82 deletion
+861
-82
drivers/base/regmap/internal.h
drivers/base/regmap/internal.h
+18
-0
drivers/base/regmap/regmap-spi.c
drivers/base/regmap/regmap-spi.c
+52
-0
drivers/base/regmap/regmap.c
drivers/base/regmap/regmap.c
+254
-47
include/linux/regmap.h
include/linux/regmap.h
+28
-0
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.c
+481
-30
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/wm_adsp.h
+18
-0
sound/soc/codecs/wmfw.h
sound/soc/codecs/wmfw.h
+10
-5
未找到文件。
drivers/base/regmap/internal.h
浏览文件 @
c331a23b
...
...
@@ -16,6 +16,7 @@
#include <linux/regmap.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/wait.h>
struct
regmap
;
struct
regcache_ops
;
...
...
@@ -39,6 +40,13 @@ struct regmap_format {
unsigned
int
(
*
parse_val
)(
void
*
buf
);
};
struct
regmap_async
{
struct
list_head
list
;
struct
work_struct
cleanup
;
struct
regmap
*
map
;
void
*
work_buf
;
};
struct
regmap
{
struct
mutex
mutex
;
spinlock_t
spinlock
;
...
...
@@ -53,6 +61,11 @@ struct regmap {
void
*
bus_context
;
const
char
*
name
;
spinlock_t
async_lock
;
wait_queue_head_t
async_waitq
;
struct
list_head
async_list
;
int
async_ret
;
#ifdef CONFIG_DEBUG_FS
struct
dentry
*
debugfs
;
const
char
*
debugfs_name
;
...
...
@@ -74,6 +87,9 @@ struct regmap {
const
struct
regmap_access_table
*
volatile_table
;
const
struct
regmap_access_table
*
precious_table
;
int
(
*
reg_read
)(
void
*
context
,
unsigned
int
reg
,
unsigned
int
*
val
);
int
(
*
reg_write
)(
void
*
context
,
unsigned
int
reg
,
unsigned
int
val
);
u8
read_flag_mask
;
u8
write_flag_mask
;
...
...
@@ -175,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx,
unsigned
int
val
,
unsigned
int
word_size
);
int
regcache_lookup_reg
(
struct
regmap
*
map
,
unsigned
int
reg
);
void
regmap_async_complete_cb
(
struct
regmap_async
*
async
,
int
ret
);
extern
struct
regcache_ops
regcache_rbtree_ops
;
extern
struct
regcache_ops
regcache_lzo_ops
;
...
...
drivers/base/regmap/regmap-spi.c
浏览文件 @
c331a23b
...
...
@@ -15,6 +15,21 @@
#include <linux/init.h>
#include <linux/module.h>
#include "internal.h"
struct
regmap_async_spi
{
struct
regmap_async
core
;
struct
spi_message
m
;
struct
spi_transfer
t
[
2
];
};
static
void
regmap_spi_complete
(
void
*
data
)
{
struct
regmap_async_spi
*
async
=
data
;
regmap_async_complete_cb
(
&
async
->
core
,
async
->
m
.
status
);
}
static
int
regmap_spi_write
(
void
*
context
,
const
void
*
data
,
size_t
count
)
{
struct
device
*
dev
=
context
;
...
...
@@ -40,6 +55,41 @@ static int regmap_spi_gather_write(void *context,
return
spi_sync
(
spi
,
&
m
);
}
static
int
regmap_spi_async_write
(
void
*
context
,
const
void
*
reg
,
size_t
reg_len
,
const
void
*
val
,
size_t
val_len
,
struct
regmap_async
*
a
)
{
struct
regmap_async_spi
*
async
=
container_of
(
a
,
struct
regmap_async_spi
,
core
);
struct
device
*
dev
=
context
;
struct
spi_device
*
spi
=
to_spi_device
(
dev
);
async
->
t
[
0
].
tx_buf
=
reg
;
async
->
t
[
0
].
len
=
reg_len
;
async
->
t
[
1
].
tx_buf
=
val
;
async
->
t
[
1
].
len
=
val_len
;
spi_message_init
(
&
async
->
m
);
spi_message_add_tail
(
&
async
->
t
[
0
],
&
async
->
m
);
spi_message_add_tail
(
&
async
->
t
[
1
],
&
async
->
m
);
async
->
m
.
complete
=
regmap_spi_complete
;
async
->
m
.
context
=
async
;
return
spi_async
(
spi
,
&
async
->
m
);
}
static
struct
regmap_async
*
regmap_spi_async_alloc
(
void
)
{
struct
regmap_async_spi
*
async_spi
;
async_spi
=
kzalloc
(
sizeof
(
*
async_spi
),
GFP_KERNEL
);
return
&
async_spi
->
core
;
}
static
int
regmap_spi_read
(
void
*
context
,
const
void
*
reg
,
size_t
reg_size
,
void
*
val
,
size_t
val_size
)
...
...
@@ -53,6 +103,8 @@ static int regmap_spi_read(void *context,
static
struct
regmap_bus
regmap_spi
=
{
.
write
=
regmap_spi_write
,
.
gather_write
=
regmap_spi_gather_write
,
.
async_write
=
regmap_spi_async_write
,
.
async_alloc
=
regmap_spi_async_alloc
,
.
read
=
regmap_spi_read
,
.
read_flag_mask
=
0x80
,
};
...
...
drivers/base/regmap/regmap.c
浏览文件 @
c331a23b
...
...
@@ -16,6 +16,7 @@
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/rbtree.h>
#include <linux/sched.h>
#define CREATE_TRACE_POINTS
#include <trace/events/regmap.h>
...
...
@@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned
int
mask
,
unsigned
int
val
,
bool
*
change
);
static
int
_regmap_bus_read
(
void
*
context
,
unsigned
int
reg
,
unsigned
int
*
val
);
static
int
_regmap_bus_formatted_write
(
void
*
context
,
unsigned
int
reg
,
unsigned
int
val
);
static
int
_regmap_bus_raw_write
(
void
*
context
,
unsigned
int
reg
,
unsigned
int
val
);
static
void
async_cleanup
(
struct
work_struct
*
work
)
{
struct
regmap_async
*
async
=
container_of
(
work
,
struct
regmap_async
,
cleanup
);
kfree
(
async
->
work_buf
);
kfree
(
async
);
}
bool
regmap_reg_in_ranges
(
unsigned
int
reg
,
const
struct
regmap_range
*
ranges
,
unsigned
int
nranges
)
...
...
@@ -423,6 +440,10 @@ struct regmap *regmap_init(struct device *dev,
map
->
cache_type
=
config
->
cache_type
;
map
->
name
=
config
->
name
;
spin_lock_init
(
&
map
->
async_lock
);
INIT_LIST_HEAD
(
&
map
->
async_list
);
init_waitqueue_head
(
&
map
->
async_waitq
);
if
(
config
->
read_flag_mask
||
config
->
write_flag_mask
)
{
map
->
read_flag_mask
=
config
->
read_flag_mask
;
map
->
write_flag_mask
=
config
->
write_flag_mask
;
...
...
@@ -430,6 +451,8 @@ struct regmap *regmap_init(struct device *dev,
map
->
read_flag_mask
=
bus
->
read_flag_mask
;
}
map
->
reg_read
=
_regmap_bus_read
;
reg_endian
=
config
->
reg_format_endian
;
if
(
reg_endian
==
REGMAP_ENDIAN_DEFAULT
)
reg_endian
=
bus
->
reg_format_endian_default
;
...
...
@@ -575,6 +598,11 @@ struct regmap *regmap_init(struct device *dev,
goto
err_map
;
}
if
(
map
->
format
.
format_write
)
map
->
reg_write
=
_regmap_bus_formatted_write
;
else
if
(
map
->
format
.
format_val
)
map
->
reg_write
=
_regmap_bus_raw_write
;
map
->
range_tree
=
RB_ROOT
;
for
(
i
=
0
;
i
<
config
->
num_ranges
;
i
++
)
{
const
struct
regmap_range_cfg
*
range_cfg
=
&
config
->
ranges
[
i
];
...
...
@@ -870,10 +898,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
}
static
int
_regmap_raw_write
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_len
)
const
void
*
val
,
size_t
val_len
,
bool
async
)
{
struct
regmap_range_node
*
range
;
unsigned
long
flags
;
u8
*
u8
=
map
->
work_buf
;
void
*
work_val
=
map
->
work_buf
+
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
;
void
*
buf
;
int
ret
=
-
ENOTSUPP
;
size_t
len
;
...
...
@@ -918,7 +949,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
dev_dbg
(
map
->
dev
,
"Writing window %d/%zu
\n
"
,
win_residue
,
val_len
/
map
->
format
.
val_bytes
);
ret
=
_regmap_raw_write
(
map
,
reg
,
val
,
win_residue
*
map
->
format
.
val_bytes
);
map
->
format
.
val_bytes
,
async
);
if
(
ret
!=
0
)
return
ret
;
...
...
@@ -941,6 +972,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
u8
[
0
]
|=
map
->
write_flag_mask
;
if
(
async
&&
map
->
bus
->
async_write
)
{
struct
regmap_async
*
async
=
map
->
bus
->
async_alloc
();
if
(
!
async
)
return
-
ENOMEM
;
async
->
work_buf
=
kzalloc
(
map
->
format
.
buf_size
,
GFP_KERNEL
|
GFP_DMA
);
if
(
!
async
->
work_buf
)
{
kfree
(
async
);
return
-
ENOMEM
;
}
INIT_WORK
(
&
async
->
cleanup
,
async_cleanup
);
async
->
map
=
map
;
/* If the caller supplied the value we can use it safely. */
memcpy
(
async
->
work_buf
,
map
->
work_buf
,
map
->
format
.
pad_bytes
+
map
->
format
.
reg_bytes
+
map
->
format
.
val_bytes
);
if
(
val
==
work_val
)
val
=
async
->
work_buf
+
map
->
format
.
pad_bytes
+
map
->
format
.
reg_bytes
;
spin_lock_irqsave
(
&
map
->
async_lock
,
flags
);
list_add_tail
(
&
async
->
list
,
&
map
->
async_list
);
spin_unlock_irqrestore
(
&
map
->
async_lock
,
flags
);
ret
=
map
->
bus
->
async_write
(
map
->
bus_context
,
async
->
work_buf
,
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
,
val
,
val_len
,
async
);
if
(
ret
!=
0
)
{
dev_err
(
map
->
dev
,
"Failed to schedule write: %d
\n
"
,
ret
);
spin_lock_irqsave
(
&
map
->
async_lock
,
flags
);
list_del
(
&
async
->
list
);
spin_unlock_irqrestore
(
&
map
->
async_lock
,
flags
);
kfree
(
async
->
work_buf
);
kfree
(
async
);
}
}
trace_regmap_hw_write_start
(
map
->
dev
,
reg
,
val_len
/
map
->
format
.
val_bytes
);
...
...
@@ -948,8 +1023,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
* send the work_buf directly, otherwise try to do a gather
* write.
*/
if
(
val
==
(
map
->
work_buf
+
map
->
format
.
pad_bytes
+
map
->
format
.
reg_bytes
))
if
(
val
==
work_val
)
ret
=
map
->
bus
->
write
(
map
->
bus_context
,
map
->
work_buf
,
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
+
...
...
@@ -981,12 +1055,54 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
return
ret
;
}
static
int
_regmap_bus_formatted_write
(
void
*
context
,
unsigned
int
reg
,
unsigned
int
val
)
{
int
ret
;
struct
regmap_range_node
*
range
;
struct
regmap
*
map
=
context
;
BUG_ON
(
!
map
->
format
.
format_write
);
range
=
_regmap_range_lookup
(
map
,
reg
);
if
(
range
)
{
ret
=
_regmap_select_page
(
map
,
&
reg
,
range
,
1
);
if
(
ret
!=
0
)
return
ret
;
}
map
->
format
.
format_write
(
map
,
reg
,
val
);
trace_regmap_hw_write_start
(
map
->
dev
,
reg
,
1
);
ret
=
map
->
bus
->
write
(
map
->
bus_context
,
map
->
work_buf
,
map
->
format
.
buf_size
);
trace_regmap_hw_write_done
(
map
->
dev
,
reg
,
1
);
return
ret
;
}
static
int
_regmap_bus_raw_write
(
void
*
context
,
unsigned
int
reg
,
unsigned
int
val
)
{
struct
regmap
*
map
=
context
;
BUG_ON
(
!
map
->
format
.
format_val
);
map
->
format
.
format_val
(
map
->
work_buf
+
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
,
val
,
0
);
return
_regmap_raw_write
(
map
,
reg
,
map
->
work_buf
+
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
,
map
->
format
.
val_bytes
,
false
);
}
int
_regmap_write
(
struct
regmap
*
map
,
unsigned
int
reg
,
unsigned
int
val
)
{
struct
regmap_range_node
*
range
;
int
ret
;
BUG_ON
(
!
map
->
format
.
format_write
&&
!
map
->
format
.
format_val
);
if
(
!
map
->
cache_bypass
&&
map
->
format
.
format_write
)
{
ret
=
regcache_write
(
map
,
reg
,
val
);
...
...
@@ -1005,33 +1121,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
trace_regmap_reg_write
(
map
->
dev
,
reg
,
val
);
if
(
map
->
format
.
format_write
)
{
range
=
_regmap_range_lookup
(
map
,
reg
);
if
(
range
)
{
ret
=
_regmap_select_page
(
map
,
&
reg
,
range
,
1
);
if
(
ret
!=
0
)
return
ret
;
}
map
->
format
.
format_write
(
map
,
reg
,
val
);
trace_regmap_hw_write_start
(
map
->
dev
,
reg
,
1
);
ret
=
map
->
bus
->
write
(
map
->
bus_context
,
map
->
work_buf
,
map
->
format
.
buf_size
);
trace_regmap_hw_write_done
(
map
->
dev
,
reg
,
1
);
return
ret
;
}
else
{
map
->
format
.
format_val
(
map
->
work_buf
+
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
,
val
,
0
);
return
_regmap_raw_write
(
map
,
reg
,
map
->
work_buf
+
map
->
format
.
reg_bytes
+
map
->
format
.
pad_bytes
,
map
->
format
.
val_bytes
);
}
return
map
->
reg_write
(
map
,
reg
,
val
);
}
/**
...
...
@@ -1089,7 +1179,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
map
->
lock
(
map
->
lock_arg
);
ret
=
_regmap_raw_write
(
map
,
reg
,
val
,
val_len
);
ret
=
_regmap_raw_write
(
map
,
reg
,
val
,
val_len
,
false
);
map
->
unlock
(
map
->
lock_arg
);
...
...
@@ -1145,14 +1235,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
if
(
map
->
use_single_rw
)
{
for
(
i
=
0
;
i
<
val_count
;
i
++
)
{
ret
=
regmap_raw_write
(
map
,
reg
+
(
i
*
map
->
reg_stride
),
val
+
(
i
*
val_bytes
),
val_bytes
);
reg
+
(
i
*
map
->
reg_stride
),
val
+
(
i
*
val_bytes
),
val_bytes
);
if
(
ret
!=
0
)
return
ret
;
}
}
else
{
ret
=
_regmap_raw_write
(
map
,
reg
,
wval
,
val_bytes
*
val_count
);
ret
=
_regmap_raw_write
(
map
,
reg
,
wval
,
val_bytes
*
val_count
,
false
);
}
if
(
val_bytes
!=
1
)
...
...
@@ -1164,6 +1255,48 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
}
EXPORT_SYMBOL_GPL
(
regmap_bulk_write
);
/**
* regmap_raw_write_async(): Write raw values to one or more registers
* asynchronously
*
* @map: Register map to write to
* @reg: Initial register to write to
* @val: Block of data to be written, laid out for direct transmission to the
* device. Must be valid until regmap_async_complete() is called.
* @val_len: Length of data pointed to by val.
*
* This function is intended to be used for things like firmware
* download where a large block of data needs to be transferred to the
* device. No formatting will be done on the data provided.
*
* If supported by the underlying bus the write will be scheduled
* asynchronously, helping maximise I/O speed on higher speed buses
* like SPI. regmap_async_complete() can be called to ensure that all
* asynchrnous writes have been completed.
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int
regmap_raw_write_async
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_len
)
{
int
ret
;
if
(
val_len
%
map
->
format
.
val_bytes
)
return
-
EINVAL
;
if
(
reg
%
map
->
reg_stride
)
return
-
EINVAL
;
map
->
lock
(
map
->
lock_arg
);
ret
=
_regmap_raw_write
(
map
,
reg
,
val
,
val_len
,
true
);
map
->
unlock
(
map
->
lock_arg
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
regmap_raw_write_async
);
static
int
_regmap_raw_read
(
struct
regmap
*
map
,
unsigned
int
reg
,
void
*
val
,
unsigned
int
val_len
)
{
...
...
@@ -1202,10 +1335,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return
ret
;
}
static
int
_regmap_bus_read
(
void
*
context
,
unsigned
int
reg
,
unsigned
int
*
val
)
{
int
ret
;
struct
regmap
*
map
=
context
;
if
(
!
map
->
format
.
parse_val
)
return
-
EINVAL
;
ret
=
_regmap_raw_read
(
map
,
reg
,
map
->
work_buf
,
map
->
format
.
val_bytes
);
if
(
ret
==
0
)
*
val
=
map
->
format
.
parse_val
(
map
->
work_buf
);
return
ret
;
}
static
int
_regmap_read
(
struct
regmap
*
map
,
unsigned
int
reg
,
unsigned
int
*
val
)
{
int
ret
;
BUG_ON
(
!
map
->
reg_read
);
if
(
!
map
->
cache_bypass
)
{
ret
=
regcache_read
(
map
,
reg
,
val
);
...
...
@@ -1213,26 +1363,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
return
0
;
}
if
(
!
map
->
format
.
parse_val
)
return
-
EINVAL
;
if
(
map
->
cache_only
)
return
-
EBUSY
;
ret
=
_regmap_raw_read
(
map
,
reg
,
map
->
work_buf
,
map
->
format
.
val_bytes
);
ret
=
map
->
reg_read
(
map
,
reg
,
val
);
if
(
ret
==
0
)
{
*
val
=
map
->
format
.
parse_val
(
map
->
work_buf
);
#ifdef LOG_DEVICE
if
(
strcmp
(
dev_name
(
map
->
dev
),
LOG_DEVICE
)
==
0
)
dev_info
(
map
->
dev
,
"%x => %x
\n
"
,
reg
,
*
val
);
#endif
trace_regmap_reg_read
(
map
->
dev
,
reg
,
*
val
);
}
if
(
ret
==
0
&&
!
map
->
cache_bypass
)
regcache_write
(
map
,
reg
,
*
val
);
if
(
!
map
->
cache_bypass
)
regcache_write
(
map
,
reg
,
*
val
);
}
return
ret
;
}
...
...
@@ -1450,6 +1595,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
}
EXPORT_SYMBOL_GPL
(
regmap_update_bits_check
);
void
regmap_async_complete_cb
(
struct
regmap_async
*
async
,
int
ret
)
{
struct
regmap
*
map
=
async
->
map
;
bool
wake
;
spin_lock
(
&
map
->
async_lock
);
list_del
(
&
async
->
list
);
wake
=
list_empty
(
&
map
->
async_list
);
if
(
ret
!=
0
)
map
->
async_ret
=
ret
;
spin_unlock
(
&
map
->
async_lock
);
schedule_work
(
&
async
->
cleanup
);
if
(
wake
)
wake_up
(
&
map
->
async_waitq
);
}
EXPORT_SYMBOL_GPL
(
regmap_async_complete_cb
);
static
int
regmap_async_is_done
(
struct
regmap
*
map
)
{
unsigned
long
flags
;
int
ret
;
spin_lock_irqsave
(
&
map
->
async_lock
,
flags
);
ret
=
list_empty
(
&
map
->
async_list
);
spin_unlock_irqrestore
(
&
map
->
async_lock
,
flags
);
return
ret
;
}
/**
* regmap_async_complete: Ensure all asynchronous I/O has completed.
*
* @map: Map to operate on.
*
* Blocks until any pending asynchronous I/O has completed. Returns
* an error code for any failed I/O operations.
*/
int
regmap_async_complete
(
struct
regmap
*
map
)
{
unsigned
long
flags
;
int
ret
;
/* Nothing to do with no async support */
if
(
!
map
->
bus
->
async_write
)
return
0
;
wait_event
(
map
->
async_waitq
,
regmap_async_is_done
(
map
));
spin_lock_irqsave
(
&
map
->
async_lock
,
flags
);
ret
=
map
->
async_ret
;
map
->
async_ret
=
0
;
spin_unlock_irqrestore
(
&
map
->
async_lock
,
flags
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
regmap_async_complete
);
/**
* regmap_register_patch: Register and apply register updates to be applied
* on device initialistion
...
...
include/linux/regmap.h
浏览文件 @
c331a23b
...
...
@@ -235,14 +235,21 @@ struct regmap_range_cfg {
unsigned
int
window_len
;
};
struct
regmap_async
;
typedef
int
(
*
regmap_hw_write
)(
void
*
context
,
const
void
*
data
,
size_t
count
);
typedef
int
(
*
regmap_hw_gather_write
)(
void
*
context
,
const
void
*
reg
,
size_t
reg_len
,
const
void
*
val
,
size_t
val_len
);
typedef
int
(
*
regmap_hw_async_write
)(
void
*
context
,
const
void
*
reg
,
size_t
reg_len
,
const
void
*
val
,
size_t
val_len
,
struct
regmap_async
*
async
);
typedef
int
(
*
regmap_hw_read
)(
void
*
context
,
const
void
*
reg_buf
,
size_t
reg_size
,
void
*
val_buf
,
size_t
val_size
);
typedef
struct
regmap_async
*
(
*
regmap_hw_async_alloc
)(
void
);
typedef
void
(
*
regmap_hw_free_context
)(
void
*
context
);
/**
...
...
@@ -255,8 +262,11 @@ typedef void (*regmap_hw_free_context)(void *context);
* @write: Write operation.
* @gather_write: Write operation with split register/value, return -ENOTSUPP
* if not implemented on a given device.
* @async_write: Write operation which completes asynchronously, optional and
* must serialise with respect to non-async I/O.
* @read: Read operation. Data is returned in the buffer used to transmit
* data.
* @async_alloc: Allocate a regmap_async() structure.
* @read_flag_mask: Mask to be set in the top byte of the register when doing
* a read.
* @reg_format_endian_default: Default endianness for formatted register
...
...
@@ -265,13 +275,16 @@ typedef void (*regmap_hw_free_context)(void *context);
* @val_format_endian_default: Default endianness for formatted register
* values. Used when the regmap_config specifies DEFAULT. If this is
* DEFAULT, BIG is assumed.
* @async_size: Size of struct used for async work.
*/
struct
regmap_bus
{
bool
fast_io
;
regmap_hw_write
write
;
regmap_hw_gather_write
gather_write
;
regmap_hw_async_write
async_write
;
regmap_hw_read
read
;
regmap_hw_free_context
free_context
;
regmap_hw_async_alloc
async_alloc
;
u8
read_flag_mask
;
enum
regmap_endian
reg_format_endian_default
;
enum
regmap_endian
val_format_endian_default
;
...
...
@@ -310,6 +323,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
const
void
*
val
,
size_t
val_len
);
int
regmap_bulk_write
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_count
);
int
regmap_raw_write_async
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_len
);
int
regmap_read
(
struct
regmap
*
map
,
unsigned
int
reg
,
unsigned
int
*
val
);
int
regmap_raw_read
(
struct
regmap
*
map
,
unsigned
int
reg
,
void
*
val
,
size_t
val_len
);
...
...
@@ -321,6 +336,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned
int
mask
,
unsigned
int
val
,
bool
*
change
);
int
regmap_get_val_bytes
(
struct
regmap
*
map
);
int
regmap_async_complete
(
struct
regmap
*
map
);
int
regcache_sync
(
struct
regmap
*
map
);
int
regcache_sync_region
(
struct
regmap
*
map
,
unsigned
int
min
,
...
...
@@ -422,6 +438,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
return
-
EINVAL
;
}
static
inline
int
regmap_raw_write_async
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_len
)
{
WARN_ONCE
(
1
,
"regmap API is disabled"
);
return
-
EINVAL
;
}
static
inline
int
regmap_bulk_write
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_count
)
{
...
...
@@ -500,6 +523,11 @@ static inline void regcache_mark_dirty(struct regmap *map)
WARN_ONCE
(
1
,
"regmap API is disabled"
);
}
static
inline
void
regmap_async_complete
(
struct
regmap
*
map
)
{
WARN_ONCE
(
1
,
"regmap API is disabled"
);
}
static
inline
int
regmap_register_patch
(
struct
regmap
*
map
,
const
struct
reg_default
*
regs
,
int
num_regs
)
...
...
sound/soc/codecs/wm_adsp.c
浏览文件 @
c331a23b
...
...
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
...
...
@@ -103,6 +104,13 @@
#define ADSP1_START_SHIFT 0
/* DSP1_START */
#define ADSP1_START_WIDTH 1
/* DSP1_START */
/*
* ADSP1 Control 31
*/
#define ADSP1_CLK_SEL_MASK 0x0007
/* CLK_SEL_ENA */
#define ADSP1_CLK_SEL_SHIFT 0
/* CLK_SEL_ENA */
#define ADSP1_CLK_SEL_WIDTH 3
/* CLK_SEL_ENA */
#define ADSP2_CONTROL 0x0
#define ADSP2_CLOCKING 0x1
#define ADSP2_STATUS1 0x4
...
...
@@ -146,6 +154,109 @@
#define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1
struct
wm_adsp_buf
{
struct
list_head
list
;
void
*
buf
;
};
static
struct
wm_adsp_buf
*
wm_adsp_buf_alloc
(
const
void
*
src
,
size_t
len
,
struct
list_head
*
list
)
{
struct
wm_adsp_buf
*
buf
=
kzalloc
(
sizeof
(
*
buf
),
GFP_KERNEL
);
if
(
buf
==
NULL
)
return
NULL
;
buf
->
buf
=
kmemdup
(
src
,
len
,
GFP_KERNEL
|
GFP_DMA
);
if
(
!
buf
->
buf
)
{
kfree
(
buf
);
return
NULL
;
}
if
(
list
)
list_add_tail
(
&
buf
->
list
,
list
);
return
buf
;
}
static
void
wm_adsp_buf_free
(
struct
list_head
*
list
)
{
while
(
!
list_empty
(
list
))
{
struct
wm_adsp_buf
*
buf
=
list_first_entry
(
list
,
struct
wm_adsp_buf
,
list
);
list_del
(
&
buf
->
list
);
kfree
(
buf
->
buf
);
kfree
(
buf
);
}
}
#define WM_ADSP_NUM_FW 4
static
const
char
*
wm_adsp_fw_text
[
WM_ADSP_NUM_FW
]
=
{
"MBC/VSS"
,
"Tx"
,
"Tx Speaker"
,
"Rx ANC"
};
static
struct
{
const
char
*
file
;
}
wm_adsp_fw
[
WM_ADSP_NUM_FW
]
=
{
{
.
file
=
"mbc-vss"
},
{
.
file
=
"tx"
},
{
.
file
=
"tx-spk"
},
{
.
file
=
"rx-anc"
},
};
static
int
wm_adsp_fw_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
struct
wm_adsp
*
adsp
=
snd_soc_codec_get_drvdata
(
codec
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
adsp
[
e
->
shift_l
].
fw
;
return
0
;
}
static
int
wm_adsp_fw_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
struct
wm_adsp
*
adsp
=
snd_soc_codec_get_drvdata
(
codec
);
if
(
ucontrol
->
value
.
integer
.
value
[
0
]
==
adsp
[
e
->
shift_l
].
fw
)
return
0
;
if
(
ucontrol
->
value
.
integer
.
value
[
0
]
>=
WM_ADSP_NUM_FW
)
return
-
EINVAL
;
if
(
adsp
[
e
->
shift_l
].
running
)
return
-
EBUSY
;
adsp
[
e
->
shift_l
].
fw
=
ucontrol
->
value
.
integer
.
value
[
0
];
return
0
;
}
static
const
struct
soc_enum
wm_adsp_fw_enum
[]
=
{
SOC_ENUM_SINGLE
(
0
,
0
,
ARRAY_SIZE
(
wm_adsp_fw_text
),
wm_adsp_fw_text
),
SOC_ENUM_SINGLE
(
0
,
1
,
ARRAY_SIZE
(
wm_adsp_fw_text
),
wm_adsp_fw_text
),
SOC_ENUM_SINGLE
(
0
,
2
,
ARRAY_SIZE
(
wm_adsp_fw_text
),
wm_adsp_fw_text
),
SOC_ENUM_SINGLE
(
0
,
3
,
ARRAY_SIZE
(
wm_adsp_fw_text
),
wm_adsp_fw_text
),
};
const
struct
snd_kcontrol_new
wm_adsp_fw_controls
[]
=
{
SOC_ENUM_EXT
(
"DSP1 Firmware"
,
wm_adsp_fw_enum
[
0
],
wm_adsp_fw_get
,
wm_adsp_fw_put
),
SOC_ENUM_EXT
(
"DSP2 Firmware"
,
wm_adsp_fw_enum
[
1
],
wm_adsp_fw_get
,
wm_adsp_fw_put
),
SOC_ENUM_EXT
(
"DSP3 Firmware"
,
wm_adsp_fw_enum
[
2
],
wm_adsp_fw_get
,
wm_adsp_fw_put
),
SOC_ENUM_EXT
(
"DSP4 Firmware"
,
wm_adsp_fw_enum
[
3
],
wm_adsp_fw_get
,
wm_adsp_fw_put
),
};
EXPORT_SYMBOL_GPL
(
wm_adsp_fw_controls
);
static
struct
wm_adsp_region
const
*
wm_adsp_find_region
(
struct
wm_adsp
*
dsp
,
int
type
)
...
...
@@ -159,8 +270,29 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
return
NULL
;
}
static
unsigned
int
wm_adsp_region_to_reg
(
struct
wm_adsp_region
const
*
region
,
unsigned
int
offset
)
{
switch
(
region
->
type
)
{
case
WMFW_ADSP1_PM
:
return
region
->
base
+
(
offset
*
3
);
case
WMFW_ADSP1_DM
:
return
region
->
base
+
(
offset
*
2
);
case
WMFW_ADSP2_XM
:
return
region
->
base
+
(
offset
*
2
);
case
WMFW_ADSP2_YM
:
return
region
->
base
+
(
offset
*
2
);
case
WMFW_ADSP1_ZM
:
return
region
->
base
+
(
offset
*
2
);
default:
WARN_ON
(
NULL
!=
"Unknown memory region type"
);
return
offset
;
}
}
static
int
wm_adsp_load
(
struct
wm_adsp
*
dsp
)
{
LIST_HEAD
(
buf_list
);
const
struct
firmware
*
firmware
;
struct
regmap
*
regmap
=
dsp
->
regmap
;
unsigned
int
pos
=
0
;
...
...
@@ -172,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
const
struct
wm_adsp_region
*
mem
;
const
char
*
region_name
;
char
*
file
,
*
text
;
void
*
buf
;
struct
wm_adsp_buf
*
buf
;
unsigned
int
reg
;
int
regions
=
0
;
int
ret
,
offset
,
type
,
sizes
;
...
...
@@ -181,7 +313,8 @@ static int wm_adsp_load(struct wm_adsp *dsp)
if
(
file
==
NULL
)
return
-
ENOMEM
;
snprintf
(
file
,
PAGE_SIZE
,
"%s-dsp%d.wmfw"
,
dsp
->
part
,
dsp
->
num
);
snprintf
(
file
,
PAGE_SIZE
,
"%s-dsp%d-%s.wmfw"
,
dsp
->
part
,
dsp
->
num
,
wm_adsp_fw
[
dsp
->
fw
].
file
);
file
[
PAGE_SIZE
-
1
]
=
'\0'
;
ret
=
request_firmware
(
&
firmware
,
file
,
dsp
->
dev
);
...
...
@@ -286,27 +419,27 @@ static int wm_adsp_load(struct wm_adsp *dsp)
case
WMFW_ADSP1_PM
:
BUG_ON
(
!
mem
);
region_name
=
"PM"
;
reg
=
mem
->
base
+
(
offset
*
3
);
reg
=
wm_adsp_region_to_reg
(
mem
,
offset
);
break
;
case
WMFW_ADSP1_DM
:
BUG_ON
(
!
mem
);
region_name
=
"DM"
;
reg
=
mem
->
base
+
(
offset
*
2
);
reg
=
wm_adsp_region_to_reg
(
mem
,
offset
);
break
;
case
WMFW_ADSP2_XM
:
BUG_ON
(
!
mem
);
region_name
=
"XM"
;
reg
=
mem
->
base
+
(
offset
*
2
);
reg
=
wm_adsp_region_to_reg
(
mem
,
offset
);
break
;
case
WMFW_ADSP2_YM
:
BUG_ON
(
!
mem
);
region_name
=
"YM"
;
reg
=
mem
->
base
+
(
offset
*
2
);
reg
=
wm_adsp_region_to_reg
(
mem
,
offset
);
break
;
case
WMFW_ADSP1_ZM
:
BUG_ON
(
!
mem
);
region_name
=
"ZM"
;
reg
=
mem
->
base
+
(
offset
*
2
);
reg
=
wm_adsp_region_to_reg
(
mem
,
offset
);
break
;
default:
adsp_warn
(
dsp
,
...
...
@@ -326,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp)
}
if
(
reg
)
{
buf
=
kmemdup
(
region
->
data
,
le32_to_cpu
(
region
->
len
),
GFP_KERNEL
|
GFP_DMA
);
buf
=
wm_adsp_buf_alloc
(
region
->
data
,
le32_to_cpu
(
region
->
len
),
&
buf_list
);
if
(
!
buf
)
{
adsp_err
(
dsp
,
"Out of memory
\n
"
);
return
-
ENOMEM
;
}
ret
=
regmap_raw_write
(
regmap
,
reg
,
buf
,
le32_to_cpu
(
region
->
len
));
kfree
(
buf
);
ret
=
regmap_raw_write_async
(
regmap
,
reg
,
buf
->
buf
,
le32_to_cpu
(
region
->
len
));
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"%s.%d: Failed to write %d bytes at %d in %s: %d
\n
"
,
...
...
@@ -351,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp)
pos
+=
le32_to_cpu
(
region
->
len
)
+
sizeof
(
*
region
);
regions
++
;
}
ret
=
regmap_async_complete
(
regmap
);
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to complete async write: %d
\n
"
,
ret
);
goto
out_fw
;
}
if
(
pos
>
firmware
->
size
)
adsp_warn
(
dsp
,
"%s.%d: %zu bytes at end of file
\n
"
,
file
,
regions
,
pos
-
firmware
->
size
);
out_fw:
regmap_async_complete
(
regmap
);
wm_adsp_buf_free
(
&
buf_list
);
release_firmware
(
firmware
);
out:
kfree
(
file
);
...
...
@@ -364,22 +503,222 @@ static int wm_adsp_load(struct wm_adsp *dsp)
return
ret
;
}
static
int
wm_adsp_setup_algs
(
struct
wm_adsp
*
dsp
)
{
struct
regmap
*
regmap
=
dsp
->
regmap
;
struct
wmfw_adsp1_id_hdr
adsp1_id
;
struct
wmfw_adsp2_id_hdr
adsp2_id
;
struct
wmfw_adsp1_alg_hdr
*
adsp1_alg
;
struct
wmfw_adsp2_alg_hdr
*
adsp2_alg
;
void
*
alg
,
*
buf
;
struct
wm_adsp_alg_region
*
region
;
const
struct
wm_adsp_region
*
mem
;
unsigned
int
pos
,
term
;
size_t
algs
,
buf_size
;
__be32
val
;
int
i
,
ret
;
switch
(
dsp
->
type
)
{
case
WMFW_ADSP1
:
mem
=
wm_adsp_find_region
(
dsp
,
WMFW_ADSP1_DM
);
break
;
case
WMFW_ADSP2
:
mem
=
wm_adsp_find_region
(
dsp
,
WMFW_ADSP2_XM
);
break
;
default:
mem
=
NULL
;
break
;
}
if
(
mem
==
NULL
)
{
BUG_ON
(
mem
!=
NULL
);
return
-
EINVAL
;
}
switch
(
dsp
->
type
)
{
case
WMFW_ADSP1
:
ret
=
regmap_raw_read
(
regmap
,
mem
->
base
,
&
adsp1_id
,
sizeof
(
adsp1_id
));
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to read algorithm info: %d
\n
"
,
ret
);
return
ret
;
}
buf
=
&
adsp1_id
;
buf_size
=
sizeof
(
adsp1_id
);
algs
=
be32_to_cpu
(
adsp1_id
.
algs
);
adsp_info
(
dsp
,
"Firmware: %x v%d.%d.%d, %zu algorithms
\n
"
,
be32_to_cpu
(
adsp1_id
.
fw
.
id
),
(
be32_to_cpu
(
adsp1_id
.
fw
.
ver
)
&
0xff0000
)
>>
16
,
(
be32_to_cpu
(
adsp1_id
.
fw
.
ver
)
&
0xff00
)
>>
8
,
be32_to_cpu
(
adsp1_id
.
fw
.
ver
)
&
0xff
,
algs
);
pos
=
sizeof
(
adsp1_id
)
/
2
;
term
=
pos
+
((
sizeof
(
*
adsp1_alg
)
*
algs
)
/
2
);
break
;
case
WMFW_ADSP2
:
ret
=
regmap_raw_read
(
regmap
,
mem
->
base
,
&
adsp2_id
,
sizeof
(
adsp2_id
));
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to read algorithm info: %d
\n
"
,
ret
);
return
ret
;
}
buf
=
&
adsp2_id
;
buf_size
=
sizeof
(
adsp2_id
);
algs
=
be32_to_cpu
(
adsp2_id
.
algs
);
adsp_info
(
dsp
,
"Firmware: %x v%d.%d.%d, %zu algorithms
\n
"
,
be32_to_cpu
(
adsp2_id
.
fw
.
id
),
(
be32_to_cpu
(
adsp2_id
.
fw
.
ver
)
&
0xff0000
)
>>
16
,
(
be32_to_cpu
(
adsp2_id
.
fw
.
ver
)
&
0xff00
)
>>
8
,
be32_to_cpu
(
adsp2_id
.
fw
.
ver
)
&
0xff
,
algs
);
pos
=
sizeof
(
adsp2_id
)
/
2
;
term
=
pos
+
((
sizeof
(
*
adsp2_alg
)
*
algs
)
/
2
);
break
;
default:
BUG_ON
(
NULL
==
"Unknown DSP type"
);
return
-
EINVAL
;
}
if
(
algs
==
0
)
{
adsp_err
(
dsp
,
"No algorithms
\n
"
);
return
-
EINVAL
;
}
if
(
algs
>
1024
)
{
adsp_err
(
dsp
,
"Algorithm count %zx excessive
\n
"
,
algs
);
print_hex_dump_bytes
(
dev_name
(
dsp
->
dev
),
DUMP_PREFIX_OFFSET
,
buf
,
buf_size
);
return
-
EINVAL
;
}
/* Read the terminator first to validate the length */
ret
=
regmap_raw_read
(
regmap
,
mem
->
base
+
term
,
&
val
,
sizeof
(
val
));
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to read algorithm list end: %d
\n
"
,
ret
);
return
ret
;
}
if
(
be32_to_cpu
(
val
)
!=
0xbedead
)
adsp_warn
(
dsp
,
"Algorithm list end %x 0x%x != 0xbeadead
\n
"
,
term
,
be32_to_cpu
(
val
));
alg
=
kzalloc
((
term
-
pos
)
*
2
,
GFP_KERNEL
|
GFP_DMA
);
if
(
!
alg
)
return
-
ENOMEM
;
ret
=
regmap_raw_read
(
regmap
,
mem
->
base
+
pos
,
alg
,
(
term
-
pos
)
*
2
);
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to read algorithm list: %d
\n
"
,
ret
);
goto
out
;
}
adsp1_alg
=
alg
;
adsp2_alg
=
alg
;
for
(
i
=
0
;
i
<
algs
;
i
++
)
{
switch
(
dsp
->
type
)
{
case
WMFW_ADSP1
:
adsp_info
(
dsp
,
"%d: ID %x v%d.%d.%d DM@%x ZM@%x
\n
"
,
i
,
be32_to_cpu
(
adsp1_alg
[
i
].
alg
.
id
),
(
be32_to_cpu
(
adsp1_alg
[
i
].
alg
.
ver
)
&
0xff0000
)
>>
16
,
(
be32_to_cpu
(
adsp1_alg
[
i
].
alg
.
ver
)
&
0xff00
)
>>
8
,
be32_to_cpu
(
adsp1_alg
[
i
].
alg
.
ver
)
&
0xff
,
be32_to_cpu
(
adsp1_alg
[
i
].
dm
),
be32_to_cpu
(
adsp1_alg
[
i
].
zm
));
region
=
kzalloc
(
sizeof
(
*
region
),
GFP_KERNEL
);
if
(
!
region
)
return
-
ENOMEM
;
region
->
type
=
WMFW_ADSP1_DM
;
region
->
alg
=
be32_to_cpu
(
adsp1_alg
[
i
].
alg
.
id
);
region
->
base
=
be32_to_cpu
(
adsp1_alg
[
i
].
dm
);
list_add_tail
(
&
region
->
list
,
&
dsp
->
alg_regions
);
region
=
kzalloc
(
sizeof
(
*
region
),
GFP_KERNEL
);
if
(
!
region
)
return
-
ENOMEM
;
region
->
type
=
WMFW_ADSP1_ZM
;
region
->
alg
=
be32_to_cpu
(
adsp1_alg
[
i
].
alg
.
id
);
region
->
base
=
be32_to_cpu
(
adsp1_alg
[
i
].
zm
);
list_add_tail
(
&
region
->
list
,
&
dsp
->
alg_regions
);
break
;
case
WMFW_ADSP2
:
adsp_info
(
dsp
,
"%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x
\n
"
,
i
,
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
id
),
(
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
ver
)
&
0xff0000
)
>>
16
,
(
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
ver
)
&
0xff00
)
>>
8
,
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
ver
)
&
0xff
,
be32_to_cpu
(
adsp2_alg
[
i
].
xm
),
be32_to_cpu
(
adsp2_alg
[
i
].
ym
),
be32_to_cpu
(
adsp2_alg
[
i
].
zm
));
region
=
kzalloc
(
sizeof
(
*
region
),
GFP_KERNEL
);
if
(
!
region
)
return
-
ENOMEM
;
region
->
type
=
WMFW_ADSP2_XM
;
region
->
alg
=
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
id
);
region
->
base
=
be32_to_cpu
(
adsp2_alg
[
i
].
xm
);
list_add_tail
(
&
region
->
list
,
&
dsp
->
alg_regions
);
region
=
kzalloc
(
sizeof
(
*
region
),
GFP_KERNEL
);
if
(
!
region
)
return
-
ENOMEM
;
region
->
type
=
WMFW_ADSP2_YM
;
region
->
alg
=
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
id
);
region
->
base
=
be32_to_cpu
(
adsp2_alg
[
i
].
ym
);
list_add_tail
(
&
region
->
list
,
&
dsp
->
alg_regions
);
region
=
kzalloc
(
sizeof
(
*
region
),
GFP_KERNEL
);
if
(
!
region
)
return
-
ENOMEM
;
region
->
type
=
WMFW_ADSP2_ZM
;
region
->
alg
=
be32_to_cpu
(
adsp2_alg
[
i
].
alg
.
id
);
region
->
base
=
be32_to_cpu
(
adsp2_alg
[
i
].
zm
);
list_add_tail
(
&
region
->
list
,
&
dsp
->
alg_regions
);
break
;
}
}
out:
kfree
(
alg
);
return
ret
;
}
static
int
wm_adsp_load_coeff
(
struct
wm_adsp
*
dsp
)
{
LIST_HEAD
(
buf_list
);
struct
regmap
*
regmap
=
dsp
->
regmap
;
struct
wmfw_coeff_hdr
*
hdr
;
struct
wmfw_coeff_item
*
blk
;
const
struct
firmware
*
firmware
;
const
struct
wm_adsp_region
*
mem
;
struct
wm_adsp_alg_region
*
alg_region
;
const
char
*
region_name
;
int
ret
,
pos
,
blocks
,
type
,
offset
,
reg
;
char
*
file
;
void
*
buf
;
struct
wm_adsp_buf
*
buf
;
int
tmp
;
file
=
kzalloc
(
PAGE_SIZE
,
GFP_KERNEL
);
if
(
file
==
NULL
)
return
-
ENOMEM
;
snprintf
(
file
,
PAGE_SIZE
,
"%s-dsp%d.bin"
,
dsp
->
part
,
dsp
->
num
);
snprintf
(
file
,
PAGE_SIZE
,
"%s-dsp%d-%s.bin"
,
dsp
->
part
,
dsp
->
num
,
wm_adsp_fw
[
dsp
->
fw
].
file
);
file
[
PAGE_SIZE
-
1
]
=
'\0'
;
ret
=
request_firmware
(
&
firmware
,
file
,
dsp
->
dev
);
...
...
@@ -402,6 +741,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
goto
out_fw
;
}
switch
(
be32_to_cpu
(
hdr
->
rev
)
&
0xff
)
{
case
1
:
break
;
default:
adsp_err
(
dsp
,
"%s: Unsupported coefficient file format %d
\n
"
,
file
,
be32_to_cpu
(
hdr
->
rev
)
&
0xff
);
ret
=
-
EINVAL
;
goto
out_fw
;
}
adsp_dbg
(
dsp
,
"%s: v%d.%d.%d
\n
"
,
file
,
(
le32_to_cpu
(
hdr
->
ver
)
>>
16
)
&
0xff
,
(
le32_to_cpu
(
hdr
->
ver
)
>>
8
)
&
0xff
,
...
...
@@ -414,8 +763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
pos
-
firmware
->
size
>
sizeof
(
*
blk
))
{
blk
=
(
void
*
)(
&
firmware
->
data
[
pos
]);
type
=
be32_to_cpu
(
blk
->
type
)
&
0xff
;
offset
=
le
32_to_cpu
(
blk
->
offset
)
&
0xffffff
;
type
=
le16_to_cpu
(
blk
->
type
)
;
offset
=
le
16_to_cpu
(
blk
->
offset
)
;
adsp_dbg
(
dsp
,
"%s.%d: %x v%d.%d.%d
\n
"
,
file
,
blocks
,
le32_to_cpu
(
blk
->
id
),
...
...
@@ -428,52 +777,105 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
reg
=
0
;
region_name
=
"Unknown"
;
switch
(
type
)
{
case
WMFW_NAME_TEXT
:
case
WMFW_INFO_TEXT
:
case
(
WMFW_NAME_TEXT
<<
8
)
:
case
(
WMFW_INFO_TEXT
<<
8
)
:
break
;
case
WMFW_ABSOLUTE
:
case
(
WMFW_ABSOLUTE
<<
8
)
:
region_name
=
"register"
;
reg
=
offset
;
break
;
case
WMFW_ADSP1_DM
:
case
WMFW_ADSP1_ZM
:
case
WMFW_ADSP2_XM
:
case
WMFW_ADSP2_YM
:
adsp_dbg
(
dsp
,
"%s.%d: %d bytes in %x for %x
\n
"
,
file
,
blocks
,
le32_to_cpu
(
blk
->
len
),
type
,
le32_to_cpu
(
blk
->
id
));
mem
=
wm_adsp_find_region
(
dsp
,
type
);
if
(
!
mem
)
{
adsp_err
(
dsp
,
"No base for region %x
\n
"
,
type
);
break
;
}
reg
=
0
;
list_for_each_entry
(
alg_region
,
&
dsp
->
alg_regions
,
list
)
{
if
(
le32_to_cpu
(
blk
->
id
)
==
alg_region
->
alg
&&
type
==
alg_region
->
type
)
{
reg
=
alg_region
->
base
;
reg
=
wm_adsp_region_to_reg
(
mem
,
reg
);
reg
+=
offset
;
}
}
if
(
reg
==
0
)
adsp_err
(
dsp
,
"No %x for algorithm %x
\n
"
,
type
,
le32_to_cpu
(
blk
->
id
));
break
;
default:
adsp_err
(
dsp
,
"Unknown region type %x
\n
"
,
type
);
adsp_err
(
dsp
,
"%s.%d: Unknown region type %x at %d
\n
"
,
file
,
blocks
,
type
,
pos
);
break
;
}
if
(
reg
)
{
buf
=
kmemdup
(
blk
->
data
,
le32_to_cpu
(
blk
->
len
),
GFP_KERNEL
|
GFP_DMA
);
buf
=
wm_adsp_buf_alloc
(
blk
->
data
,
le32_to_cpu
(
blk
->
len
),
&
buf_list
);
if
(
!
buf
)
{
adsp_err
(
dsp
,
"Out of memory
\n
"
);
return
-
ENOMEM
;
}
ret
=
regmap_raw_write
(
regmap
,
reg
,
blk
->
data
,
le32_to_cpu
(
blk
->
len
));
adsp_dbg
(
dsp
,
"%s.%d: Writing %d bytes at %x
\n
"
,
file
,
blocks
,
le32_to_cpu
(
blk
->
len
),
reg
);
ret
=
regmap_raw_write_async
(
regmap
,
reg
,
buf
->
buf
,
le32_to_cpu
(
blk
->
len
));
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"%s.%d: Failed to write to %x in %s
\n
"
,
file
,
blocks
,
reg
,
region_name
);
}
kfree
(
buf
);
}
pos
+=
le32_to_cpu
(
blk
->
len
)
+
sizeof
(
*
blk
);
tmp
=
le32_to_cpu
(
blk
->
len
)
%
4
;
if
(
tmp
)
pos
+=
le32_to_cpu
(
blk
->
len
)
+
(
4
-
tmp
)
+
sizeof
(
*
blk
);
else
pos
+=
le32_to_cpu
(
blk
->
len
)
+
sizeof
(
*
blk
);
blocks
++
;
}
ret
=
regmap_async_complete
(
regmap
);
if
(
ret
!=
0
)
adsp_err
(
dsp
,
"Failed to complete async write: %d
\n
"
,
ret
);
if
(
pos
>
firmware
->
size
)
adsp_warn
(
dsp
,
"%s.%d: %zu bytes at end of file
\n
"
,
file
,
blocks
,
pos
-
firmware
->
size
);
out_fw:
release_firmware
(
firmware
);
wm_adsp_buf_free
(
&
buf_list
);
out:
kfree
(
file
);
return
0
;
}
int
wm_adsp1_init
(
struct
wm_adsp
*
adsp
)
{
INIT_LIST_HEAD
(
&
adsp
->
alg_regions
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
wm_adsp1_init
);
int
wm_adsp1_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
kcontrol
,
int
event
)
...
...
@@ -482,16 +884,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct
wm_adsp
*
dsps
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm_adsp
*
dsp
=
&
dsps
[
w
->
shift
];
int
ret
;
int
val
;
switch
(
event
)
{
case
SND_SOC_DAPM_POST_PMU
:
regmap_update_bits
(
dsp
->
regmap
,
dsp
->
base
+
ADSP1_CONTROL_30
,
ADSP1_SYS_ENA
,
ADSP1_SYS_ENA
);
/*
* For simplicity set the DSP clock rate to be the
* SYSCLK rate rather than making it configurable.
*/
if
(
dsp
->
sysclk_reg
)
{
ret
=
regmap_read
(
dsp
->
regmap
,
dsp
->
sysclk_reg
,
&
val
);
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to read SYSCLK state: %d
\n
"
,
ret
);
return
ret
;
}
val
=
(
val
&
dsp
->
sysclk_mask
)
>>
dsp
->
sysclk_shift
;
ret
=
regmap_update_bits
(
dsp
->
regmap
,
dsp
->
base
+
ADSP1_CONTROL_31
,
ADSP1_CLK_SEL_MASK
,
val
);
if
(
ret
!=
0
)
{
adsp_err
(
dsp
,
"Failed to set clock rate: %d
\n
"
,
ret
);
return
ret
;
}
}
ret
=
wm_adsp_load
(
dsp
);
if
(
ret
!=
0
)
goto
err
;
ret
=
wm_adsp_setup_algs
(
dsp
);
if
(
ret
!=
0
)
goto
err
;
ret
=
wm_adsp_load_coeff
(
dsp
);
if
(
ret
!=
0
)
goto
err
;
...
...
@@ -563,6 +995,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct
snd_soc_codec
*
codec
=
w
->
codec
;
struct
wm_adsp
*
dsps
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm_adsp
*
dsp
=
&
dsps
[
w
->
shift
];
struct
wm_adsp_alg_region
*
alg_region
;
unsigned
int
val
;
int
ret
;
...
...
@@ -628,6 +1061,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if
(
ret
!=
0
)
goto
err
;
ret
=
wm_adsp_setup_algs
(
dsp
);
if
(
ret
!=
0
)
goto
err
;
ret
=
wm_adsp_load_coeff
(
dsp
);
if
(
ret
!=
0
)
goto
err
;
...
...
@@ -638,9 +1075,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ADSP2_CORE_ENA
|
ADSP2_START
);
if
(
ret
!=
0
)
goto
err
;
dsp
->
running
=
true
;
break
;
case
SND_SOC_DAPM_PRE_PMD
:
dsp
->
running
=
false
;
regmap_update_bits
(
dsp
->
regmap
,
dsp
->
base
+
ADSP2_CONTROL
,
ADSP2_SYS_ENA
|
ADSP2_CORE_ENA
|
ADSP2_START
,
0
);
...
...
@@ -664,6 +1105,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
"Failed to enable supply: %d
\n
"
,
ret
);
}
while
(
!
list_empty
(
&
dsp
->
alg_regions
))
{
alg_region
=
list_first_entry
(
&
dsp
->
alg_regions
,
struct
wm_adsp_alg_region
,
list
);
list_del
(
&
alg_region
->
list
);
kfree
(
alg_region
);
}
break
;
default:
...
...
@@ -693,6 +1142,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
return
ret
;
}
INIT_LIST_HEAD
(
&
adsp
->
alg_regions
);
if
(
dvfs
)
{
adsp
->
dvfs
=
devm_regulator_get
(
adsp
->
dev
,
"DCVDD"
);
if
(
IS_ERR
(
adsp
->
dvfs
))
{
...
...
sound/soc/codecs/wm_adsp.h
浏览文件 @
c331a23b
...
...
@@ -25,6 +25,13 @@ struct wm_adsp_region {
unsigned
int
base
;
};
struct
wm_adsp_alg_region
{
struct
list_head
list
;
unsigned
int
alg
;
int
type
;
unsigned
int
base
;
};
struct
wm_adsp
{
const
char
*
part
;
int
num
;
...
...
@@ -33,10 +40,18 @@ struct wm_adsp {
struct
regmap
*
regmap
;
int
base
;
int
sysclk_reg
;
int
sysclk_mask
;
int
sysclk_shift
;
struct
list_head
alg_regions
;
const
struct
wm_adsp_region
*
mem
;
int
num_mems
;
int
fw
;
bool
running
;
struct
regulator
*
dvfs
;
};
...
...
@@ -50,6 +65,9 @@ struct wm_adsp {
.shift = num, .event = wm_adsp2_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
extern
const
struct
snd_kcontrol_new
wm_adsp_fw_controls
[];
int
wm_adsp1_init
(
struct
wm_adsp
*
adsp
);
int
wm_adsp2_init
(
struct
wm_adsp
*
adsp
,
bool
dvfs
);
int
wm_adsp1_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
kcontrol
,
int
event
);
...
...
sound/soc/codecs/wmfw.h
浏览文件 @
c331a23b
...
...
@@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr {
struct
wmfw_coeff_hdr
{
u8
magic
[
4
];
__le32
len
;
__le32
ver
;
union
{
__be32
rev
;
__le32
ver
;
};
union
{
__be32
core
;
__le32
core_ver
;
};
u8
data
[];
}
__packed
;
struct
wmfw_coeff_item
{
union
{
__be32
type
;
__le32
offset
;
};
__le16
offset
;
__le16
type
;
__le32
id
;
__le32
ver
;
__le32
sr
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录