Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
c331a23b
Commit
c331a23b
authored
Feb 11, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/adsp' into asoc-next
parents
708558d3
bdaacea3
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
861 additions
and
82 deletions
+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
No files found.
drivers/base/regmap/internal.h
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment