Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
06f1c663
Commit
06f1c663
authored
Dec 10, 2012
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/wm8994' into asoc-next
parents
6f5716a2
98869f68
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
273 additions
and
208 deletions
+273
-208
drivers/mfd/wm8994-core.c
drivers/mfd/wm8994-core.c
+19
-16
include/linux/mfd/wm8994/core.h
include/linux/mfd/wm8994/core.h
+4
-0
include/linux/mfd/wm8994/pdata.h
include/linux/mfd/wm8994/pdata.h
+5
-0
sound/soc/codecs/wm8958-dsp2.c
sound/soc/codecs/wm8958-dsp2.c
+34
-45
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.c
+202
-141
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm8994.h
+8
-5
sound/soc/samsung/littlemill.c
sound/soc/samsung/littlemill.c
+1
-1
No files found.
drivers/mfd/wm8994-core.c
View file @
06f1c663
...
...
@@ -401,13 +401,19 @@ static const __devinitconst struct reg_default wm1811_reva_patch[] = {
*/
static
__devinit
int
wm8994_device_init
(
struct
wm8994
*
wm8994
,
int
irq
)
{
struct
wm8994_pdata
*
pdata
=
wm8994
->
dev
->
platform_data
;
struct
wm8994_pdata
*
pdata
;
struct
regmap_config
*
regmap_config
;
const
struct
reg_default
*
regmap_patch
=
NULL
;
const
char
*
devname
;
int
ret
,
i
,
patch_regs
;
int
pulls
=
0
;
if
(
dev_get_platdata
(
wm8994
->
dev
))
{
pdata
=
dev_get_platdata
(
wm8994
->
dev
);
wm8994
->
pdata
=
*
pdata
;
}
pdata
=
&
wm8994
->
pdata
;
dev_set_drvdata
(
wm8994
->
dev
,
wm8994
);
/* Add the on-chip regulators first for bootstrapping */
...
...
@@ -604,7 +610,6 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
}
}
if
(
pdata
)
{
wm8994
->
irq_base
=
pdata
->
irq_base
;
wm8994
->
gpio_base
=
pdata
->
gpio_base
;
...
...
@@ -612,8 +617,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
pdata
->
gpio_defaults
);
i
++
)
{
if
(
pdata
->
gpio_defaults
[
i
])
{
wm8994_set_bits
(
wm8994
,
WM8994_GPIO_1
+
i
,
0xffff
,
pdata
->
gpio_defaults
[
i
]);
0xffff
,
pdata
->
gpio_defaults
[
i
]);
}
}
...
...
@@ -621,7 +625,6 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
if
(
pdata
->
spkmode_pu
)
pulls
|=
WM8994_SPKMODE_PU
;
}
/* Disable unneeded pulls */
wm8994_set_bits
(
wm8994
,
WM8994_PULL_CONTROL_2
,
...
...
include/linux/mfd/wm8994/core.h
View file @
06f1c663
...
...
@@ -19,6 +19,8 @@
#include <linux/interrupt.h>
#include <linux/regmap.h>
#include <linux/mfd/wm8994/pdata.h>
enum
wm8994_type
{
WM8994
=
0
,
WM8958
=
1
,
...
...
@@ -55,6 +57,8 @@ struct regulator_bulk_data;
struct
wm8994
{
struct
mutex
irq_lock
;
struct
wm8994_pdata
pdata
;
enum
wm8994_type
type
;
int
revision
;
int
cust_id
;
...
...
include/linux/mfd/wm8994/pdata.h
View file @
06f1c663
...
...
@@ -176,6 +176,11 @@ struct wm8994_pdata {
unsigned
int
lineout1fb
:
1
;
unsigned
int
lineout2fb
:
1
;
/* Delay between detecting a jack and starting microphone
* detect (specified in ms)
*/
int
micdet_delay
;
/* IRQ for microphone detection if brought out directly as a
* signal.
*/
...
...
sound/soc/codecs/wm8958-dsp2.c
View file @
06f1c663
...
...
@@ -195,7 +195,7 @@ static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
static
void
wm8958_dsp_start_mbc
(
struct
snd_soc_codec
*
codec
,
int
path
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
i
;
/* If the DSP is already running then noop */
...
...
@@ -210,9 +210,9 @@ static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
WM8958_DSP2_ENA
,
WM8958_DSP2_ENA
);
/* If we've got user supplied MBC settings use them */
if
(
pdata
&&
pdata
->
num_mbc_cfgs
)
{
if
(
control
->
pdata
.
num_mbc_cfgs
)
{
struct
wm8958_mbc_cfg
*
cfg
=
&
pdata
->
mbc_cfgs
[
wm8994
->
mbc_cfg
];
=
&
control
->
pdata
.
mbc_cfgs
[
wm8994
->
mbc_cfg
];
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
cfg
->
coeff_regs
);
i
++
)
snd_soc_write
(
codec
,
i
+
WM8958_MBC_BAND_1_K_1
,
...
...
@@ -239,7 +239,7 @@ static void wm8958_dsp_start_mbc(struct snd_soc_codec *codec, int path)
static
void
wm8958_dsp_start_vss
(
struct
snd_soc_codec
*
codec
,
int
path
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
i
,
ena
;
if
(
wm8994
->
mbc_vss
)
...
...
@@ -249,26 +249,26 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
WM8958_DSP2_ENA
,
WM8958_DSP2_ENA
);
/* If we've got user supplied settings use them */
if
(
pdata
&&
pdata
->
num_mbc_cfgs
)
{
if
(
control
->
pdata
.
num_mbc_cfgs
)
{
struct
wm8958_mbc_cfg
*
cfg
=
&
pdata
->
mbc_cfgs
[
wm8994
->
mbc_cfg
];
=
&
control
->
pdata
.
mbc_cfgs
[
wm8994
->
mbc_cfg
];
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
cfg
->
combined_regs
);
i
++
)
snd_soc_write
(
codec
,
i
+
0x2800
,
cfg
->
combined_regs
[
i
]);
}
if
(
pdata
&&
pdata
->
num_vss_cfgs
)
{
if
(
control
->
pdata
.
num_vss_cfgs
)
{
struct
wm8958_vss_cfg
*
cfg
=
&
pdata
->
vss_cfgs
[
wm8994
->
vss_cfg
];
=
&
control
->
pdata
.
vss_cfgs
[
wm8994
->
vss_cfg
];
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
cfg
->
regs
);
i
++
)
snd_soc_write
(
codec
,
i
+
0x2600
,
cfg
->
regs
[
i
]);
}
if
(
pdata
&&
pdata
->
num_vss_hpf_cfgs
)
{
if
(
control
->
pdata
.
num_vss_hpf_cfgs
)
{
struct
wm8958_vss_hpf_cfg
*
cfg
=
&
pdata
->
vss_hpf_cfgs
[
wm8994
->
vss_hpf_cfg
];
=
&
control
->
pdata
.
vss_hpf_cfgs
[
wm8994
->
vss_hpf_cfg
];
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
cfg
->
regs
);
i
++
)
snd_soc_write
(
codec
,
i
+
0x2400
,
cfg
->
regs
[
i
]);
...
...
@@ -300,7 +300,7 @@ static void wm8958_dsp_start_vss(struct snd_soc_codec *codec, int path)
static
void
wm8958_dsp_start_enh_eq
(
struct
snd_soc_codec
*
codec
,
int
path
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
i
;
wm8958_dsp2_fw
(
codec
,
"ENH_EQ"
,
wm8994
->
enh_eq
,
false
);
...
...
@@ -309,9 +309,9 @@ static void wm8958_dsp_start_enh_eq(struct snd_soc_codec *codec, int path)
WM8958_DSP2_ENA
,
WM8958_DSP2_ENA
);
/* If we've got user supplied settings use them */
if
(
pdata
&&
pdata
->
num_enh_eq_cfgs
)
{
if
(
control
->
pdata
.
num_enh_eq_cfgs
)
{
struct
wm8958_enh_eq_cfg
*
cfg
=
&
pdata
->
enh_eq_cfgs
[
wm8994
->
enh_eq_cfg
];
=
&
control
->
pdata
.
enh_eq_cfgs
[
wm8994
->
enh_eq_cfg
];
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
cfg
->
regs
);
i
++
)
snd_soc_write
(
codec
,
i
+
0x2200
,
...
...
@@ -458,7 +458,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
value
=
ucontrol
->
value
.
integer
.
value
[
0
];
int
reg
;
...
...
@@ -467,7 +467,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
if
(
reg
<
0
||
reg
&
WM8958_DSP2CLK_ENA
)
return
-
EBUSY
;
if
(
value
>=
pdata
->
num_mbc_cfgs
)
if
(
value
>=
control
->
pdata
.
num_mbc_cfgs
)
return
-
EINVAL
;
wm8994
->
mbc_cfg
=
value
;
...
...
@@ -548,7 +548,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
value
=
ucontrol
->
value
.
integer
.
value
[
0
];
int
reg
;
...
...
@@ -557,7 +557,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
if
(
reg
<
0
||
reg
&
WM8958_DSP2CLK_ENA
)
return
-
EBUSY
;
if
(
value
>=
pdata
->
num_vss_cfgs
)
if
(
value
>=
control
->
pdata
.
num_vss_cfgs
)
return
-
EINVAL
;
wm8994
->
vss_cfg
=
value
;
...
...
@@ -581,7 +581,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
value
=
ucontrol
->
value
.
integer
.
value
[
0
];
int
reg
;
...
...
@@ -590,7 +590,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
if
(
reg
<
0
||
reg
&
WM8958_DSP2CLK_ENA
)
return
-
EBUSY
;
if
(
value
>=
pdata
->
num_vss_hpf_cfgs
)
if
(
value
>=
control
->
pdata
.
num_vss_hpf_cfgs
)
return
-
EINVAL
;
wm8994
->
vss_hpf_cfg
=
value
;
...
...
@@ -748,7 +748,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
value
=
ucontrol
->
value
.
integer
.
value
[
0
];
int
reg
;
...
...
@@ -757,7 +757,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
if
(
reg
<
0
||
reg
&
WM8958_DSP2CLK_ENA
)
return
-
EBUSY
;
if
(
value
>=
pdata
->
num_enh_eq_cfgs
)
if
(
value
>=
control
->
pdata
.
num_enh_eq_cfgs
)
return
-
EINVAL
;
wm8994
->
enh_eq_cfg
=
value
;
...
...
@@ -883,13 +883,6 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
wm8994
->
mbc_vss
=
fw
;
mutex_unlock
(
&
codec
->
mutex
);
}
/* We can't have more than one request outstanding at once so
* we daisy chain.
*/
request_firmware_nowait
(
THIS_MODULE
,
FW_ACTION_HOTPLUG
,
"wm8958_enh_eq.wfw"
,
codec
->
dev
,
GFP_KERNEL
,
codec
,
wm8958_enh_eq_loaded
);
}
static
void
wm8958_mbc_loaded
(
const
struct
firmware
*
fw
,
void
*
context
)
...
...
@@ -897,25 +890,18 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
struct
snd_soc_codec
*
codec
=
context
;
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
if
(
wm8958_dsp2_fw
(
codec
,
"MBC"
,
fw
,
true
)
!=
0
)
return
;
if
(
fw
&&
(
wm8958_dsp2_fw
(
codec
,
"MBC"
,
fw
,
true
)
==
0
))
{
mutex_lock
(
&
codec
->
mutex
);
wm8994
->
mbc
=
fw
;
mutex_unlock
(
&
codec
->
mutex
);
/* We can't have more than one request outstanding at once so
* we daisy chain.
*/
request_firmware_nowait
(
THIS_MODULE
,
FW_ACTION_HOTPLUG
,
"wm8958_mbc_vss.wfw"
,
codec
->
dev
,
GFP_KERNEL
,
codec
,
wm8958_mbc_vss_loaded
);
}
}
void
wm8958_dsp2_init
(
struct
snd_soc_codec
*
codec
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
int
ret
,
i
;
wm8994
->
dsp_active
=
-
1
;
...
...
@@ -932,9 +918,12 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
request_firmware_nowait
(
THIS_MODULE
,
FW_ACTION_HOTPLUG
,
"wm8958_mbc.wfw"
,
codec
->
dev
,
GFP_KERNEL
,
codec
,
wm8958_mbc_loaded
);
if
(
!
pdata
)
return
;
request_firmware_nowait
(
THIS_MODULE
,
FW_ACTION_HOTPLUG
,
"wm8958_mbc_vss.wfw"
,
codec
->
dev
,
GFP_KERNEL
,
codec
,
wm8958_mbc_vss_loaded
);
request_firmware_nowait
(
THIS_MODULE
,
FW_ACTION_HOTPLUG
,
"wm8958_enh_eq.wfw"
,
codec
->
dev
,
GFP_KERNEL
,
codec
,
wm8958_enh_eq_loaded
);
if
(
pdata
->
num_mbc_cfgs
)
{
struct
snd_kcontrol_new
control
[]
=
{
...
...
sound/soc/codecs/wm8994.c
View file @
06f1c663
...
...
@@ -91,8 +91,6 @@ static int wm8994_retune_mobile_base[] = {
WM8994_AIF2_EQ_GAINS_1
,
};
static
void
wm8958_default_micdet
(
u16
status
,
void
*
data
);
static
const
struct
wm8958_micd_rate
micdet_rates
[]
=
{
{
32768
,
true
,
1
,
4
},
{
32768
,
false
,
1
,
1
},
...
...
@@ -110,15 +108,12 @@ static const struct wm8958_micd_rate jackdet_rates[] = {
static
void
wm8958_micd_set_rate
(
struct
snd_soc_codec
*
codec
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
*
control
=
wm8994
->
wm8994
;
int
best
,
i
,
sysclk
,
val
;
bool
idle
;
const
struct
wm8958_micd_rate
*
rates
;
int
num_rates
;
if
(
!
(
wm8994
->
pdata
&&
wm8994
->
pdata
->
micd_rates
)
&&
wm8994
->
jack_cb
!=
wm8958_default_micdet
)
return
;
idle
=
!
wm8994
->
jack_mic
;
sysclk
=
snd_soc_read
(
codec
,
WM8994_CLOCKING_1
);
...
...
@@ -127,9 +122,9 @@ static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
else
sysclk
=
wm8994
->
aifclk
[
0
];
if
(
wm8994
->
pdata
&&
wm8994
->
pdata
->
micd_rates
)
{
rates
=
wm8994
->
pdata
->
micd_rates
;
num_rates
=
wm8994
->
pdata
->
num_micd_rates
;
if
(
control
->
pdata
.
micd_rates
)
{
rates
=
control
->
pdata
.
micd_rates
;
num_rates
=
control
->
pdata
.
num_micd_rates
;
}
else
if
(
wm8994
->
jackdet
)
{
rates
=
jackdet_rates
;
num_rates
=
ARRAY_SIZE
(
jackdet_rates
);
...
...
@@ -326,7 +321,8 @@ static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
static
void
wm8994_set_drc
(
struct
snd_soc_codec
*
codec
,
int
drc
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
int
base
=
wm8994_drc_base
[
drc
];
int
cfg
=
wm8994
->
drc_cfg
[
drc
];
int
save
,
i
;
...
...
@@ -362,7 +358,8 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
int
drc
=
wm8994_get_drc
(
kcontrol
->
id
.
name
);
int
value
=
ucontrol
->
value
.
integer
.
value
[
0
];
...
...
@@ -394,7 +391,8 @@ static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol,
static
void
wm8994_set_retune_mobile
(
struct
snd_soc_codec
*
codec
,
int
block
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
int
base
=
wm8994_retune_mobile_base
[
block
];
int
iface
,
best
,
best_val
,
save
,
i
,
cfg
;
...
...
@@ -465,7 +463,8 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
{
struct
snd_soc_codec
*
codec
=
snd_kcontrol_chip
(
kcontrol
);
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
int
block
=
wm8994_get_retune_mobile_block
(
kcontrol
->
id
.
name
);
int
value
=
ucontrol
->
value
.
integer
.
value
[
0
];
...
...
@@ -736,7 +735,7 @@ static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
if
(
!
wm8994
->
jackdet
||
!
wm8994
->
jack_cb
)
if
(
!
wm8994
->
jackdet
||
!
wm8994
->
micdet
[
0
].
jack
)
return
;
if
(
wm8994
->
active_refcount
)
...
...
@@ -862,7 +861,7 @@ static void vmid_reference(struct snd_soc_codec *codec)
WM8994_BIAS_SRC
|
WM8994_STARTUP_BIAS_ENA
|
WM8994_VMID_BUF_ENA
|
(
0x
3
<<
WM8994_VMID_RAMP_SHIFT
));
(
0x
2
<<
WM8994_VMID_RAMP_SHIFT
));
/* Main bias enable, VMID=2x40k */
snd_soc_update_bits
(
codec
,
WM8994_POWER_MANAGEMENT_1
,
...
...
@@ -870,7 +869,7 @@ static void vmid_reference(struct snd_soc_codec *codec)
WM8994_VMID_SEL_MASK
,
WM8994_BIAS_ENA
|
0x2
);
msleep
(
5
0
);
msleep
(
30
0
);
snd_soc_update_bits
(
codec
,
WM8994_ANTIPOP_2
,
WM8994_VMID_RAMP_MASK
|
...
...
@@ -939,16 +938,10 @@ static void vmid_dereference(struct snd_soc_codec *codec)
WM8994_BIAS_SRC
|
WM8994_VMID_DISCH
);
switch
(
wm8994
->
vmid_mode
)
{
case
WM8994_VMID_FORCE
:
msleep
(
350
);
break
;
default:
break
;
}
snd_soc_update_bits
(
codec
,
WM8994_POWER_MANAGEMENT_1
,
WM8994_VMID_SEL_MASK
,
0
);
snd_soc_update_bits
(
codec
,
WM8994_ADDITIONAL_CONTROL
,
WM8994_VROI
,
WM8994_VROI
);
msleep
(
400
);
/* Active discharge */
snd_soc_update_bits
(
codec
,
WM8994_ANTIPOP_1
,
...
...
@@ -957,17 +950,12 @@ static void vmid_dereference(struct snd_soc_codec *codec)
WM8994_LINEOUT1_DISCH
|
WM8994_LINEOUT2_DISCH
);
msleep
(
150
);
snd_soc_update_bits
(
codec
,
WM8994_POWER_MANAGEMENT_3
,
WM8994_LINEOUT1N_ENA
|
WM8994_LINEOUT1P_ENA
|
WM8994_LINEOUT2N_ENA
|
WM8994_LINEOUT2P_ENA
,
0
);
snd_soc_update_bits
(
codec
,
WM8994_ADDITIONAL_CONTROL
,
WM8994_VROI
,
0
);
/* Switch off startup biases */
snd_soc_update_bits
(
codec
,
WM8994_ANTIPOP_2
,
WM8994_BIAS_SRC
|
...
...
@@ -976,10 +964,7 @@ static void vmid_dereference(struct snd_soc_codec *codec)
WM8994_VMID_RAMP_MASK
,
0
);
snd_soc_update_bits
(
codec
,
WM8994_POWER_MANAGEMENT_1
,
WM8994_BIAS_ENA
|
WM8994_VMID_SEL_MASK
,
0
);
snd_soc_update_bits
(
codec
,
WM8994_ANTIPOP_2
,
WM8994_VMID_RAMP_MASK
,
0
);
WM8994_VMID_SEL_MASK
,
0
);
}
pm_runtime_put
(
codec
->
dev
);
...
...
@@ -2277,6 +2262,18 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
configure_clock
(
codec
);
/*
* If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
* for detection.
*/
if
(
max
(
wm8994
->
aifclk
[
0
],
wm8994
->
aifclk
[
1
])
<
50000
)
{
dev_dbg
(
codec
->
dev
,
"Configuring AIFs for 128fs
\n
"
);
snd_soc_update_bits
(
codec
,
WM8994_AIF1_RATE
,
WM8994_AIF1CLK_RATE_MASK
,
0x1
);
snd_soc_update_bits
(
codec
,
WM8994_AIF2_RATE
,
WM8994_AIF2CLK_RATE_MASK
,
0x1
);
}
return
0
;
}
...
...
@@ -2365,6 +2362,18 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
configure_clock
(
codec
);
/*
* If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
* for detection.
*/
if
(
max
(
wm8994
->
aifclk
[
0
],
wm8994
->
aifclk
[
1
])
<
50000
)
{
dev_dbg
(
codec
->
dev
,
"Configuring AIFs for 128fs
\n
"
);
snd_soc_update_bits
(
codec
,
WM8994_AIF1_RATE
,
WM8994_AIF1CLK_RATE_MASK
,
0x1
);
snd_soc_update_bits
(
codec
,
WM8994_AIF2_RATE
,
WM8994_AIF2CLK_RATE_MASK
,
0x1
);
}
return
0
;
}
...
...
@@ -3082,7 +3091,8 @@ static int wm8994_codec_resume(struct snd_soc_codec *codec)
static
void
wm8994_handle_retune_mobile_pdata
(
struct
wm8994_priv
*
wm8994
)
{
struct
snd_soc_codec
*
codec
=
wm8994
->
hubs
.
codec
;
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
struct
snd_kcontrol_new
controls
[]
=
{
SOC_ENUM_EXT
(
"AIF1.1 EQ Mode"
,
wm8994
->
retune_mobile_enum
,
...
...
@@ -3149,7 +3159,8 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
static
void
wm8994_handle_pdata
(
struct
wm8994_priv
*
wm8994
)
{
struct
snd_soc_codec
*
codec
=
wm8994
->
hubs
.
codec
;
struct
wm8994_pdata
*
pdata
=
wm8994
->
pdata
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
wm8994_pdata
*
pdata
=
&
control
->
pdata
;
int
ret
,
i
;
if
(
!
pdata
)
...
...
@@ -3389,38 +3400,80 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
return
IRQ_HANDLED
;
}
/* Default microphone detection handler for WM8958 - the user can
* override this if they wish.
*/
static
void
wm8958_default_micdet
(
u16
status
,
void
*
data
)
static
void
wm1811_micd_stop
(
struct
snd_soc_codec
*
codec
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
if
(
!
wm8994
->
jackdet
)
return
;
mutex_lock
(
&
wm8994
->
accdet_lock
);
snd_soc_update_bits
(
codec
,
WM8958_MIC_DETECT_1
,
WM8958_MICD_ENA
,
0
);
wm1811_jackdet_set_mode
(
codec
,
WM1811_JACKDET_MODE_JACK
);
mutex_unlock
(
&
wm8994
->
accdet_lock
);
if
(
wm8994
->
wm8994
->
pdata
.
jd_ext_cap
)
snd_soc_dapm_disable_pin
(
&
codec
->
dapm
,
"MICBIAS2"
);
}
static
void
wm8958_button_det
(
struct
snd_soc_codec
*
codec
,
u16
status
)
{
struct
snd_soc_codec
*
codec
=
data
;
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
int
report
;
dev_dbg
(
codec
->
dev
,
"MICDET %x
\n
"
,
status
);
report
=
0
;
if
(
status
&
0x4
)
report
|=
SND_JACK_BTN_0
;
if
(
status
&
0x8
)
report
|=
SND_JACK_BTN_1
;
if
(
status
&
0x10
)
report
|=
SND_JACK_BTN_2
;
if
(
status
&
0x20
)
report
|=
SND_JACK_BTN_3
;
if
(
status
&
0x40
)
report
|=
SND_JACK_BTN_4
;
if
(
status
&
0x80
)
report
|=
SND_JACK_BTN_5
;
snd_soc_jack_report
(
wm8994
->
micdet
[
0
].
jack
,
report
,
wm8994
->
btn_mask
);
}
static
void
wm8958_mic_id
(
void
*
data
,
u16
status
)
{
struct
snd_soc_codec
*
codec
=
data
;
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
/* Either nothing present or just starting detection */
if
(
!
(
status
&
WM8958_MICD_STS
))
{
if
(
!
wm8994
->
jackdet
)
{
/* If nothing present then clear our statuses */
dev_dbg
(
codec
->
dev
,
"Detected open circuit
\n
"
);
wm8994
->
jack_mic
=
false
;
wm8994
->
mic_detecting
=
true
;
wm1811_micd_stop
(
codec
);
wm8958_micd_set_rate
(
codec
);
snd_soc_jack_report
(
wm8994
->
micdet
[
0
].
jack
,
0
,
wm8994
->
btn_mask
|
SND_JACK_HEADSET
);
}
return
;
}
/* If the measurement is showing a high impedence we've got a
* microphone.
*/
if
(
wm8994
->
mic_detecting
&&
(
status
&
0x600
)
)
{
if
(
status
&
0x600
)
{
dev_dbg
(
codec
->
dev
,
"Detected microphone
\n
"
);
wm8994
->
mic_detecting
=
false
;
...
...
@@ -3433,64 +3486,67 @@ static void wm8958_default_micdet(u16 status, void *data)
}
if
(
wm8994
->
mic_detecting
&&
status
&
0xfc
)
{
if
(
status
&
0xfc
)
{
dev_dbg
(
codec
->
dev
,
"Detected headphone
\n
"
);
wm8994
->
mic_detecting
=
false
;
wm8958_micd_set_rate
(
codec
);
/* If we have jackdet that will detect removal */
if
(
wm8994
->
jackdet
)
{
mutex_lock
(
&
wm8994
->
accdet_lock
);
snd_soc_update_bits
(
codec
,
WM8958_MIC_DETECT_1
,
WM8958_MICD_ENA
,
0
);
wm1811_jackdet_set_mode
(
codec
,
WM1811_JACKDET_MODE_JACK
);
mutex_unlock
(
&
wm8994
->
accdet_lock
);
if
(
wm8994
->
pdata
->
jd_ext_cap
)
snd_soc_dapm_disable_pin
(
&
codec
->
dapm
,
"MICBIAS2"
);
}
wm1811_micd_stop
(
codec
);
snd_soc_jack_report
(
wm8994
->
micdet
[
0
].
jack
,
SND_JACK_HEADPHONE
,
SND_JACK_HEADSET
);
}
}
/* Report short circuit as a button */
if
(
wm8994
->
jack_mic
)
{
report
=
0
;
if
(
status
&
0x4
)
report
|=
SND_JACK_BTN_0
;
/* Deferred mic detection to allow for extra settling time */
static
void
wm1811_mic_work
(
struct
work_struct
*
work
)
{
struct
wm8994_priv
*
wm8994
=
container_of
(
work
,
struct
wm8994_priv
,
mic_work
.
work
);
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
snd_soc_codec
*
codec
=
wm8994
->
hubs
.
codec
;
if
(
status
&
0x8
)
report
|=
SND_JACK_BTN_1
;
pm_runtime_get_sync
(
codec
->
dev
);
if
(
status
&
0x10
)
report
|=
SND_JACK_BTN_2
;
/* If required for an external cap force MICBIAS on */
if
(
control
->
pdata
.
jd_ext_cap
)
{
snd_soc_dapm_force_enable_pin
(
&
codec
->
dapm
,
"MICBIAS2"
);
snd_soc_dapm_sync
(
&
codec
->
dapm
);
}
if
(
status
&
0x20
)
report
|=
SND_JACK_BTN_3
;
mutex_lock
(
&
wm8994
->
accdet_lock
);
if
(
status
&
0x40
)
report
|=
SND_JACK_BTN_4
;
dev_dbg
(
codec
->
dev
,
"Starting mic detection
\n
"
);
if
(
status
&
0x80
)
report
|=
SND_JACK_BTN_5
;
/* Use a user-supplied callback if we have one */
if
(
wm8994
->
micd_cb
)
{
wm8994
->
micd_cb
(
wm8994
->
micd_cb_data
);
}
else
{
/*
* Start off measument of microphone impedence to find out
* what's actually there.
*/
wm8994
->
mic_detecting
=
true
;
wm1811_jackdet_set_mode
(
codec
,
WM1811_JACKDET_MODE_MIC
);
snd_soc_
jack_report
(
wm8994
->
micdet
[
0
].
jack
,
report
,
wm8994
->
btn_mask
);
snd_soc_
update_bits
(
codec
,
WM8958_MIC_DETECT_1
,
WM8958_MICD_ENA
,
WM8958_MICD_ENA
);
}
mutex_unlock
(
&
wm8994
->
accdet_lock
);
pm_runtime_put
(
codec
->
dev
);
}
static
irqreturn_t
wm1811_jackdet_irq
(
int
irq
,
void
*
data
)
{
struct
wm8994_priv
*
wm8994
=
data
;
struct
wm8994
*
control
=
wm8994
->
wm8994
;
struct
snd_soc_codec
*
codec
=
wm8994
->
hubs
.
codec
;
int
reg
;
int
reg
,
delay
;
bool
present
;
pm_runtime_get_sync
(
codec
->
dev
);
...
...
@@ -3521,18 +3577,14 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
snd_soc_update_bits
(
codec
,
WM1811_JACKDET_CTRL
,
WM1811_JACKDET_DB
,
0
);
/*
* Start off measument of microphone impedence to find
* out what's actually there.
*/
wm8994
->
mic_detecting
=
true
;
wm1811_jackdet_set_mode
(
codec
,
WM1811_JACKDET_MODE_MIC
);
snd_soc_update_bits
(
codec
,
WM8958_MIC_DETECT_1
,
WM8958_MICD_ENA
,
WM8958_MICD_ENA
);
delay
=
control
->
pdata
.
micdet_delay
;
schedule_delayed_work
(
&
wm8994
->
mic_work
,
msecs_to_jiffies
(
delay
));
}
else
{
dev_dbg
(
codec
->
dev
,
"Jack not detected
\n
"
);
cancel_delayed_work_sync
(
&
wm8994
->
mic_work
);
snd_soc_update_bits
(
codec
,
WM8958_MICBIAS2
,
WM8958_MICB2_DISCH
,
WM8958_MICB2_DISCH
);
...
...
@@ -3549,14 +3601,9 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
mutex_unlock
(
&
wm8994
->
accdet_lock
);
/* If required for an external cap force MICBIAS on */
if
(
wm8994
->
pdata
->
jd_ext_cap
)
{
if
(
present
)
snd_soc_dapm_force_enable_pin
(
&
codec
->
dapm
,
"MICBIAS2"
);
else
/* Turn off MICBIAS if it was on for an external cap */
if
(
control
->
pdata
.
jd_ext_cap
&&
!
present
)
snd_soc_dapm_disable_pin
(
&
codec
->
dapm
,
"MICBIAS2"
);
}
if
(
present
)
snd_soc_jack_report
(
wm8994
->
micdet
[
0
].
jack
,
...
...
@@ -3599,7 +3646,8 @@ static void wm1811_jackdet_bootstrap(struct work_struct *work)
* detection algorithm.
*/
int
wm8958_mic_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
,
wm8958_micdet_cb
cb
,
void
*
cb_data
)
wm1811_micdet_cb
det_cb
,
void
*
det_cb_data
,
wm1811_mic_id_cb
id_cb
,
void
*
id_cb_data
)
{
struct
wm8994_priv
*
wm8994
=
snd_soc_codec_get_drvdata
(
codec
);
struct
wm8994
*
control
=
wm8994
->
wm8994
;
...
...
@@ -3614,27 +3662,32 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
}
if
(
jack
)
{
if
(
!
cb
)
{
dev_dbg
(
codec
->
dev
,
"Using default micdet callback
\n
"
);
cb
=
wm8958_default_micdet
;
cb_data
=
codec
;
}
snd_soc_dapm_force_enable_pin
(
&
codec
->
dapm
,
"CLK_SYS"
);
snd_soc_dapm_sync
(
&
codec
->
dapm
);
wm8994
->
micdet
[
0
].
jack
=
jack
;
wm8994
->
jack_cb
=
cb
;
wm8994
->
jack_cb_data
=
cb_data
;
if
(
det_cb
)
{
wm8994
->
micd_cb
=
det_cb
;
wm8994
->
micd_cb_data
=
det_cb_data
;
}
else
{
wm8994
->
mic_detecting
=
true
;
wm8994
->
jack_mic
=
false
;
}
if
(
id_cb
)
{
wm8994
->
mic_id_cb
=
id_cb
;
wm8994
->
mic_id_cb_data
=
id_cb_data
;
}
else
{
wm8994
->
mic_id_cb
=
wm8958_mic_id
;
wm8994
->
mic_id_cb_data
=
codec
;
}
wm8958_micd_set_rate
(
codec
);
/* Detect microphones and short circuits by default */
if
(
wm8994
->
pdata
->
micd_lvl_sel
)
micd_lvl_sel
=
wm8994
->
pdata
->
micd_lvl_sel
;
if
(
control
->
pdata
.
micd_lvl_sel
)
micd_lvl_sel
=
control
->
pdata
.
micd_lvl_sel
;
else
micd_lvl_sel
=
0x41
;
...
...
@@ -3728,10 +3781,22 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
trace_snd_soc_jack_irq
(
dev_name
(
codec
->
dev
));
#endif
if
(
wm8994
->
jack_cb
)
wm8994
->
jack_cb
(
reg
,
wm8994
->
jack_cb_data
);
/* Avoid a transient report when the accessory is being removed */
if
(
wm8994
->
jackdet
)
{
reg
=
snd_soc_read
(
codec
,
WM1811_JACKDET_CTRL
);
if
(
reg
<
0
)
{
dev_err
(
codec
->
dev
,
"Failed to read jack status: %d
\n
"
,
reg
);
}
else
if
(
!
(
reg
&
WM1811_JACKDET_LVL
))
{
dev_dbg
(
codec
->
dev
,
"Ignoring removed jack
\n
"
);
return
IRQ_HANDLED
;
}
}
if
(
wm8994
->
mic_detecting
)
wm8994
->
mic_id_cb
(
wm8994
->
mic_id_cb_data
,
reg
);
else
dev_warn
(
codec
->
dev
,
"Accessory detection with no callback
\n
"
);
wm8958_button_det
(
codec
,
reg
);
out:
pm_runtime_put
(
codec
->
dev
);
...
...
@@ -3779,15 +3844,24 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
snd_soc_codec_set_cache_io
(
codec
,
16
,
16
,
SND_SOC_REGMAP
);
mutex_init
(
&
wm8994
->
accdet_lock
);
INIT_DELAYED_WORK
(
&
wm8994
->
mic_work
,
wm8994_mic_work
);
INIT_DELAYED_WORK
(
&
wm8994
->
jackdet_bootstrap
,
wm1811_jackdet_bootstrap
);
switch
(
control
->
type
)
{
case
WM8994
:
INIT_DELAYED_WORK
(
&
wm8994
->
mic_work
,
wm8994_mic_work
);
break
;
case
WM1811
:
INIT_DELAYED_WORK
(
&
wm8994
->
mic_work
,
wm1811_mic_work
);
break
;
default:
break
;
}
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
wm8994
->
fll_locked
);
i
++
)
init_completion
(
&
wm8994
->
fll_locked
[
i
]);
if
(
wm8994
->
pdata
&&
wm8994
->
pdata
->
micdet_irq
)
wm8994
->
micdet_irq
=
wm8994
->
pdata
->
micdet_irq
;
wm8994
->
micdet_irq
=
control
->
pdata
.
micdet_irq
;
pm_runtime_enable
(
codec
->
dev
);
pm_runtime_idle
(
codec
->
dev
);
...
...
@@ -3800,8 +3874,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
switch
(
control
->
type
)
{
case
WM8994
:
/* Single ended line outputs should have VMID on. */
if
(
!
wm8994
->
pdata
->
lineout1_diff
||
!
wm8994
->
pdata
->
lineout2_diff
)
if
(
!
control
->
pdata
.
lineout1_diff
||
!
control
->
pdata
.
lineout2_diff
)
codec
->
dapm
.
idle_bias_off
=
0
;
switch
(
wm8994
->
revision
)
{
...
...
@@ -3839,20 +3913,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
wm8994
->
hubs
.
no_cache_dac_hp_direct
=
true
;
wm8994
->
fll_byp
=
true
;
switch
(
control
->
cust_id
)
{
case
0
:
case
2
:
wm8994
->
hubs
.
dcs_codes_l
=
-
9
;
wm8994
->
hubs
.
dcs_codes_r
=
-
7
;
break
;
case
1
:
case
3
:
wm8994
->
hubs
.
dcs_codes_l
=
-
8
;
wm8994
->
hubs
.
dcs_codes_r
=
-
7
;
break
;
default:
break
;
}
snd_soc_update_bits
(
codec
,
WM8994_ANALOGUE_HP_1
,
WM1811_HPOUT1_ATTN
,
WM1811_HPOUT1_ATTN
);
...
...
@@ -4236,7 +4298,6 @@ static int __devinit wm8994_probe(struct platform_device *pdev)
platform_set_drvdata
(
pdev
,
wm8994
);
wm8994
->
wm8994
=
dev_get_drvdata
(
pdev
->
dev
.
parent
);
wm8994
->
pdata
=
dev_get_platdata
(
pdev
->
dev
.
parent
);
return
snd_soc_register_codec
(
&
pdev
->
dev
,
&
soc_codec_dev_wm8994
,
wm8994_dai
,
ARRAY_SIZE
(
wm8994_dai
));
...
...
@@ -4266,7 +4327,7 @@ static int wm8994_resume(struct device *dev)
{
struct
wm8994_priv
*
wm8994
=
dev_get_drvdata
(
dev
);
if
(
wm8994
->
jackdet
&&
wm8994
->
jack
_cb
)
if
(
wm8994
->
jackdet
&&
wm8994
->
jack
det_mode
)
regmap_update_bits
(
wm8994
->
wm8994
->
regmap
,
WM8994_ANTIPOP_2
,
WM1811_JACKDET_MODE_MASK
,
WM1811_JACKDET_MODE_AUDIO
);
...
...
sound/soc/codecs/wm8994.h
View file @
06f1c663
...
...
@@ -39,12 +39,14 @@ enum wm8994_vmid_mode {
WM8994_VMID_FORCE
,
};
typedef
void
(
*
wm8958_micdet_cb
)(
u16
status
,
void
*
data
);
typedef
void
(
*
wm1811_micdet_cb
)(
void
*
data
);
typedef
void
(
*
wm1811_mic_id_cb
)(
void
*
data
,
u16
status
);
int
wm8994_mic_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
,
int
micbias
);
int
wm8958_mic_detect
(
struct
snd_soc_codec
*
codec
,
struct
snd_soc_jack
*
jack
,
wm8958_micdet_cb
cb
,
void
*
cb_data
);
wm1811_micdet_cb
cb
,
void
*
det_cb_data
,
wm1811_mic_id_cb
id_cb
,
void
*
id_cb_data
);
int
wm8994_vmid_mode
(
struct
snd_soc_codec
*
codec
,
enum
wm8994_vmid_mode
mode
);
...
...
@@ -138,12 +140,13 @@ struct wm8994_priv {
int
jackdet_mode
;
struct
delayed_work
jackdet_bootstrap
;
wm8958_micdet_cb
jack_cb
;
void
*
jack_cb_data
;
int
micdet_irq
;
wm1811_micdet_cb
micd_cb
;
void
*
micd_cb_data
;
wm1811_mic_id_cb
mic_id_cb
;
void
*
mic_id_cb_data
;
int
revision
;
struct
wm8994_pdata
*
pdata
;
unsigned
int
aif1clk_enable
:
1
;
unsigned
int
aif2clk_enable
:
1
;
...
...
sound/soc/samsung/littlemill.c
View file @
06f1c663
...
...
@@ -270,7 +270,7 @@ static int littlemill_late_probe(struct snd_soc_card *card)
return
ret
;
/* This will check device compatibility itself */
wm8958_mic_detect
(
codec
,
&
littlemill_headset
,
NULL
,
NULL
);
wm8958_mic_detect
(
codec
,
&
littlemill_headset
,
NULL
,
NULL
,
NULL
,
NULL
);
/* As will this */
wm8994_mic_detect
(
codec
,
&
littlemill_headset
,
1
);
...
...
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