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
nexedi
linux
Commits
941725f5
Commit
941725f5
authored
Dec 08, 2014
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/core' into asoc-next
parents
3ee3f454
c362effe
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1388 additions
and
1446 deletions
+1388
-1446
include/sound/soc-dai.h
include/sound/soc-dai.h
+0
-3
include/sound/soc-dapm.h
include/sound/soc-dapm.h
+6
-3
include/sound/soc.h
include/sound/soc.h
+11
-18
sound/soc/Makefile
sound/soc/Makefile
+1
-1
sound/soc/soc-core.c
sound/soc/soc-core.c
+17
-1062
sound/soc/soc-dapm.c
sound/soc/soc-dapm.c
+397
-358
sound/soc/soc-ops.c
sound/soc/soc-ops.c
+952
-0
sound/soc/soc-pcm.c
sound/soc/soc-pcm.c
+3
-0
sound/soc/txx9/txx9aclc.c
sound/soc/txx9/txx9aclc.c
+1
-1
No files found.
include/sound/soc-dai.h
View file @
941725f5
...
...
@@ -268,7 +268,6 @@ struct snd_soc_dai {
unsigned
int
sample_bits
;
/* parent platform/codec */
struct
snd_soc_platform
*
platform
;
struct
snd_soc_codec
*
codec
;
struct
snd_soc_component
*
component
;
...
...
@@ -276,8 +275,6 @@ struct snd_soc_dai {
unsigned
int
tx_mask
;
unsigned
int
rx_mask
;
struct
snd_soc_card
*
card
;
struct
list_head
list
;
};
...
...
include/sound/soc-dapm.h
View file @
941725f5
...
...
@@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
unsigned
int
dapm_kcontrol_get_value
(
const
struct
snd_kcontrol
*
kcontrol
);
/* Mostly internal - should not normally be used */
void
dapm_mark_
io_dirty
(
struct
snd_soc_dapm_context
*
dapm
);
void
dapm_mark_
endpoints_dirty
(
struct
snd_soc_card
*
card
);
/* dapm path query */
int
snd_soc_dapm_dai_get_connected_widgets
(
struct
snd_soc_dai
*
dai
,
int
stream
,
...
...
@@ -508,9 +508,9 @@ struct snd_soc_dapm_path {
/* status */
u32
connect
:
1
;
/* source and sink widgets are connected */
u32
walked
:
1
;
/* path has been walked */
u32
walking
:
1
;
/* path is in the process of being walked */
u32
weak
:
1
;
/* path ignored for power management */
u32
is_supply
:
1
;
/* At least one of the connected widgets is a supply */
int
(
*
connected
)(
struct
snd_soc_dapm_widget
*
source
,
struct
snd_soc_dapm_widget
*
sink
);
...
...
@@ -544,11 +544,13 @@ struct snd_soc_dapm_widget {
unsigned
char
active
:
1
;
/* active stream on DAC, ADC's */
unsigned
char
connected
:
1
;
/* connected codec pin */
unsigned
char
new
:
1
;
/* cnew complete */
unsigned
char
ext
:
1
;
/* has external widgets */
unsigned
char
force
:
1
;
/* force state */
unsigned
char
ignore_suspend
:
1
;
/* kept enabled over suspend */
unsigned
char
new_power
:
1
;
/* power from this run */
unsigned
char
power_checked
:
1
;
/* power checked this run */
unsigned
char
is_supply
:
1
;
/* Widget is a supply type widget */
unsigned
char
is_sink
:
1
;
/* Widget is a sink type widget */
unsigned
char
is_source
:
1
;
/* Widget is a source type widget */
int
subseq
;
/* sort within widget type */
int
(
*
power_check
)(
struct
snd_soc_dapm_widget
*
w
);
...
...
@@ -567,6 +569,7 @@ struct snd_soc_dapm_widget {
struct
list_head
sinks
;
/* used during DAPM updates */
struct
list_head
work_list
;
struct
list_head
power_list
;
struct
list_head
dirty
;
int
inputs
;
...
...
include/sound/soc.h
View file @
941725f5
...
...
@@ -36,6 +36,11 @@
{.reg = xreg, .rreg = xreg, .shift = shift_left, \
.rshift = shift_right, .max = xmax, .platform_max = xmax, \
.invert = xinvert, .autodisable = xautodisable})
#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .rreg = xreg, .shift = shift_left, \
.rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \
.sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable})
#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
#define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
...
...
@@ -171,11 +176,9 @@
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
.put = snd_soc_put_volsw_s8, \
.private_value = (unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .min = xmin, .max = xmax, \
.platform_max = xmax} }
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
.put = snd_soc_put_volsw, \
.private_value = SOC_DOUBLE_S_VALUE(xreg, 0, 8, xmin, xmax, 7, 0, 0) }
#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \
{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
.items = xitems, .texts = xtexts, \
...
...
@@ -541,12 +544,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
struct
snd_ctl_elem_value
*
ucontrol
);
int
snd_soc_put_volsw_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
);
int
snd_soc_info_volsw_s8
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
);
int
snd_soc_get_volsw_s8
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
);
int
snd_soc_put_volsw_s8
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
);
int
snd_soc_info_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
);
int
snd_soc_put_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
...
...
@@ -854,8 +851,6 @@ struct snd_soc_platform_driver {
int
(
*
probe
)(
struct
snd_soc_platform
*
);
int
(
*
remove
)(
struct
snd_soc_platform
*
);
int
(
*
suspend
)(
struct
snd_soc_dai
*
dai
);
int
(
*
resume
)(
struct
snd_soc_dai
*
dai
);
struct
snd_soc_component_driver
component_driver
;
/* pcm creation and destruction */
...
...
@@ -880,7 +875,7 @@ struct snd_soc_platform_driver {
struct
snd_soc_dai_link_component
{
const
char
*
name
;
const
struct
device_node
*
of_node
;
struct
device_node
*
of_node
;
const
char
*
dai_name
;
};
...
...
@@ -888,8 +883,6 @@ struct snd_soc_platform {
struct
device
*
dev
;
const
struct
snd_soc_platform_driver
*
driver
;
unsigned
int
suspended
:
1
;
/* platform is suspended */
struct
list_head
list
;
struct
snd_soc_component
component
;
...
...
@@ -984,7 +977,7 @@ struct snd_soc_codec_conf {
* DT/OF node, but not both.
*/
const
char
*
dev_name
;
const
struct
device_node
*
of_node
;
struct
device_node
*
of_node
;
/*
* optional map of kcontrol, widget and path name prefixes that are
...
...
@@ -1001,7 +994,7 @@ struct snd_soc_aux_dev {
* DT/OF node, but not both.
*/
const
char
*
codec_name
;
const
struct
device_node
*
codec_of_node
;
struct
device_node
*
codec_of_node
;
/* codec/machine specific init - e.g. add machine controls */
int
(
*
init
)(
struct
snd_soc_component
*
component
);
...
...
sound/soc/Makefile
View file @
941725f5
snd-soc-core-objs
:=
soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
snd-soc-core-objs
+=
soc-pcm.o soc-compress.o soc-io.o soc-devres.o
snd-soc-core-objs
+=
soc-pcm.o soc-compress.o soc-io.o soc-devres.o
soc-ops.o
ifneq
($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs
+=
soc-generic-dmaengine-pcm.o
...
...
sound/soc/soc-core.c
View file @
941725f5
...
...
@@ -589,17 +589,12 @@ int snd_soc_suspend(struct device *dev)
for
(
i
=
0
;
i
<
card
->
num_rtd
;
i
++
)
{
struct
snd_soc_dai
*
cpu_dai
=
card
->
rtd
[
i
].
cpu_dai
;
struct
snd_soc_platform
*
platform
=
card
->
rtd
[
i
].
platform
;
if
(
card
->
rtd
[
i
].
dai_link
->
ignore_suspend
)
continue
;
if
(
cpu_dai
->
driver
->
suspend
&&
!
cpu_dai
->
driver
->
ac97_control
)
cpu_dai
->
driver
->
suspend
(
cpu_dai
);
if
(
platform
->
driver
->
suspend
&&
!
platform
->
suspended
)
{
platform
->
driver
->
suspend
(
cpu_dai
);
platform
->
suspended
=
1
;
}
}
/* close any waiting streams and save state */
...
...
@@ -626,8 +621,8 @@ int snd_soc_suspend(struct device *dev)
SND_SOC_DAPM_STREAM_SUSPEND
);
}
/* Recheck all
analogue paths too
*/
dapm_mark_
io_dirty
(
&
card
->
dapm
);
/* Recheck all
endpoints too, their state is affected by suspend
*/
dapm_mark_
endpoints_dirty
(
card
);
snd_soc_dapm_sync
(
&
card
->
dapm
);
/* suspend all CODECs */
...
...
@@ -771,17 +766,12 @@ static void soc_resume_deferred(struct work_struct *work)
for
(
i
=
0
;
i
<
card
->
num_rtd
;
i
++
)
{
struct
snd_soc_dai
*
cpu_dai
=
card
->
rtd
[
i
].
cpu_dai
;
struct
snd_soc_platform
*
platform
=
card
->
rtd
[
i
].
platform
;
if
(
card
->
rtd
[
i
].
dai_link
->
ignore_suspend
)
continue
;
if
(
cpu_dai
->
driver
->
resume
&&
!
cpu_dai
->
driver
->
ac97_control
)
cpu_dai
->
driver
->
resume
(
cpu_dai
);
if
(
platform
->
driver
->
resume
&&
platform
->
suspended
)
{
platform
->
driver
->
resume
(
cpu_dai
);
platform
->
suspended
=
0
;
}
}
if
(
card
->
resume_post
)
...
...
@@ -792,8 +782,8 @@ static void soc_resume_deferred(struct work_struct *work)
/* userspace can access us now we are back as we were before */
snd_power_change_state
(
card
->
snd_card
,
SNDRV_CTL_POWER_D0
);
/* Recheck all
analogue paths too
*/
dapm_mark_
io_dirty
(
&
card
->
dapm
);
/* Recheck all
endpoints too, their state is affected by suspend
*/
dapm_mark_
endpoints_dirty
(
card
);
snd_soc_dapm_sync
(
&
card
->
dapm
);
}
...
...
@@ -1247,25 +1237,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
return
0
;
}
static
int
soc_probe_codec_dai
(
struct
snd_soc_card
*
card
,
struct
snd_soc_dai
*
codec_dai
,
int
order
)
static
int
soc_probe_dai
(
struct
snd_soc_dai
*
dai
,
int
order
)
{
int
ret
;
if
(
!
codec_dai
->
probed
&&
codec_
dai
->
driver
->
probe_order
==
order
)
{
if
(
codec_
dai
->
driver
->
probe
)
{
ret
=
codec_dai
->
driver
->
probe
(
codec_
dai
);
if
(
!
dai
->
probed
&&
dai
->
driver
->
probe_order
==
order
)
{
if
(
dai
->
driver
->
probe
)
{
ret
=
dai
->
driver
->
probe
(
dai
);
if
(
ret
<
0
)
{
dev_err
(
codec_
dai
->
dev
,
"ASoC: failed to probe
CODEC
DAI %s: %d
\n
"
,
codec_
dai
->
name
,
ret
);
dev_err
(
dai
->
dev
,
"ASoC: failed to probe DAI %s: %d
\n
"
,
dai
->
name
,
ret
);
return
ret
;
}
}
/* mark codec_dai as probed and add to card dai list */
codec_dai
->
probed
=
1
;
dai
->
probed
=
1
;
}
return
0
;
...
...
@@ -1315,40 +1302,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{
struct
snd_soc_dai_link
*
dai_link
=
&
card
->
dai_link
[
num
];
struct
snd_soc_pcm_runtime
*
rtd
=
&
card
->
rtd
[
num
];
struct
snd_soc_platform
*
platform
=
rtd
->
platform
;
struct
snd_soc_dai
*
cpu_dai
=
rtd
->
cpu_dai
;
int
i
,
ret
;
dev_dbg
(
card
->
dev
,
"ASoC: probe %s dai link %d late %d
\n
"
,
card
->
name
,
num
,
order
);
/* config components */
cpu_dai
->
platform
=
platform
;
cpu_dai
->
card
=
card
;
for
(
i
=
0
;
i
<
rtd
->
num_codecs
;
i
++
)
rtd
->
codec_dais
[
i
]
->
card
=
card
;
/* set default power off timeout */
rtd
->
pmdown_time
=
pmdown_time
;
/* probe the cpu_dai */
if
(
!
cpu_dai
->
probed
&&
cpu_dai
->
driver
->
probe_order
==
order
)
{
if
(
cpu_dai
->
driver
->
probe
)
{
ret
=
cpu_dai
->
driver
->
probe
(
cpu_dai
);
if
(
ret
<
0
)
{
dev_err
(
cpu_dai
->
dev
,
"ASoC: failed to probe CPU DAI %s: %d
\n
"
,
cpu_dai
->
name
,
ret
);
ret
=
soc_probe_dai
(
cpu_dai
,
order
);
if
(
ret
)
return
ret
;
}
}
cpu_dai
->
probed
=
1
;
}
/* probe the CODEC DAI */
for
(
i
=
0
;
i
<
rtd
->
num_codecs
;
i
++
)
{
ret
=
soc_probe_
codec_dai
(
card
,
rtd
->
codec_dais
[
i
],
order
);
ret
=
soc_probe_
dai
(
rtd
->
codec_dais
[
i
],
order
);
if
(
ret
)
return
ret
;
}
...
...
@@ -2322,1027 +2291,13 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
int
snd_soc_add_dai_controls
(
struct
snd_soc_dai
*
dai
,
const
struct
snd_kcontrol_new
*
controls
,
int
num_controls
)
{
struct
snd_card
*
card
=
dai
->
card
->
snd_card
;
struct
snd_card
*
card
=
dai
->
c
omponent
->
c
ard
->
snd_card
;
return
snd_soc_add_controls
(
card
,
dai
->
dev
,
controls
,
num_controls
,
NULL
,
dai
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_add_dai_controls
);
/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a double enumerated
* mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_info_enum_double
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_ENUMERATED
;
uinfo
->
count
=
e
->
shift_l
==
e
->
shift_r
?
1
:
2
;
uinfo
->
value
.
enumerated
.
items
=
e
->
items
;
if
(
uinfo
->
value
.
enumerated
.
item
>=
e
->
items
)
uinfo
->
value
.
enumerated
.
item
=
e
->
items
-
1
;
strlcpy
(
uinfo
->
value
.
enumerated
.
name
,
e
->
texts
[
uinfo
->
value
.
enumerated
.
item
],
sizeof
(
uinfo
->
value
.
enumerated
.
name
));
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_enum_double
);
/**
* snd_soc_get_enum_double - enumerated double mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int
snd_soc_get_enum_double
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
unsigned
int
val
,
item
;
unsigned
int
reg_val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
e
->
reg
,
&
reg_val
);
if
(
ret
)
return
ret
;
val
=
(
reg_val
>>
e
->
shift_l
)
&
e
->
mask
;
item
=
snd_soc_enum_val_to_item
(
e
,
val
);
ucontrol
->
value
.
enumerated
.
item
[
0
]
=
item
;
if
(
e
->
shift_l
!=
e
->
shift_r
)
{
val
=
(
reg_val
>>
e
->
shift_l
)
&
e
->
mask
;
item
=
snd_soc_enum_val_to_item
(
e
,
val
);
ucontrol
->
value
.
enumerated
.
item
[
1
]
=
item
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_enum_double
);
/**
* snd_soc_put_enum_double - enumerated double mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int
snd_soc_put_enum_double
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
unsigned
int
*
item
=
ucontrol
->
value
.
enumerated
.
item
;
unsigned
int
val
;
unsigned
int
mask
;
if
(
item
[
0
]
>=
e
->
items
)
return
-
EINVAL
;
val
=
snd_soc_enum_item_to_val
(
e
,
item
[
0
])
<<
e
->
shift_l
;
mask
=
e
->
mask
<<
e
->
shift_l
;
if
(
e
->
shift_l
!=
e
->
shift_r
)
{
if
(
item
[
1
]
>=
e
->
items
)
return
-
EINVAL
;
val
|=
snd_soc_enum_item_to_val
(
e
,
item
[
1
])
<<
e
->
shift_r
;
mask
|=
e
->
mask
<<
e
->
shift_r
;
}
return
snd_soc_component_update_bits
(
component
,
e
->
reg
,
mask
,
val
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_enum_double
);
/**
* snd_soc_read_signed - Read a codec register and interprete as signed value
* @component: component
* @reg: Register to read
* @mask: Mask to use after shifting the register value
* @shift: Right shift of register value
* @sign_bit: Bit that describes if a number is negative or not.
* @signed_val: Pointer to where the read value should be stored
*
* This functions reads a codec register. The register value is shifted right
* by 'shift' bits and masked with the given 'mask'. Afterwards it translates
* the given registervalue into a signed integer if sign_bit is non-zero.
*
* Returns 0 on sucess, otherwise an error value
*/
static
int
snd_soc_read_signed
(
struct
snd_soc_component
*
component
,
unsigned
int
reg
,
unsigned
int
mask
,
unsigned
int
shift
,
unsigned
int
sign_bit
,
int
*
signed_val
)
{
int
ret
;
unsigned
int
val
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
<
0
)
return
ret
;
val
=
(
val
>>
shift
)
&
mask
;
if
(
!
sign_bit
)
{
*
signed_val
=
val
;
return
0
;
}
/* non-negative number */
if
(
!
(
val
&
BIT
(
sign_bit
)))
{
*
signed_val
=
val
;
return
0
;
}
ret
=
val
;
/*
* The register most probably does not contain a full-sized int.
* Instead we have an arbitrary number of bits in a signed
* representation which has to be translated into a full-sized int.
* This is done by filling up all bits above the sign-bit.
*/
ret
|=
~
((
int
)(
BIT
(
sign_bit
)
-
1
));
*
signed_val
=
ret
;
return
0
;
}
/**
* snd_soc_info_volsw - single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a single mixer control, or a double
* mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_info_volsw
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
int
platform_max
;
if
(
!
mc
->
platform_max
)
mc
->
platform_max
=
mc
->
max
;
platform_max
=
mc
->
platform_max
;
if
(
platform_max
==
1
&&
!
strstr
(
kcontrol
->
id
.
name
,
" Volume"
))
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BOOLEAN
;
else
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
snd_soc_volsw_is_stereo
(
mc
)
?
2
:
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
platform_max
-
mc
->
min
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_volsw
);
/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
int
sign_bit
=
mc
->
sign_bit
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
int
val
;
int
ret
;
if
(
sign_bit
)
mask
=
BIT
(
sign_bit
+
1
)
-
1
;
ret
=
snd_soc_read_signed
(
component
,
reg
,
mask
,
shift
,
sign_bit
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
val
-
min
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
0
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
0
];
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
if
(
reg
==
reg2
)
ret
=
snd_soc_read_signed
(
component
,
reg
,
mask
,
rshift
,
sign_bit
,
&
val
);
else
ret
=
snd_soc_read_signed
(
component
,
reg2
,
mask
,
shift
,
sign_bit
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
val
-
min
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
1
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
1
];
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw
);
/**
* snd_soc_put_volsw - single mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
unsigned
int
sign_bit
=
mc
->
sign_bit
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
int
err
;
bool
type_2r
=
false
;
unsigned
int
val2
=
0
;
unsigned
int
val
,
val_mask
;
if
(
sign_bit
)
mask
=
BIT
(
sign_bit
+
1
)
-
1
;
val
=
((
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
mask
);
if
(
invert
)
val
=
max
-
val
;
val_mask
=
mask
<<
shift
;
val
=
val
<<
shift
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
val2
=
((
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
mask
);
if
(
invert
)
val2
=
max
-
val2
;
if
(
reg
==
reg2
)
{
val_mask
|=
mask
<<
rshift
;
val
|=
val2
<<
rshift
;
}
else
{
val2
=
val2
<<
shift
;
type_2r
=
true
;
}
}
err
=
snd_soc_component_update_bits
(
component
,
reg
,
val_mask
,
val
);
if
(
err
<
0
)
return
err
;
if
(
type_2r
)
err
=
snd_soc_component_update_bits
(
component
,
reg2
,
val_mask
,
val2
);
return
err
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw
);
/**
* snd_soc_get_volsw_sx - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
int
mask
=
(
1
<<
(
fls
(
min
+
max
)
-
1
))
-
1
;
unsigned
int
val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
<
0
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
((
val
>>
shift
)
-
min
)
&
mask
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
ret
=
snd_soc_component_read
(
component
,
reg2
,
&
val
);
if
(
ret
<
0
)
return
ret
;
val
=
((
val
>>
rshift
)
-
min
)
&
mask
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
val
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw_sx
);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
int
mask
=
(
1
<<
(
fls
(
min
+
max
)
-
1
))
-
1
;
int
err
=
0
;
unsigned
int
val
,
val_mask
,
val2
=
0
;
val_mask
=
mask
<<
shift
;
val
=
(
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
mask
;
val
=
val
<<
shift
;
err
=
snd_soc_component_update_bits
(
component
,
reg
,
val_mask
,
val
);
if
(
err
<
0
)
return
err
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
val_mask
=
mask
<<
rshift
;
val2
=
(
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
mask
;
val2
=
val2
<<
rshift
;
err
=
snd_soc_component_update_bits
(
component
,
reg2
,
val_mask
,
val2
);
}
return
err
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw_sx
);
/**
* snd_soc_info_volsw_s8 - signed mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a signed mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_info_volsw_s8
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
int
platform_max
;
int
min
=
mc
->
min
;
if
(
!
mc
->
platform_max
)
mc
->
platform_max
=
mc
->
max
;
platform_max
=
mc
->
platform_max
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
2
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
platform_max
-
min
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_volsw_s8
);
/**
* snd_soc_get_volsw_s8 - signed mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a signed mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw_s8
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
val
;
int
min
=
mc
->
min
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
((
signed
char
)(
val
&
0xff
))
-
min
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
((
signed
char
)((
val
>>
8
)
&
0xff
))
-
min
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw_s8
);
/**
* snd_soc_put_volsw_sgn - signed mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a signed mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw_s8
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
unsigned
int
reg
=
mc
->
reg
;
int
min
=
mc
->
min
;
unsigned
int
val
;
val
=
(
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
0xff
;
val
|=
((
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
0xff
)
<<
8
;
return
snd_soc_component_update_bits
(
component
,
reg
,
0xffff
,
val
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw_s8
);
/**
* snd_soc_info_volsw_range - single mixer info callback with range.
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information, within a range, about a single
* mixer control.
*
* returns 0 for success.
*/
int
snd_soc_info_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
int
platform_max
;
int
min
=
mc
->
min
;
if
(
!
mc
->
platform_max
)
mc
->
platform_max
=
mc
->
max
;
platform_max
=
mc
->
platform_max
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
snd_soc_volsw_is_stereo
(
mc
)
?
2
:
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
platform_max
-
min
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_volsw_range
);
/**
* snd_soc_put_volsw_range - single mixer put value callback with range.
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value, within a range, for a single mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
rreg
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
int
min
=
mc
->
min
;
int
max
=
mc
->
max
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
int
val
,
val_mask
;
int
ret
;
if
(
invert
)
val
=
(
max
-
ucontrol
->
value
.
integer
.
value
[
0
])
&
mask
;
else
val
=
((
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
mask
);
val_mask
=
mask
<<
shift
;
val
=
val
<<
shift
;
ret
=
snd_soc_component_update_bits
(
component
,
reg
,
val_mask
,
val
);
if
(
ret
<
0
)
return
ret
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
if
(
invert
)
val
=
(
max
-
ucontrol
->
value
.
integer
.
value
[
1
])
&
mask
;
else
val
=
((
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
mask
);
val_mask
=
mask
<<
shift
;
val
=
val
<<
shift
;
ret
=
snd_soc_component_update_bits
(
component
,
rreg
,
val_mask
,
val
);
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw_range
);
/**
* snd_soc_get_volsw_range - single mixer get callback with range
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value, within a range, of a single mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
rreg
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
int
min
=
mc
->
min
;
int
max
=
mc
->
max
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
int
val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
(
val
>>
shift
)
&
mask
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
0
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
0
];
else
ucontrol
->
value
.
integer
.
value
[
0
]
=
ucontrol
->
value
.
integer
.
value
[
0
]
-
min
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
ret
=
snd_soc_component_read
(
component
,
rreg
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
(
val
>>
shift
)
&
mask
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
1
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
1
];
else
ucontrol
->
value
.
integer
.
value
[
1
]
=
ucontrol
->
value
.
integer
.
value
[
1
]
-
min
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw_range
);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
* @codec: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
int
snd_soc_limit_volume
(
struct
snd_soc_codec
*
codec
,
const
char
*
name
,
int
max
)
{
struct
snd_card
*
card
=
codec
->
component
.
card
->
snd_card
;
struct
snd_kcontrol
*
kctl
;
struct
soc_mixer_control
*
mc
;
int
found
=
0
;
int
ret
=
-
EINVAL
;
/* Sanity check for name and max */
if
(
unlikely
(
!
name
||
max
<=
0
))
return
-
EINVAL
;
list_for_each_entry
(
kctl
,
&
card
->
controls
,
list
)
{
if
(
!
strncmp
(
kctl
->
id
.
name
,
name
,
sizeof
(
kctl
->
id
.
name
)))
{
found
=
1
;
break
;
}
}
if
(
found
)
{
mc
=
(
struct
soc_mixer_control
*
)
kctl
->
private_value
;
if
(
max
<=
mc
->
max
)
{
mc
->
platform_max
=
max
;
ret
=
0
;
}
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_limit_volume
);
int
snd_soc_bytes_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_bytes
*
params
=
(
void
*
)
kcontrol
->
private_value
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BYTES
;
uinfo
->
count
=
params
->
num_regs
*
component
->
val_bytes
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_info
);
int
snd_soc_bytes_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_bytes
*
params
=
(
void
*
)
kcontrol
->
private_value
;
int
ret
;
if
(
component
->
regmap
)
ret
=
regmap_raw_read
(
component
->
regmap
,
params
->
base
,
ucontrol
->
value
.
bytes
.
data
,
params
->
num_regs
*
component
->
val_bytes
);
else
ret
=
-
EINVAL
;
/* Hide any masked bytes to ensure consistent data reporting */
if
(
ret
==
0
&&
params
->
mask
)
{
switch
(
component
->
val_bytes
)
{
case
1
:
ucontrol
->
value
.
bytes
.
data
[
0
]
&=
~
params
->
mask
;
break
;
case
2
:
((
u16
*
)(
&
ucontrol
->
value
.
bytes
.
data
))[
0
]
&=
cpu_to_be16
(
~
params
->
mask
);
break
;
case
4
:
((
u32
*
)(
&
ucontrol
->
value
.
bytes
.
data
))[
0
]
&=
cpu_to_be32
(
~
params
->
mask
);
break
;
default:
return
-
EINVAL
;
}
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_get
);
int
snd_soc_bytes_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_bytes
*
params
=
(
void
*
)
kcontrol
->
private_value
;
int
ret
,
len
;
unsigned
int
val
,
mask
;
void
*
data
;
if
(
!
component
->
regmap
||
!
params
->
num_regs
)
return
-
EINVAL
;
len
=
params
->
num_regs
*
component
->
val_bytes
;
data
=
kmemdup
(
ucontrol
->
value
.
bytes
.
data
,
len
,
GFP_KERNEL
|
GFP_DMA
);
if
(
!
data
)
return
-
ENOMEM
;
/*
* If we've got a mask then we need to preserve the register
* bits. We shouldn't modify the incoming data so take a
* copy.
*/
if
(
params
->
mask
)
{
ret
=
regmap_read
(
component
->
regmap
,
params
->
base
,
&
val
);
if
(
ret
!=
0
)
goto
out
;
val
&=
params
->
mask
;
switch
(
component
->
val_bytes
)
{
case
1
:
((
u8
*
)
data
)[
0
]
&=
~
params
->
mask
;
((
u8
*
)
data
)[
0
]
|=
val
;
break
;
case
2
:
mask
=
~
params
->
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
mask
,
&
mask
);
if
(
ret
!=
0
)
goto
out
;
((
u16
*
)
data
)[
0
]
&=
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
val
,
&
val
);
if
(
ret
!=
0
)
goto
out
;
((
u16
*
)
data
)[
0
]
|=
val
;
break
;
case
4
:
mask
=
~
params
->
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
mask
,
&
mask
);
if
(
ret
!=
0
)
goto
out
;
((
u32
*
)
data
)[
0
]
&=
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
val
,
&
val
);
if
(
ret
!=
0
)
goto
out
;
((
u32
*
)
data
)[
0
]
|=
val
;
break
;
default:
ret
=
-
EINVAL
;
goto
out
;
}
}
ret
=
regmap_raw_write
(
component
->
regmap
,
params
->
base
,
data
,
len
);
out:
kfree
(
data
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_put
);
int
snd_soc_bytes_info_ext
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
ucontrol
)
{
struct
soc_bytes_ext
*
params
=
(
void
*
)
kcontrol
->
private_value
;
ucontrol
->
type
=
SNDRV_CTL_ELEM_TYPE_BYTES
;
ucontrol
->
count
=
params
->
max
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_info_ext
);
int
snd_soc_bytes_tlv_callback
(
struct
snd_kcontrol
*
kcontrol
,
int
op_flag
,
unsigned
int
size
,
unsigned
int
__user
*
tlv
)
{
struct
soc_bytes_ext
*
params
=
(
void
*
)
kcontrol
->
private_value
;
unsigned
int
count
=
size
<
params
->
max
?
size
:
params
->
max
;
int
ret
=
-
ENXIO
;
switch
(
op_flag
)
{
case
SNDRV_CTL_TLV_OP_READ
:
if
(
params
->
get
)
ret
=
params
->
get
(
tlv
,
count
);
break
;
case
SNDRV_CTL_TLV_OP_WRITE
:
if
(
params
->
put
)
ret
=
params
->
put
(
tlv
,
count
);
break
;
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_tlv_callback
);
/**
* snd_soc_info_xr_sx - signed multi register info callback
* @kcontrol: mreg control
* @uinfo: control element information
*
* Callback to provide information of a control that can
* span multiple codec registers which together
* forms a single signed value in a MSB/LSB manner.
*
* Returns 0 for success.
*/
int
snd_soc_info_xr_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mreg_control
*
mc
=
(
struct
soc_mreg_control
*
)
kcontrol
->
private_value
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
mc
->
min
;
uinfo
->
value
.
integer
.
max
=
mc
->
max
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_xr_sx
);
/**
* snd_soc_get_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to get the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int
snd_soc_get_xr_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mreg_control
*
mc
=
(
struct
soc_mreg_control
*
)
kcontrol
->
private_value
;
unsigned
int
regbase
=
mc
->
regbase
;
unsigned
int
regcount
=
mc
->
regcount
;
unsigned
int
regwshift
=
component
->
val_bytes
*
BITS_PER_BYTE
;
unsigned
int
regwmask
=
(
1
<<
regwshift
)
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
long
mask
=
(
1UL
<<
mc
->
nbits
)
-
1
;
long
min
=
mc
->
min
;
long
max
=
mc
->
max
;
long
val
=
0
;
unsigned
int
regval
;
unsigned
int
i
;
int
ret
;
for
(
i
=
0
;
i
<
regcount
;
i
++
)
{
ret
=
snd_soc_component_read
(
component
,
regbase
+
i
,
&
regval
);
if
(
ret
)
return
ret
;
val
|=
(
regval
&
regwmask
)
<<
(
regwshift
*
(
regcount
-
i
-
1
));
}
val
&=
mask
;
if
(
min
<
0
&&
val
>
max
)
val
|=
~
mask
;
if
(
invert
)
val
=
max
-
val
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
val
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_xr_sx
);
/**
* snd_soc_put_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to set the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int
snd_soc_put_xr_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mreg_control
*
mc
=
(
struct
soc_mreg_control
*
)
kcontrol
->
private_value
;
unsigned
int
regbase
=
mc
->
regbase
;
unsigned
int
regcount
=
mc
->
regcount
;
unsigned
int
regwshift
=
component
->
val_bytes
*
BITS_PER_BYTE
;
unsigned
int
regwmask
=
(
1
<<
regwshift
)
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
long
mask
=
(
1UL
<<
mc
->
nbits
)
-
1
;
long
max
=
mc
->
max
;
long
val
=
ucontrol
->
value
.
integer
.
value
[
0
];
unsigned
int
i
,
regval
,
regmask
;
int
err
;
if
(
invert
)
val
=
max
-
val
;
val
&=
mask
;
for
(
i
=
0
;
i
<
regcount
;
i
++
)
{
regval
=
(
val
>>
(
regwshift
*
(
regcount
-
i
-
1
)))
&
regwmask
;
regmask
=
(
mask
>>
(
regwshift
*
(
regcount
-
i
-
1
)))
&
regwmask
;
err
=
snd_soc_component_update_bits
(
component
,
regbase
+
i
,
regmask
,
regval
);
if
(
err
<
0
)
return
err
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_xr_sx
);
/**
* snd_soc_get_strobe - strobe get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback get the value of a strobe mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_get_strobe
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
mask
=
1
<<
shift
;
unsigned
int
invert
=
mc
->
invert
!=
0
;
unsigned
int
val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
)
return
ret
;
val
&=
mask
;
if
(
shift
!=
0
&&
val
!=
0
)
val
=
val
>>
shift
;
ucontrol
->
value
.
enumerated
.
item
[
0
]
=
val
^
invert
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_strobe
);
/**
* snd_soc_put_strobe - strobe put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback strobe a register bit to high then low (or the inverse)
* in one pass of a single mixer enum control.
*
* Returns 1 for success.
*/
int
snd_soc_put_strobe
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
mask
=
1
<<
shift
;
unsigned
int
invert
=
mc
->
invert
!=
0
;
unsigned
int
strobe
=
ucontrol
->
value
.
enumerated
.
item
[
0
]
!=
0
;
unsigned
int
val1
=
(
strobe
^
invert
)
?
mask
:
0
;
unsigned
int
val2
=
(
strobe
^
invert
)
?
0
:
mask
;
int
err
;
err
=
snd_soc_component_update_bits
(
component
,
reg
,
mask
,
val1
);
if
(
err
<
0
)
return
err
;
return
snd_soc_component_update_bits
(
component
,
reg
,
mask
,
val2
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_strobe
);
/**
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
* @dai: DAI
...
...
sound/soc/soc-dapm.c
View file @
941725f5
...
...
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
}
}
void
dapm_mark_io_dirty
(
struct
snd_soc_dapm_context
*
dapm
)
/*
* dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
* paths
* @w: The widget for which to invalidate the cached number of input paths
*
* The function resets the cached number of inputs for the specified widget and
* all widgets that can be reached via outgoing paths from the widget.
*
* This function must be called if the number of input paths for a widget might
* have changed. E.g. if the source state of a widget changes or a path is added
* or activated with the widget as the sink.
*/
static
void
dapm_widget_invalidate_input_paths
(
struct
snd_soc_dapm_widget
*
w
)
{
struct
snd_soc_dapm_widget
*
sink
;
struct
snd_soc_dapm_path
*
p
;
LIST_HEAD
(
list
);
dapm_assert_locked
(
w
->
dapm
);
if
(
w
->
inputs
==
-
1
)
return
;
w
->
inputs
=
-
1
;
list_add_tail
(
&
w
->
work_list
,
&
list
);
list_for_each_entry
(
w
,
&
list
,
work_list
)
{
list_for_each_entry
(
p
,
&
w
->
sinks
,
list_source
)
{
if
(
p
->
is_supply
||
p
->
weak
||
!
p
->
connect
)
continue
;
sink
=
p
->
sink
;
if
(
sink
->
inputs
!=
-
1
)
{
sink
->
inputs
=
-
1
;
list_add_tail
(
&
sink
->
work_list
,
&
list
);
}
}
}
}
/*
* dapm_widget_invalidate_output_paths() - Invalidate the cached number of
* output paths
* @w: The widget for which to invalidate the cached number of output paths
*
* Resets the cached number of outputs for the specified widget and all widgets
* that can be reached via incoming paths from the widget.
*
* This function must be called if the number of output paths for a widget might
* have changed. E.g. if the sink state of a widget changes or a path is added
* or activated with the widget as the source.
*/
static
void
dapm_widget_invalidate_output_paths
(
struct
snd_soc_dapm_widget
*
w
)
{
struct
snd_soc_dapm_widget
*
source
;
struct
snd_soc_dapm_path
*
p
;
LIST_HEAD
(
list
);
dapm_assert_locked
(
w
->
dapm
);
if
(
w
->
outputs
==
-
1
)
return
;
w
->
outputs
=
-
1
;
list_add_tail
(
&
w
->
work_list
,
&
list
);
list_for_each_entry
(
w
,
&
list
,
work_list
)
{
list_for_each_entry
(
p
,
&
w
->
sources
,
list_sink
)
{
if
(
p
->
is_supply
||
p
->
weak
||
!
p
->
connect
)
continue
;
source
=
p
->
source
;
if
(
source
->
outputs
!=
-
1
)
{
source
->
outputs
=
-
1
;
list_add_tail
(
&
source
->
work_list
,
&
list
);
}
}
}
}
/*
* dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
* for the widgets connected to a path
* @p: The path to invalidate
*
* Resets the cached number of inputs for the sink of the path and the cached
* number of outputs for the source of the path.
*
* This function must be called when a path is added, removed or the connected
* state changes.
*/
static
void
dapm_path_invalidate
(
struct
snd_soc_dapm_path
*
p
)
{
/*
* Weak paths or supply paths do not influence the number of input or
* output paths of their neighbors.
*/
if
(
p
->
weak
||
p
->
is_supply
)
return
;
/*
* The number of connected endpoints is the sum of the number of
* connected endpoints of all neighbors. If a node with 0 connected
* endpoints is either connected or disconnected that sum won't change,
* so there is no need to re-check the path.
*/
if
(
p
->
source
->
inputs
!=
0
)
dapm_widget_invalidate_input_paths
(
p
->
sink
);
if
(
p
->
sink
->
outputs
!=
0
)
dapm_widget_invalidate_output_paths
(
p
->
source
);
}
void
dapm_mark_endpoints_dirty
(
struct
snd_soc_card
*
card
)
{
struct
snd_soc_card
*
card
=
dapm
->
card
;
struct
snd_soc_dapm_widget
*
w
;
mutex_lock
(
&
card
->
dapm_mutex
);
list_for_each_entry
(
w
,
&
card
->
widgets
,
list
)
{
switch
(
w
->
id
)
{
case
snd_soc_dapm_input
:
case
snd_soc_dapm_output
:
dapm_mark_dirty
(
w
,
"Rechecking inputs and outputs"
);
break
;
default:
break
;
if
(
w
->
is_sink
||
w
->
is_source
)
{
dapm_mark_dirty
(
w
,
"Rechecking endpoints"
);
if
(
w
->
is_sink
)
dapm_widget_invalidate_output_paths
(
w
);
if
(
w
->
is_source
)
dapm_widget_invalidate_input_paths
(
w
);
}
}
mutex_unlock
(
&
card
->
dapm_mutex
);
}
EXPORT_SYMBOL_GPL
(
dapm_mark_
io
_dirty
);
EXPORT_SYMBOL_GPL
(
dapm_mark_
endpoints
_dirty
);
/* create a new dapm widget */
static
inline
struct
snd_soc_dapm_widget
*
dapm_cnew_widget
(
...
...
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
list_for_each_entry
(
w
,
&
card
->
widgets
,
list
)
{
w
->
new_power
=
w
->
power
;
w
->
power_checked
=
false
;
w
->
inputs
=
-
1
;
w
->
outputs
=
-
1
;
}
}
...
...
@@ -469,10 +575,9 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
/* connect mux widget to its interconnecting audio paths */
static
int
dapm_connect_mux
(
struct
snd_soc_dapm_context
*
dapm
,
struct
snd_soc_dapm_widget
*
src
,
struct
snd_soc_dapm_widget
*
dest
,
struct
snd_soc_dapm_path
*
path
,
const
char
*
control_name
,
const
struct
snd_kcontrol_new
*
kcontrol
)
struct
snd_soc_dapm_path
*
path
,
const
char
*
control_name
)
{
const
struct
snd_kcontrol_new
*
kcontrol
=
&
path
->
sink
->
kcontrol_news
[
0
];
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
unsigned
int
val
,
item
;
int
i
;
...
...
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
for
(
i
=
0
;
i
<
e
->
items
;
i
++
)
{
if
(
!
(
strcmp
(
control_name
,
e
->
texts
[
i
])))
{
list_add
(
&
path
->
list
,
&
dapm
->
card
->
paths
);
list_add
(
&
path
->
list_sink
,
&
dest
->
sources
);
list_add
(
&
path
->
list_source
,
&
src
->
sinks
);
path
->
name
=
(
char
*
)
e
->
texts
[
i
];
path
->
name
=
e
->
texts
[
i
];
if
(
i
==
item
)
path
->
connect
=
1
;
else
...
...
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
}
/* set up initial codec paths */
static
void
dapm_set_mixer_path_status
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_soc_dapm_path
*
p
,
int
i
)
static
void
dapm_set_mixer_path_status
(
struct
snd_soc_dapm_path
*
p
,
int
i
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
w
->
kcontrol_news
[
i
].
private_value
;
p
->
sink
->
kcontrol_news
[
i
].
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
max
=
mc
->
max
;
...
...
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
unsigned
int
val
;
if
(
reg
!=
SND_SOC_NOPM
)
{
soc_dapm_read
(
w
->
dapm
,
reg
,
&
val
);
soc_dapm_read
(
p
->
sink
->
dapm
,
reg
,
&
val
);
val
=
(
val
>>
shift
)
&
mask
;
if
(
invert
)
val
=
max
-
val
;
...
...
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
/* connect mixer widget to its interconnecting audio paths */
static
int
dapm_connect_mixer
(
struct
snd_soc_dapm_context
*
dapm
,
struct
snd_soc_dapm_widget
*
src
,
struct
snd_soc_dapm_widget
*
dest
,
struct
snd_soc_dapm_path
*
path
,
const
char
*
control_name
)
{
int
i
;
/* search for mixer kcontrol */
for
(
i
=
0
;
i
<
dest
->
num_kcontrols
;
i
++
)
{
if
(
!
strcmp
(
control_name
,
dest
->
kcontrol_news
[
i
].
name
))
{
list_add
(
&
path
->
list
,
&
dapm
->
card
->
paths
);
list_add
(
&
path
->
list_sink
,
&
dest
->
sources
);
list_add
(
&
path
->
list_source
,
&
src
->
sinks
);
path
->
name
=
dest
->
kcontrol_news
[
i
].
name
;
dapm_set_mixer_path_status
(
dest
,
path
,
i
);
for
(
i
=
0
;
i
<
path
->
sink
->
num_kcontrols
;
i
++
)
{
if
(
!
strcmp
(
control_name
,
path
->
sink
->
kcontrol_news
[
i
].
name
))
{
path
->
name
=
path
->
sink
->
kcontrol_news
[
i
].
name
;
dapm_set_mixer_path_status
(
path
,
i
);
return
0
;
}
}
...
...
@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
if
(
ret
<
0
)
return
ret
;
list_for_each_entry
(
path
,
&
w
->
sources
,
list_sink
)
list_for_each_entry
(
path
,
&
w
->
sources
,
list_sink
)
{
if
(
path
->
name
)
dapm_kcontrol_add_path
(
w
->
kcontrols
[
0
],
path
);
}
return
0
;
}
...
...
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
return
0
;
}
/* reset 'walked' bit for each dapm path */
static
void
dapm_clear_walk_output
(
struct
snd_soc_dapm_context
*
dapm
,
struct
list_head
*
sink
)
{
struct
snd_soc_dapm_path
*
p
;
list_for_each_entry
(
p
,
sink
,
list_source
)
{
if
(
p
->
walked
)
{
p
->
walked
=
0
;
dapm_clear_walk_output
(
dapm
,
&
p
->
sink
->
sinks
);
}
}
}
static
void
dapm_clear_walk_input
(
struct
snd_soc_dapm_context
*
dapm
,
struct
list_head
*
source
)
{
struct
snd_soc_dapm_path
*
p
;
list_for_each_entry
(
p
,
source
,
list_sink
)
{
if
(
p
->
walked
)
{
p
->
walked
=
0
;
dapm_clear_walk_input
(
dapm
,
&
p
->
source
->
sources
);
}
}
}
/* We implement power down on suspend by checking the power state of
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
...
...
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT
(
widget
,
path_checks
);
switch
(
widget
->
id
)
{
case
snd_soc_dapm_supply
:
case
snd_soc_dapm_regulator_supply
:
case
snd_soc_dapm_clock_supply
:
case
snd_soc_dapm_kcontrol
:
return
0
;
default:
break
;
}
switch
(
widget
->
id
)
{
case
snd_soc_dapm_adc
:
case
snd_soc_dapm_aif_out
:
case
snd_soc_dapm_dai_out
:
if
(
widget
->
active
)
{
widget
->
outputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
outputs
;
}
default:
break
;
}
if
(
widget
->
connected
)
{
/* connected pin ? */
if
(
widget
->
id
==
snd_soc_dapm_output
&&
!
widget
->
ext
)
{
widget
->
outputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
outputs
;
}
/* connected jack or spk ? */
if
(
widget
->
id
==
snd_soc_dapm_hp
||
widget
->
id
==
snd_soc_dapm_spk
||
(
widget
->
id
==
snd_soc_dapm_line
&&
!
list_empty
(
&
widget
->
sources
)))
{
if
(
widget
->
is_sink
&&
widget
->
connected
)
{
widget
->
outputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
outputs
;
}
}
list_for_each_entry
(
path
,
&
widget
->
sinks
,
list_source
)
{
DAPM_UPDATE_STAT
(
widget
,
neighbour_checks
);
if
(
path
->
weak
)
if
(
path
->
weak
||
path
->
is_supply
)
continue
;
if
(
path
->
walking
)
return
1
;
if
(
path
->
walked
)
continue
;
trace_snd_soc_dapm_output_path
(
widget
,
path
);
if
(
path
->
sink
&&
path
->
connect
)
{
path
->
walked
=
1
;
if
(
path
->
connect
)
{
path
->
walking
=
1
;
/* do we need to add this widget to the list ? */
...
...
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT
(
widget
,
path_checks
);
switch
(
widget
->
id
)
{
case
snd_soc_dapm_supply
:
case
snd_soc_dapm_regulator_supply
:
case
snd_soc_dapm_clock_supply
:
case
snd_soc_dapm_kcontrol
:
return
0
;
default:
break
;
}
/* active stream ? */
switch
(
widget
->
id
)
{
case
snd_soc_dapm_dac
:
case
snd_soc_dapm_aif_in
:
case
snd_soc_dapm_dai_in
:
if
(
widget
->
active
)
{
widget
->
inputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
inputs
;
}
default:
break
;
}
if
(
widget
->
connected
)
{
/* connected pin ? */
if
(
widget
->
id
==
snd_soc_dapm_input
&&
!
widget
->
ext
)
{
widget
->
inputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
inputs
;
}
/* connected VMID/Bias for lower pops */
if
(
widget
->
id
==
snd_soc_dapm_vmid
)
{
widget
->
inputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
inputs
;
}
/* connected jack ? */
if
(
widget
->
id
==
snd_soc_dapm_mic
||
(
widget
->
id
==
snd_soc_dapm_line
&&
!
list_empty
(
&
widget
->
sinks
)))
{
if
(
widget
->
is_source
&&
widget
->
connected
)
{
widget
->
inputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
inputs
;
}
/* signal generator */
if
(
widget
->
id
==
snd_soc_dapm_siggen
)
{
widget
->
inputs
=
snd_soc_dapm_suspend_check
(
widget
);
return
widget
->
inputs
;
}
}
list_for_each_entry
(
path
,
&
widget
->
sources
,
list_sink
)
{
DAPM_UPDATE_STAT
(
widget
,
neighbour_checks
);
if
(
path
->
weak
)
if
(
path
->
weak
||
path
->
is_supply
)
continue
;
if
(
path
->
walking
)
return
1
;
if
(
path
->
walked
)
continue
;
trace_snd_soc_dapm_input_path
(
widget
,
path
);
if
(
path
->
source
&&
path
->
connect
)
{
path
->
walked
=
1
;
if
(
path
->
connect
)
{
path
->
walking
=
1
;
/* do we need to add this widget to the list ? */
...
...
@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
int
snd_soc_dapm_dai_get_connected_widgets
(
struct
snd_soc_dai
*
dai
,
int
stream
,
struct
snd_soc_dapm_widget_list
**
list
)
{
struct
snd_soc_card
*
card
=
dai
->
card
;
struct
snd_soc_card
*
card
=
dai
->
component
->
card
;
struct
snd_soc_dapm_widget
*
w
;
int
paths
;
mutex_lock_nested
(
&
card
->
dapm_mutex
,
SND_SOC_DAPM_CLASS_RUNTIME
);
dapm_reset
(
card
);
if
(
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
{
/*
* For is_connected_{output,input}_ep fully discover the graph we need
* to reset the cached number of inputs and outputs.
*/
list_for_each_entry
(
w
,
&
card
->
widgets
,
list
)
{
w
->
inputs
=
-
1
;
w
->
outputs
=
-
1
;
}
if
(
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
paths
=
is_connected_output_ep
(
dai
->
playback_widget
,
list
);
dapm_clear_walk_output
(
&
card
->
dapm
,
&
dai
->
playback_widget
->
sinks
);
}
else
{
else
paths
=
is_connected_input_ep
(
dai
->
capture_widget
,
list
);
dapm_clear_walk_input
(
&
card
->
dapm
,
&
dai
->
capture_widget
->
sources
);
}
trace_snd_soc_dapm_connected
(
paths
,
stream
);
mutex_unlock
(
&
card
->
dapm_mutex
);
...
...
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT
(
w
,
power_checks
);
in
=
is_connected_input_ep
(
w
,
NULL
);
dapm_clear_walk_input
(
w
->
dapm
,
&
w
->
sources
);
out
=
is_connected_output_ep
(
w
,
NULL
);
dapm_clear_walk_output
(
w
->
dapm
,
&
w
->
sinks
);
return
out
!=
0
&&
in
!=
0
;
}
/* Check to see if an ADC has power */
static
int
dapm_adc_check_power
(
struct
snd_soc_dapm_widget
*
w
)
{
int
in
;
DAPM_UPDATE_STAT
(
w
,
power_checks
);
if
(
w
->
active
)
{
in
=
is_connected_input_ep
(
w
,
NULL
);
dapm_clear_walk_input
(
w
->
dapm
,
&
w
->
sources
);
return
in
!=
0
;
}
else
{
return
dapm_generic_check_power
(
w
);
}
}
/* Check to see if a DAC has power */
static
int
dapm_dac_check_power
(
struct
snd_soc_dapm_widget
*
w
)
{
int
out
;
DAPM_UPDATE_STAT
(
w
,
power_checks
);
if
(
w
->
active
)
{
out
=
is_connected_output_ep
(
w
,
NULL
);
dapm_clear_walk_output
(
w
->
dapm
,
&
w
->
sinks
);
return
out
!=
0
;
}
else
{
return
dapm_generic_check_power
(
w
);
}
}
/* Check to see if a power supply is needed */
static
int
dapm_supply_check_power
(
struct
snd_soc_dapm_widget
*
w
)
{
...
...
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
!
path
->
connected
(
path
->
source
,
path
->
sink
))
continue
;
if
(
!
path
->
sink
)
continue
;
if
(
dapm_widget_power_check
(
path
->
sink
))
return
1
;
}
...
...
@@ -1636,28 +1586,15 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
/* If we changed our power state perhaps our neigbours changed
* also.
*/
list_for_each_entry
(
path
,
&
w
->
sources
,
list_sink
)
{
if
(
path
->
source
)
{
dapm_widget_set_peer_power
(
path
->
source
,
power
,
path
->
connect
);
}
}
switch
(
w
->
id
)
{
case
snd_soc_dapm_supply
:
case
snd_soc_dapm_regulator_supply
:
case
snd_soc_dapm_clock_supply
:
case
snd_soc_dapm_kcontrol
:
list_for_each_entry
(
path
,
&
w
->
sources
,
list_sink
)
dapm_widget_set_peer_power
(
path
->
source
,
power
,
path
->
connect
);
/* Supplies can't affect their outputs, only their inputs */
break
;
default:
list_for_each_entry
(
path
,
&
w
->
sinks
,
list_source
)
{
if
(
path
->
sink
)
{
if
(
!
w
->
is_supply
)
{
list_for_each_entry
(
path
,
&
w
->
sinks
,
list_source
)
dapm_widget_set_peer_power
(
path
->
sink
,
power
,
path
->
connect
);
}
}
break
;
}
if
(
power
)
dapm_seq_insert
(
w
,
up_list
,
true
);
...
...
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if
(
!
buf
)
return
-
ENOMEM
;
/* Supply widgets are not handled by is_connected_{input,output}_ep() */
if
(
w
->
is_supply
)
{
in
=
0
;
out
=
0
;
}
else
{
in
=
is_connected_input_ep
(
w
,
NULL
);
dapm_clear_walk_input
(
w
->
dapm
,
&
w
->
sources
);
out
=
is_connected_output_ep
(
w
,
NULL
);
dapm_clear_walk_output
(
w
->
dapm
,
&
w
->
sinks
);
}
ret
=
snprintf
(
buf
,
PAGE_SIZE
,
"%s: %s%s in %d out %d"
,
w
->
name
,
w
->
power
?
"On"
:
"Off"
,
...
...
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
/*
* soc_dapm_connect_path() - Connects or disconnects a path
* @path: The path to update
* @connect: The new connect state of the path. True if the path is connected,
* false if it is disconneted.
* @reason: The reason why the path changed (for debugging only)
*/
static
void
soc_dapm_connect_path
(
struct
snd_soc_dapm_path
*
path
,
bool
connect
,
const
char
*
reason
)
{
if
(
path
->
connect
==
connect
)
return
;
path
->
connect
=
connect
;
dapm_mark_dirty
(
path
->
source
,
reason
);
dapm_mark_dirty
(
path
->
sink
,
reason
);
dapm_path_invalidate
(
path
);
}
/* test and update the power status of a mux widget */
static
int
soc_dapm_mux_update_power
(
struct
snd_soc_card
*
card
,
struct
snd_kcontrol
*
kcontrol
,
int
mux
,
struct
soc_enum
*
e
)
{
struct
snd_soc_dapm_path
*
path
;
int
found
=
0
;
bool
connect
;
lockdep_assert_held
(
&
card
->
dapm_mutex
);
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path
(
path
,
kcontrol
)
{
if
(
!
path
->
name
||
!
e
->
texts
[
mux
])
continue
;
found
=
1
;
/* we now need to match the string in the enum to the path */
if
(
!
(
strcmp
(
path
->
name
,
e
->
texts
[
mux
])))
{
path
->
connect
=
1
;
/* new connection */
dapm_mark_dirty
(
path
->
source
,
"mux connection"
);
}
else
{
if
(
path
->
connect
)
dapm_mark_dirty
(
path
->
source
,
"mux disconnection"
);
path
->
connect
=
0
;
/* old connection must be powered down */
}
dapm_mark_dirty
(
path
->
sink
,
"mux change"
);
if
(
!
(
strcmp
(
path
->
name
,
e
->
texts
[
mux
])))
connect
=
true
;
else
connect
=
false
;
soc_dapm_connect_path
(
path
,
connect
,
"mux update"
);
}
if
(
found
)
...
...
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path
(
path
,
kcontrol
)
{
found
=
1
;
path
->
connect
=
connect
;
dapm_mark_dirty
(
path
->
source
,
"mixer connection"
);
dapm_mark_dirty
(
path
->
sink
,
"mixer update"
);
soc_dapm_connect_path
(
path
,
connect
,
"mixer update"
);
}
if
(
found
)
...
...
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
return
-
EINVAL
;
}
if
(
w
->
connected
!=
status
)
if
(
w
->
connected
!=
status
)
{
dapm_mark_dirty
(
w
,
"pin configuration"
);
dapm_widget_invalidate_input_paths
(
w
);
dapm_widget_invalidate_output_paths
(
w
);
}
w
->
connected
=
status
;
if
(
status
==
0
)
...
...
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
}
EXPORT_SYMBOL_GPL
(
snd_soc_dapm_sync
);
/*
* dapm_update_widget_flags() - Re-compute widget sink and source flags
* @w: The widget for which to update the flags
*
* Some widgets have a dynamic category which depends on which neighbors they
* are connected to. This function update the category for these widgets.
*
* This function must be called whenever a path is added or removed to a widget.
*/
static
void
dapm_update_widget_flags
(
struct
snd_soc_dapm_widget
*
w
)
{
struct
snd_soc_dapm_path
*
p
;
switch
(
w
->
id
)
{
case
snd_soc_dapm_input
:
w
->
is_source
=
1
;
list_for_each_entry
(
p
,
&
w
->
sources
,
list_sink
)
{
if
(
p
->
source
->
id
==
snd_soc_dapm_micbias
||
p
->
source
->
id
==
snd_soc_dapm_mic
||
p
->
source
->
id
==
snd_soc_dapm_line
||
p
->
source
->
id
==
snd_soc_dapm_output
)
{
w
->
is_source
=
0
;
break
;
}
}
break
;
case
snd_soc_dapm_output
:
w
->
is_sink
=
1
;
list_for_each_entry
(
p
,
&
w
->
sinks
,
list_source
)
{
if
(
p
->
sink
->
id
==
snd_soc_dapm_spk
||
p
->
sink
->
id
==
snd_soc_dapm_hp
||
p
->
sink
->
id
==
snd_soc_dapm_line
||
p
->
sink
->
id
==
snd_soc_dapm_input
)
{
w
->
is_sink
=
0
;
break
;
}
}
break
;
case
snd_soc_dapm_line
:
w
->
is_sink
=
!
list_empty
(
&
w
->
sources
);
w
->
is_source
=
!
list_empty
(
&
w
->
sinks
);
break
;
default:
break
;
}
}
static
int
snd_soc_dapm_add_path
(
struct
snd_soc_dapm_context
*
dapm
,
struct
snd_soc_dapm_widget
*
wsource
,
struct
snd_soc_dapm_widget
*
wsink
,
const
char
*
control
,
...
...
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct
snd_soc_dapm_path
*
path
;
int
ret
;
if
(
wsink
->
is_supply
&&
!
wsource
->
is_supply
)
{
dev_err
(
dapm
->
dev
,
"Connecting non-supply widget to supply widget is not supported (%s -> %s)
\n
"
,
wsource
->
name
,
wsink
->
name
);
return
-
EINVAL
;
}
if
(
connected
&&
!
wsource
->
is_supply
)
{
dev_err
(
dapm
->
dev
,
"connected() callback only supported for supply widgets (%s -> %s)
\n
"
,
wsource
->
name
,
wsink
->
name
);
return
-
EINVAL
;
}
if
(
wsource
->
is_supply
&&
control
)
{
dev_err
(
dapm
->
dev
,
"Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)
\n
"
,
wsource
->
name
,
control
,
wsink
->
name
);
return
-
EINVAL
;
}
path
=
kzalloc
(
sizeof
(
struct
snd_soc_dapm_path
),
GFP_KERNEL
);
if
(
!
path
)
return
-
ENOMEM
;
...
...
@@ -2330,84 +2353,48 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD
(
&
path
->
list_source
);
INIT_LIST_HEAD
(
&
path
->
list_sink
);
/* check for external widgets */
if
(
wsink
->
id
==
snd_soc_dapm_input
)
{
if
(
wsource
->
id
==
snd_soc_dapm_micbias
||
wsource
->
id
==
snd_soc_dapm_mic
||
wsource
->
id
==
snd_soc_dapm_line
||
wsource
->
id
==
snd_soc_dapm_output
)
wsink
->
ext
=
1
;
}
if
(
wsource
->
id
==
snd_soc_dapm_output
)
{
if
(
wsink
->
id
==
snd_soc_dapm_spk
||
wsink
->
id
==
snd_soc_dapm_hp
||
wsink
->
id
==
snd_soc_dapm_line
||
wsink
->
id
==
snd_soc_dapm_input
)
wsource
->
ext
=
1
;
}
dapm_mark_dirty
(
wsource
,
"Route added"
);
dapm_mark_dirty
(
wsink
,
"Route added"
);
if
(
wsource
->
is_supply
||
wsink
->
is_supply
)
path
->
is_supply
=
1
;
/* connect static paths */
if
(
control
==
NULL
)
{
list_add
(
&
path
->
list
,
&
dapm
->
card
->
paths
);
list_add
(
&
path
->
list_sink
,
&
wsink
->
sources
);
list_add
(
&
path
->
list_source
,
&
wsource
->
sinks
);
path
->
connect
=
1
;
return
0
;
}
}
else
{
/* connect dynamic paths */
switch
(
wsink
->
id
)
{
case
snd_soc_dapm_adc
:
case
snd_soc_dapm_dac
:
case
snd_soc_dapm_pga
:
case
snd_soc_dapm_out_drv
:
case
snd_soc_dapm_input
:
case
snd_soc_dapm_output
:
case
snd_soc_dapm_siggen
:
case
snd_soc_dapm_micbias
:
case
snd_soc_dapm_vmid
:
case
snd_soc_dapm_pre
:
case
snd_soc_dapm_post
:
case
snd_soc_dapm_supply
:
case
snd_soc_dapm_regulator_supply
:
case
snd_soc_dapm_clock_supply
:
case
snd_soc_dapm_aif_in
:
case
snd_soc_dapm_aif_out
:
case
snd_soc_dapm_dai_in
:
case
snd_soc_dapm_dai_out
:
case
snd_soc_dapm_dai_link
:
case
snd_soc_dapm_kcontrol
:
list_add
(
&
path
->
list
,
&
dapm
->
card
->
paths
);
list_add
(
&
path
->
list_sink
,
&
wsink
->
sources
);
list_add
(
&
path
->
list_source
,
&
wsource
->
sinks
);
path
->
connect
=
1
;
return
0
;
case
snd_soc_dapm_mux
:
ret
=
dapm_connect_mux
(
dapm
,
wsource
,
wsink
,
path
,
control
,
&
wsink
->
kcontrol_news
[
0
]);
ret
=
dapm_connect_mux
(
dapm
,
path
,
control
);
if
(
ret
!=
0
)
goto
err
;
break
;
case
snd_soc_dapm_switch
:
case
snd_soc_dapm_mixer
:
case
snd_soc_dapm_mixer_named_ctl
:
ret
=
dapm_connect_mixer
(
dapm
,
wsource
,
wsink
,
path
,
control
);
ret
=
dapm_connect_mixer
(
dapm
,
path
,
control
);
if
(
ret
!=
0
)
goto
err
;
break
;
case
snd_soc_dapm_hp
:
case
snd_soc_dapm_mic
:
case
snd_soc_dapm_line
:
case
snd_soc_dapm_spk
:
default:
dev_err
(
dapm
->
dev
,
"Control not supported for path %s -> [%s] -> %s
\n
"
,
wsource
->
name
,
control
,
wsink
->
name
);
ret
=
-
EINVAL
;
goto
err
;
}
}
list_add
(
&
path
->
list
,
&
dapm
->
card
->
paths
);
list_add
(
&
path
->
list_sink
,
&
wsink
->
sources
);
list_add
(
&
path
->
list_source
,
&
wsource
->
sinks
);
path
->
connect
=
0
;
return
0
;
}
dapm_update_widget_flags
(
wsource
);
dapm_update_widget_flags
(
wsink
);
dapm_mark_dirty
(
wsource
,
"Route added"
);
dapm_mark_dirty
(
wsink
,
"Route added"
);
if
(
dapm
->
card
->
instantiated
&&
path
->
connect
)
dapm_path_invalidate
(
path
);
return
0
;
err:
...
...
@@ -2489,6 +2476,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
static
int
snd_soc_dapm_del_route
(
struct
snd_soc_dapm_context
*
dapm
,
const
struct
snd_soc_dapm_route
*
route
)
{
struct
snd_soc_dapm_widget
*
wsource
,
*
wsink
;
struct
snd_soc_dapm_path
*
path
,
*
p
;
const
char
*
sink
;
const
char
*
source
;
...
...
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
}
if
(
path
)
{
dapm_mark_dirty
(
path
->
source
,
"Route removed"
);
dapm_mark_dirty
(
path
->
sink
,
"Route removed"
);
wsource
=
path
->
source
;
wsink
=
path
->
sink
;
dapm_mark_dirty
(
wsource
,
"Route removed"
);
dapm_mark_dirty
(
wsink
,
"Route removed"
);
if
(
path
->
connect
)
dapm_path_invalidate
(
path
);
dapm_free_path
(
path
);
/* Update any path related flags */
dapm_update_widget_flags
(
wsource
);
dapm_update_widget_flags
(
wsink
);
}
else
{
dev_warn
(
dapm
->
dev
,
"ASoC: Route %s->%s does not exist
\n
"
,
source
,
sink
);
...
...
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
}
switch
(
w
->
id
)
{
case
snd_soc_dapm_
switch
:
case
snd_soc_dapm_
mixer
:
case
snd_soc_dapm_mixer_named_ctl
:
case
snd_soc_dapm_
mic
:
case
snd_soc_dapm_
input
:
w
->
is_source
=
1
;
w
->
power_check
=
dapm_generic_check_power
;
break
;
case
snd_soc_dapm_mux
:
case
snd_soc_dapm_spk
:
case
snd_soc_dapm_hp
:
case
snd_soc_dapm_output
:
w
->
is_sink
=
1
;
w
->
power_check
=
dapm_generic_check_power
;
break
;
case
snd_soc_dapm_dai_out
:
w
->
power_check
=
dapm_adc_check_power
;
break
;
case
snd_soc_dapm_dai_in
:
w
->
power_check
=
dapm_dac_check_power
;
case
snd_soc_dapm_vmid
:
case
snd_soc_dapm_siggen
:
w
->
is_source
=
1
;
w
->
power_check
=
dapm_always_on_check_power
;
break
;
case
snd_soc_dapm_mux
:
case
snd_soc_dapm_switch
:
case
snd_soc_dapm_mixer
:
case
snd_soc_dapm_mixer_named_ctl
:
case
snd_soc_dapm_adc
:
case
snd_soc_dapm_aif_out
:
case
snd_soc_dapm_dac
:
case
snd_soc_dapm_aif_in
:
case
snd_soc_dapm_pga
:
case
snd_soc_dapm_out_drv
:
case
snd_soc_dapm_input
:
case
snd_soc_dapm_output
:
case
snd_soc_dapm_micbias
:
case
snd_soc_dapm_spk
:
case
snd_soc_dapm_hp
:
case
snd_soc_dapm_mic
:
case
snd_soc_dapm_line
:
case
snd_soc_dapm_dai_link
:
case
snd_soc_dapm_dai_out
:
case
snd_soc_dapm_dai_in
:
w
->
power_check
=
dapm_generic_check_power
;
break
;
case
snd_soc_dapm_supply
:
case
snd_soc_dapm_regulator_supply
:
case
snd_soc_dapm_clock_supply
:
case
snd_soc_dapm_kcontrol
:
w
->
is_supply
=
1
;
w
->
power_check
=
dapm_supply_check_power
;
break
;
default:
...
...
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD
(
&
w
->
dirty
);
list_add
(
&
w
->
list
,
&
dapm
->
card
->
widgets
);
w
->
inputs
=
-
1
;
w
->
outputs
=
-
1
;
/* machine layer set ups unconnected pins and insertions */
w
->
connected
=
1
;
return
w
;
...
...
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
case
SND_SOC_DAPM_STREAM_PAUSE_RELEASE
:
break
;
}
if
(
w
->
id
==
snd_soc_dapm_dai_in
)
{
w
->
is_source
=
w
->
active
;
dapm_widget_invalidate_input_paths
(
w
);
}
else
{
w
->
is_sink
=
w
->
active
;
dapm_widget_invalidate_output_paths
(
w
);
}
}
}
...
...
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
}
dev_dbg
(
w
->
dapm
->
dev
,
"ASoC: force enable pin %s
\n
"
,
pin
);
if
(
!
w
->
connected
)
{
/*
* w->force does not affect the number of input or output paths,
* so we only have to recheck if w->connected is changed
*/
dapm_widget_invalidate_input_paths
(
w
);
dapm_widget_invalidate_output_paths
(
w
);
w
->
connected
=
1
;
}
w
->
force
=
1
;
dapm_mark_dirty
(
w
,
"force enable"
);
...
...
@@ -3788,27 +3808,30 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL
(
snd_soc_dapm_ignore_suspend
);
static
bool
snd_soc_dapm_widget_in_card_paths
(
struct
snd_soc_card
*
card
,
struct
snd_soc_dapm_widget
*
w
)
/**
* dapm_is_external_path() - Checks if a path is a external path
* @card: The card the path belongs to
* @path: The path to check
*
* Returns true if the path is either between two different DAPM contexts or
* between two external pins of the same DAPM context. Otherwise returns
* false.
*/
static
bool
dapm_is_external_path
(
struct
snd_soc_card
*
card
,
struct
snd_soc_dapm_path
*
path
)
{
struct
snd_soc_dapm_path
*
p
;
list_for_each_entry
(
p
,
&
card
->
paths
,
list
)
{
if
((
p
->
source
==
w
)
||
(
p
->
sink
==
w
))
{
dev_dbg
(
card
->
dev
,
"... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)
\n
"
,
p
->
source
->
name
,
p
->
source
->
id
,
p
->
source
->
dapm
,
p
->
sink
->
name
,
p
->
sink
->
id
,
p
->
sink
->
dapm
);
path
->
source
->
name
,
path
->
source
->
id
,
path
->
source
->
dapm
,
path
->
sink
->
name
,
path
->
sink
->
id
,
path
->
sink
->
dapm
);
/* Connected to something other than the codec
*/
if
(
p
->
source
->
dapm
!=
p
->
sink
->
dapm
)
/* Connection between two different DAPM contexts
*/
if
(
path
->
source
->
dapm
!=
path
->
sink
->
dapm
)
return
true
;
/*
* Loopback connection from codec external pin to
* codec external pin
*/
if
(
p
->
sink
->
id
==
snd_soc_dapm_input
)
{
switch
(
p
->
source
->
id
)
{
/* Loopback connection from external pin to external pin */
if
(
path
->
sink
->
id
==
snd_soc_dapm_input
)
{
switch
(
path
->
source
->
id
)
{
case
snd_soc_dapm_output
:
case
snd_soc_dapm_micbias
:
return
true
;
...
...
@@ -3816,7 +3839,23 @@ static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
break
;
}
}
return
false
;
}
static
bool
snd_soc_dapm_widget_in_card_paths
(
struct
snd_soc_card
*
card
,
struct
snd_soc_dapm_widget
*
w
)
{
struct
snd_soc_dapm_path
*
p
;
list_for_each_entry
(
p
,
&
w
->
sources
,
list_sink
)
{
if
(
dapm_is_external_path
(
card
,
p
))
return
true
;
}
list_for_each_entry
(
p
,
&
w
->
sinks
,
list_source
)
{
if
(
dapm_is_external_path
(
card
,
p
))
return
true
;
}
return
false
;
...
...
sound/soc/soc-ops.c
0 → 100644
View file @
941725f5
/*
* soc-ops.c -- Generic ASoC operations
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
* Copyright (C) 2010 Slimlogic Ltd.
* Copyright (C) 2010 Texas Instruments Inc.
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
* with code, comments and ideas from :-
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
#include <sound/initval.h>
/**
* snd_soc_info_enum_double - enumerated double mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a double enumerated
* mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_info_enum_double
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
return
snd_ctl_enum_info
(
uinfo
,
e
->
shift_l
==
e
->
shift_r
?
1
:
2
,
e
->
items
,
e
->
texts
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_enum_double
);
/**
* snd_soc_get_enum_double - enumerated double mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int
snd_soc_get_enum_double
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
unsigned
int
val
,
item
;
unsigned
int
reg_val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
e
->
reg
,
&
reg_val
);
if
(
ret
)
return
ret
;
val
=
(
reg_val
>>
e
->
shift_l
)
&
e
->
mask
;
item
=
snd_soc_enum_val_to_item
(
e
,
val
);
ucontrol
->
value
.
enumerated
.
item
[
0
]
=
item
;
if
(
e
->
shift_l
!=
e
->
shift_r
)
{
val
=
(
reg_val
>>
e
->
shift_l
)
&
e
->
mask
;
item
=
snd_soc_enum_val_to_item
(
e
,
val
);
ucontrol
->
value
.
enumerated
.
item
[
1
]
=
item
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_enum_double
);
/**
* snd_soc_put_enum_double - enumerated double mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a double enumerated mixer.
*
* Returns 0 for success.
*/
int
snd_soc_put_enum_double
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_enum
*
e
=
(
struct
soc_enum
*
)
kcontrol
->
private_value
;
unsigned
int
*
item
=
ucontrol
->
value
.
enumerated
.
item
;
unsigned
int
val
;
unsigned
int
mask
;
if
(
item
[
0
]
>=
e
->
items
)
return
-
EINVAL
;
val
=
snd_soc_enum_item_to_val
(
e
,
item
[
0
])
<<
e
->
shift_l
;
mask
=
e
->
mask
<<
e
->
shift_l
;
if
(
e
->
shift_l
!=
e
->
shift_r
)
{
if
(
item
[
1
]
>=
e
->
items
)
return
-
EINVAL
;
val
|=
snd_soc_enum_item_to_val
(
e
,
item
[
1
])
<<
e
->
shift_r
;
mask
|=
e
->
mask
<<
e
->
shift_r
;
}
return
snd_soc_component_update_bits
(
component
,
e
->
reg
,
mask
,
val
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_enum_double
);
/**
* snd_soc_read_signed - Read a codec register and interprete as signed value
* @component: component
* @reg: Register to read
* @mask: Mask to use after shifting the register value
* @shift: Right shift of register value
* @sign_bit: Bit that describes if a number is negative or not.
* @signed_val: Pointer to where the read value should be stored
*
* This functions reads a codec register. The register value is shifted right
* by 'shift' bits and masked with the given 'mask'. Afterwards it translates
* the given registervalue into a signed integer if sign_bit is non-zero.
*
* Returns 0 on sucess, otherwise an error value
*/
static
int
snd_soc_read_signed
(
struct
snd_soc_component
*
component
,
unsigned
int
reg
,
unsigned
int
mask
,
unsigned
int
shift
,
unsigned
int
sign_bit
,
int
*
signed_val
)
{
int
ret
;
unsigned
int
val
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
<
0
)
return
ret
;
val
=
(
val
>>
shift
)
&
mask
;
if
(
!
sign_bit
)
{
*
signed_val
=
val
;
return
0
;
}
/* non-negative number */
if
(
!
(
val
&
BIT
(
sign_bit
)))
{
*
signed_val
=
val
;
return
0
;
}
ret
=
val
;
/*
* The register most probably does not contain a full-sized int.
* Instead we have an arbitrary number of bits in a signed
* representation which has to be translated into a full-sized int.
* This is done by filling up all bits above the sign-bit.
*/
ret
|=
~
((
int
)(
BIT
(
sign_bit
)
-
1
));
*
signed_val
=
ret
;
return
0
;
}
/**
* snd_soc_info_volsw - single mixer info callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information about a single mixer control, or a double
* mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_info_volsw
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
int
platform_max
;
if
(
!
mc
->
platform_max
)
mc
->
platform_max
=
mc
->
max
;
platform_max
=
mc
->
platform_max
;
if
(
platform_max
==
1
&&
!
strstr
(
kcontrol
->
id
.
name
,
" Volume"
))
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BOOLEAN
;
else
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
snd_soc_volsw_is_stereo
(
mc
)
?
2
:
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
platform_max
-
mc
->
min
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_volsw
);
/**
* snd_soc_get_volsw - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
int
sign_bit
=
mc
->
sign_bit
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
int
val
;
int
ret
;
if
(
sign_bit
)
mask
=
BIT
(
sign_bit
+
1
)
-
1
;
ret
=
snd_soc_read_signed
(
component
,
reg
,
mask
,
shift
,
sign_bit
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
val
-
min
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
0
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
0
];
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
if
(
reg
==
reg2
)
ret
=
snd_soc_read_signed
(
component
,
reg
,
mask
,
rshift
,
sign_bit
,
&
val
);
else
ret
=
snd_soc_read_signed
(
component
,
reg2
,
mask
,
shift
,
sign_bit
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
val
-
min
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
1
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
1
];
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw
);
/**
* snd_soc_put_volsw - single mixer put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
unsigned
int
sign_bit
=
mc
->
sign_bit
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
int
err
;
bool
type_2r
=
false
;
unsigned
int
val2
=
0
;
unsigned
int
val
,
val_mask
;
if
(
sign_bit
)
mask
=
BIT
(
sign_bit
+
1
)
-
1
;
val
=
((
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
mask
);
if
(
invert
)
val
=
max
-
val
;
val_mask
=
mask
<<
shift
;
val
=
val
<<
shift
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
val2
=
((
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
mask
);
if
(
invert
)
val2
=
max
-
val2
;
if
(
reg
==
reg2
)
{
val_mask
|=
mask
<<
rshift
;
val
|=
val2
<<
rshift
;
}
else
{
val2
=
val2
<<
shift
;
type_2r
=
true
;
}
}
err
=
snd_soc_component_update_bits
(
component
,
reg
,
val_mask
,
val
);
if
(
err
<
0
)
return
err
;
if
(
type_2r
)
err
=
snd_soc_component_update_bits
(
component
,
reg2
,
val_mask
,
val2
);
return
err
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw
);
/**
* snd_soc_get_volsw_sx - single mixer get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value of a single mixer control, or a double mixer
* control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
int
mask
=
(
1
<<
(
fls
(
min
+
max
)
-
1
))
-
1
;
unsigned
int
val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
<
0
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
((
val
>>
shift
)
-
min
)
&
mask
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
ret
=
snd_soc_component_read
(
component
,
reg2
,
&
val
);
if
(
ret
<
0
)
return
ret
;
val
=
((
val
>>
rshift
)
-
min
)
&
mask
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
val
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw_sx
);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
reg2
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
rshift
=
mc
->
rshift
;
int
max
=
mc
->
max
;
int
min
=
mc
->
min
;
int
mask
=
(
1
<<
(
fls
(
min
+
max
)
-
1
))
-
1
;
int
err
=
0
;
unsigned
int
val
,
val_mask
,
val2
=
0
;
val_mask
=
mask
<<
shift
;
val
=
(
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
mask
;
val
=
val
<<
shift
;
err
=
snd_soc_component_update_bits
(
component
,
reg
,
val_mask
,
val
);
if
(
err
<
0
)
return
err
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
val_mask
=
mask
<<
rshift
;
val2
=
(
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
mask
;
val2
=
val2
<<
rshift
;
err
=
snd_soc_component_update_bits
(
component
,
reg2
,
val_mask
,
val2
);
}
return
err
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw_sx
);
/**
* snd_soc_info_volsw_range - single mixer info callback with range.
* @kcontrol: mixer control
* @uinfo: control element information
*
* Callback to provide information, within a range, about a single
* mixer control.
*
* returns 0 for success.
*/
int
snd_soc_info_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
int
platform_max
;
int
min
=
mc
->
min
;
if
(
!
mc
->
platform_max
)
mc
->
platform_max
=
mc
->
max
;
platform_max
=
mc
->
platform_max
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
snd_soc_volsw_is_stereo
(
mc
)
?
2
:
1
;
uinfo
->
value
.
integer
.
min
=
0
;
uinfo
->
value
.
integer
.
max
=
platform_max
-
min
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_volsw_range
);
/**
* snd_soc_put_volsw_range - single mixer put value callback with range.
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to set the value, within a range, for a single mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_put_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
rreg
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
int
min
=
mc
->
min
;
int
max
=
mc
->
max
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
int
val
,
val_mask
;
int
ret
;
if
(
invert
)
val
=
(
max
-
ucontrol
->
value
.
integer
.
value
[
0
])
&
mask
;
else
val
=
((
ucontrol
->
value
.
integer
.
value
[
0
]
+
min
)
&
mask
);
val_mask
=
mask
<<
shift
;
val
=
val
<<
shift
;
ret
=
snd_soc_component_update_bits
(
component
,
reg
,
val_mask
,
val
);
if
(
ret
<
0
)
return
ret
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
if
(
invert
)
val
=
(
max
-
ucontrol
->
value
.
integer
.
value
[
1
])
&
mask
;
else
val
=
((
ucontrol
->
value
.
integer
.
value
[
1
]
+
min
)
&
mask
);
val_mask
=
mask
<<
shift
;
val
=
val
<<
shift
;
ret
=
snd_soc_component_update_bits
(
component
,
rreg
,
val_mask
,
val
);
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_volsw_range
);
/**
* snd_soc_get_volsw_range - single mixer get callback with range
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback to get the value, within a range, of a single mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_get_volsw_range
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
rreg
=
mc
->
rreg
;
unsigned
int
shift
=
mc
->
shift
;
int
min
=
mc
->
min
;
int
max
=
mc
->
max
;
unsigned
int
mask
=
(
1
<<
fls
(
max
))
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
int
val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
(
val
>>
shift
)
&
mask
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
0
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
0
];
else
ucontrol
->
value
.
integer
.
value
[
0
]
=
ucontrol
->
value
.
integer
.
value
[
0
]
-
min
;
if
(
snd_soc_volsw_is_stereo
(
mc
))
{
ret
=
snd_soc_component_read
(
component
,
rreg
,
&
val
);
if
(
ret
)
return
ret
;
ucontrol
->
value
.
integer
.
value
[
1
]
=
(
val
>>
shift
)
&
mask
;
if
(
invert
)
ucontrol
->
value
.
integer
.
value
[
1
]
=
max
-
ucontrol
->
value
.
integer
.
value
[
1
];
else
ucontrol
->
value
.
integer
.
value
[
1
]
=
ucontrol
->
value
.
integer
.
value
[
1
]
-
min
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_volsw_range
);
/**
* snd_soc_limit_volume - Set new limit to an existing volume control.
*
* @codec: where to look for the control
* @name: Name of the control
* @max: new maximum limit
*
* Return 0 for success, else error.
*/
int
snd_soc_limit_volume
(
struct
snd_soc_codec
*
codec
,
const
char
*
name
,
int
max
)
{
struct
snd_card
*
card
=
codec
->
component
.
card
->
snd_card
;
struct
snd_kcontrol
*
kctl
;
struct
soc_mixer_control
*
mc
;
int
found
=
0
;
int
ret
=
-
EINVAL
;
/* Sanity check for name and max */
if
(
unlikely
(
!
name
||
max
<=
0
))
return
-
EINVAL
;
list_for_each_entry
(
kctl
,
&
card
->
controls
,
list
)
{
if
(
!
strncmp
(
kctl
->
id
.
name
,
name
,
sizeof
(
kctl
->
id
.
name
)))
{
found
=
1
;
break
;
}
}
if
(
found
)
{
mc
=
(
struct
soc_mixer_control
*
)
kctl
->
private_value
;
if
(
max
<=
mc
->
max
)
{
mc
->
platform_max
=
max
;
ret
=
0
;
}
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_limit_volume
);
int
snd_soc_bytes_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_bytes
*
params
=
(
void
*
)
kcontrol
->
private_value
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BYTES
;
uinfo
->
count
=
params
->
num_regs
*
component
->
val_bytes
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_info
);
int
snd_soc_bytes_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_bytes
*
params
=
(
void
*
)
kcontrol
->
private_value
;
int
ret
;
if
(
component
->
regmap
)
ret
=
regmap_raw_read
(
component
->
regmap
,
params
->
base
,
ucontrol
->
value
.
bytes
.
data
,
params
->
num_regs
*
component
->
val_bytes
);
else
ret
=
-
EINVAL
;
/* Hide any masked bytes to ensure consistent data reporting */
if
(
ret
==
0
&&
params
->
mask
)
{
switch
(
component
->
val_bytes
)
{
case
1
:
ucontrol
->
value
.
bytes
.
data
[
0
]
&=
~
params
->
mask
;
break
;
case
2
:
((
u16
*
)(
&
ucontrol
->
value
.
bytes
.
data
))[
0
]
&=
cpu_to_be16
(
~
params
->
mask
);
break
;
case
4
:
((
u32
*
)(
&
ucontrol
->
value
.
bytes
.
data
))[
0
]
&=
cpu_to_be32
(
~
params
->
mask
);
break
;
default:
return
-
EINVAL
;
}
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_get
);
int
snd_soc_bytes_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_bytes
*
params
=
(
void
*
)
kcontrol
->
private_value
;
int
ret
,
len
;
unsigned
int
val
,
mask
;
void
*
data
;
if
(
!
component
->
regmap
||
!
params
->
num_regs
)
return
-
EINVAL
;
len
=
params
->
num_regs
*
component
->
val_bytes
;
data
=
kmemdup
(
ucontrol
->
value
.
bytes
.
data
,
len
,
GFP_KERNEL
|
GFP_DMA
);
if
(
!
data
)
return
-
ENOMEM
;
/*
* If we've got a mask then we need to preserve the register
* bits. We shouldn't modify the incoming data so take a
* copy.
*/
if
(
params
->
mask
)
{
ret
=
regmap_read
(
component
->
regmap
,
params
->
base
,
&
val
);
if
(
ret
!=
0
)
goto
out
;
val
&=
params
->
mask
;
switch
(
component
->
val_bytes
)
{
case
1
:
((
u8
*
)
data
)[
0
]
&=
~
params
->
mask
;
((
u8
*
)
data
)[
0
]
|=
val
;
break
;
case
2
:
mask
=
~
params
->
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
mask
,
&
mask
);
if
(
ret
!=
0
)
goto
out
;
((
u16
*
)
data
)[
0
]
&=
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
val
,
&
val
);
if
(
ret
!=
0
)
goto
out
;
((
u16
*
)
data
)[
0
]
|=
val
;
break
;
case
4
:
mask
=
~
params
->
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
mask
,
&
mask
);
if
(
ret
!=
0
)
goto
out
;
((
u32
*
)
data
)[
0
]
&=
mask
;
ret
=
regmap_parse_val
(
component
->
regmap
,
&
val
,
&
val
);
if
(
ret
!=
0
)
goto
out
;
((
u32
*
)
data
)[
0
]
|=
val
;
break
;
default:
ret
=
-
EINVAL
;
goto
out
;
}
}
ret
=
regmap_raw_write
(
component
->
regmap
,
params
->
base
,
data
,
len
);
out:
kfree
(
data
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_put
);
int
snd_soc_bytes_info_ext
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
ucontrol
)
{
struct
soc_bytes_ext
*
params
=
(
void
*
)
kcontrol
->
private_value
;
ucontrol
->
type
=
SNDRV_CTL_ELEM_TYPE_BYTES
;
ucontrol
->
count
=
params
->
max
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_info_ext
);
int
snd_soc_bytes_tlv_callback
(
struct
snd_kcontrol
*
kcontrol
,
int
op_flag
,
unsigned
int
size
,
unsigned
int
__user
*
tlv
)
{
struct
soc_bytes_ext
*
params
=
(
void
*
)
kcontrol
->
private_value
;
unsigned
int
count
=
size
<
params
->
max
?
size
:
params
->
max
;
int
ret
=
-
ENXIO
;
switch
(
op_flag
)
{
case
SNDRV_CTL_TLV_OP_READ
:
if
(
params
->
get
)
ret
=
params
->
get
(
tlv
,
count
);
break
;
case
SNDRV_CTL_TLV_OP_WRITE
:
if
(
params
->
put
)
ret
=
params
->
put
(
tlv
,
count
);
break
;
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_bytes_tlv_callback
);
/**
* snd_soc_info_xr_sx - signed multi register info callback
* @kcontrol: mreg control
* @uinfo: control element information
*
* Callback to provide information of a control that can
* span multiple codec registers which together
* forms a single signed value in a MSB/LSB manner.
*
* Returns 0 for success.
*/
int
snd_soc_info_xr_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
soc_mreg_control
*
mc
=
(
struct
soc_mreg_control
*
)
kcontrol
->
private_value
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
mc
->
min
;
uinfo
->
value
.
integer
.
max
=
mc
->
max
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_info_xr_sx
);
/**
* snd_soc_get_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to get the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int
snd_soc_get_xr_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mreg_control
*
mc
=
(
struct
soc_mreg_control
*
)
kcontrol
->
private_value
;
unsigned
int
regbase
=
mc
->
regbase
;
unsigned
int
regcount
=
mc
->
regcount
;
unsigned
int
regwshift
=
component
->
val_bytes
*
BITS_PER_BYTE
;
unsigned
int
regwmask
=
(
1
<<
regwshift
)
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
long
mask
=
(
1UL
<<
mc
->
nbits
)
-
1
;
long
min
=
mc
->
min
;
long
max
=
mc
->
max
;
long
val
=
0
;
unsigned
int
regval
;
unsigned
int
i
;
int
ret
;
for
(
i
=
0
;
i
<
regcount
;
i
++
)
{
ret
=
snd_soc_component_read
(
component
,
regbase
+
i
,
&
regval
);
if
(
ret
)
return
ret
;
val
|=
(
regval
&
regwmask
)
<<
(
regwshift
*
(
regcount
-
i
-
1
));
}
val
&=
mask
;
if
(
min
<
0
&&
val
>
max
)
val
|=
~
mask
;
if
(
invert
)
val
=
max
-
val
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
val
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_xr_sx
);
/**
* snd_soc_put_xr_sx - signed multi register get callback
* @kcontrol: mreg control
* @ucontrol: control element information
*
* Callback to set the value of a control that can span
* multiple codec registers which together forms a single
* signed value in a MSB/LSB manner. The control supports
* specifying total no of bits used to allow for bitfields
* across the multiple codec registers.
*
* Returns 0 for success.
*/
int
snd_soc_put_xr_sx
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mreg_control
*
mc
=
(
struct
soc_mreg_control
*
)
kcontrol
->
private_value
;
unsigned
int
regbase
=
mc
->
regbase
;
unsigned
int
regcount
=
mc
->
regcount
;
unsigned
int
regwshift
=
component
->
val_bytes
*
BITS_PER_BYTE
;
unsigned
int
regwmask
=
(
1
<<
regwshift
)
-
1
;
unsigned
int
invert
=
mc
->
invert
;
unsigned
long
mask
=
(
1UL
<<
mc
->
nbits
)
-
1
;
long
max
=
mc
->
max
;
long
val
=
ucontrol
->
value
.
integer
.
value
[
0
];
unsigned
int
i
,
regval
,
regmask
;
int
err
;
if
(
invert
)
val
=
max
-
val
;
val
&=
mask
;
for
(
i
=
0
;
i
<
regcount
;
i
++
)
{
regval
=
(
val
>>
(
regwshift
*
(
regcount
-
i
-
1
)))
&
regwmask
;
regmask
=
(
mask
>>
(
regwshift
*
(
regcount
-
i
-
1
)))
&
regwmask
;
err
=
snd_soc_component_update_bits
(
component
,
regbase
+
i
,
regmask
,
regval
);
if
(
err
<
0
)
return
err
;
}
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_xr_sx
);
/**
* snd_soc_get_strobe - strobe get callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback get the value of a strobe mixer control.
*
* Returns 0 for success.
*/
int
snd_soc_get_strobe
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
mask
=
1
<<
shift
;
unsigned
int
invert
=
mc
->
invert
!=
0
;
unsigned
int
val
;
int
ret
;
ret
=
snd_soc_component_read
(
component
,
reg
,
&
val
);
if
(
ret
)
return
ret
;
val
&=
mask
;
if
(
shift
!=
0
&&
val
!=
0
)
val
=
val
>>
shift
;
ucontrol
->
value
.
enumerated
.
item
[
0
]
=
val
^
invert
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
snd_soc_get_strobe
);
/**
* snd_soc_put_strobe - strobe put callback
* @kcontrol: mixer control
* @ucontrol: control element information
*
* Callback strobe a register bit to high then low (or the inverse)
* in one pass of a single mixer enum control.
*
* Returns 1 for success.
*/
int
snd_soc_put_strobe
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_soc_component
*
component
=
snd_kcontrol_chip
(
kcontrol
);
struct
soc_mixer_control
*
mc
=
(
struct
soc_mixer_control
*
)
kcontrol
->
private_value
;
unsigned
int
reg
=
mc
->
reg
;
unsigned
int
shift
=
mc
->
shift
;
unsigned
int
mask
=
1
<<
shift
;
unsigned
int
invert
=
mc
->
invert
!=
0
;
unsigned
int
strobe
=
ucontrol
->
value
.
enumerated
.
item
[
0
]
!=
0
;
unsigned
int
val1
=
(
strobe
^
invert
)
?
mask
:
0
;
unsigned
int
val2
=
(
strobe
^
invert
)
?
0
:
mask
;
int
err
;
err
=
snd_soc_component_update_bits
(
component
,
reg
,
mask
,
val1
);
if
(
err
<
0
)
return
err
;
return
snd_soc_component_update_bits
(
component
,
reg
,
mask
,
val2
);
}
EXPORT_SYMBOL_GPL
(
snd_soc_put_strobe
);
sound/soc/soc-pcm.c
View file @
941725f5
...
...
@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
codec_dai
->
rate
=
0
;
}
snd_soc_dai_digital_mute
(
cpu_dai
,
1
,
substream
->
stream
);
if
(
cpu_dai
->
driver
->
ops
->
shutdown
)
cpu_dai
->
driver
->
ops
->
shutdown
(
substream
,
cpu_dai
);
...
...
@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
for
(
i
=
0
;
i
<
rtd
->
num_codecs
;
i
++
)
snd_soc_dai_digital_mute
(
rtd
->
codec_dais
[
i
],
0
,
substream
->
stream
);
snd_soc_dai_digital_mute
(
cpu_dai
,
0
,
substream
->
stream
);
out:
mutex_unlock
(
&
rtd
->
pcm_mutex
);
...
...
sound/soc/txx9/txx9aclc.c
View file @
941725f5
...
...
@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
struct
snd_card
*
card
=
rtd
->
card
->
snd_card
;
struct
snd_soc_dai
*
dai
=
rtd
->
cpu_dai
;
struct
snd_pcm
*
pcm
=
rtd
->
pcm
;
struct
platform_device
*
pdev
=
to_platform_device
(
dai
->
platform
->
dev
);
struct
platform_device
*
pdev
=
to_platform_device
(
rtd
->
platform
->
dev
);
struct
txx9aclc_soc_device
*
dev
;
struct
resource
*
r
;
int
i
;
...
...
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