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
85eb5dce
Commit
85eb5dce
authored
Oct 24, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/cs42l73' into asoc-next
parents
05be686a
7b09eea5
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
173 additions
and
90 deletions
+173
-90
Documentation/devicetree/bindings/sound/cs42l73.txt
Documentation/devicetree/bindings/sound/cs42l73.txt
+22
-0
include/sound/cs42l73.h
include/sound/cs42l73.h
+22
-0
sound/soc/codecs/cs42l73.c
sound/soc/codecs/cs42l73.c
+77
-37
sound/soc/codecs/cs42l73.h
sound/soc/codecs/cs42l73.h
+52
-53
No files found.
Documentation/devicetree/bindings/sound/cs42l73.txt
0 → 100644
View file @
85eb5dce
CS42L73 audio CODEC
Required properties:
- compatible : "cirrus,cs42l73"
- reg : the I2C address of the device for I2C
Optional properties:
- reset_gpio : a GPIO spec for the reset pin.
- chgfreq : Charge Pump Frequency values 0x00-0x0F
Example:
codec: cs42l73@4a {
compatible = "cirrus,cs42l73";
reg = <0x4a>;
reset_gpio = <&gpio 10 0>;
chgfreq = <0x05>;
};
\ No newline at end of file
include/sound/cs42l73.h
0 → 100644
View file @
85eb5dce
/*
* linux/sound/cs42l73.h -- Platform data for CS42L73
*
* Copyright (c) 2012 Cirrus Logic Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __CS42L73_H
#define __CS42L73_H
struct
cs42l73_platform_data
{
/* RST GPIO */
unsigned
int
reset_gpio
;
unsigned
int
chgfreq
;
int
jack_detection
;
unsigned
int
mclk_freq
;
};
#endif
/* __CS42L73_H */
sound/soc/codecs/cs42l73.c
View file @
85eb5dce
...
...
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
...
...
@@ -28,6 +29,7 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/cs42l73.h>
#include "cs42l73.h"
struct
sp_config
{
...
...
@@ -35,6 +37,7 @@ struct sp_config {
u32
srate
;
};
struct
cs42l73_private
{
struct
cs42l73_platform_data
pdata
;
struct
sp_config
config
[
3
];
struct
regmap
*
regmap
;
u32
sysclk
;
...
...
@@ -310,15 +313,6 @@ static const struct soc_enum ng_delay_enum =
SOC_ENUM_SINGLE
(
CS42L73_NGCAB
,
0
,
ARRAY_SIZE
(
cs42l73_ng_delay_text
),
cs42l73_ng_delay_text
);
static
const
char
*
const
charge_pump_freq_text
[]
=
{
"0"
,
"1"
,
"2"
,
"3"
,
"4"
,
"5"
,
"6"
,
"7"
,
"8"
,
"9"
,
"10"
,
"11"
,
"12"
,
"13"
,
"14"
,
"15"
};
static
const
struct
soc_enum
charge_pump_enum
=
SOC_ENUM_SINGLE
(
CS42L73_CPFCHC
,
4
,
ARRAY_SIZE
(
charge_pump_freq_text
),
charge_pump_freq_text
);
static
const
char
*
const
cs42l73_mono_mix_texts
[]
=
{
"Left"
,
"Right"
,
"Mono Mix"
};
...
...
@@ -511,8 +505,6 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
SOC_SINGLE
(
"NG Threshold"
,
CS42L73_NGCAB
,
2
,
7
,
0
),
SOC_ENUM
(
"NG Delay"
,
ng_delay_enum
),
SOC_ENUM
(
"Charge Pump Frequency"
,
charge_pump_enum
),
SOC_DOUBLE_R_TLV
(
"XSP-IP Volume"
,
CS42L73_XSPAIPAA
,
CS42L73_XSPBIPBA
,
0
,
0x3F
,
1
,
attn_tlv
),
...
...
@@ -1055,11 +1047,11 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
switch
(
fmt
&
SND_SOC_DAIFMT_MASTER_MASK
)
{
case
SND_SOC_DAIFMT_CBM_CFM
:
mmcc
|=
MS_MASTER
;
mmcc
|=
CS42L73_
MS_MASTER
;
break
;
case
SND_SOC_DAIFMT_CBS_CFS
:
mmcc
&=
~
MS_MASTER
;
mmcc
&=
~
CS42L73_
MS_MASTER
;
break
;
default:
...
...
@@ -1071,11 +1063,11 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
switch
(
format
)
{
case
SND_SOC_DAIFMT_I2S
:
spc
&=
~
SPDIF_PCM
;
spc
&=
~
CS42L73_
SPDIF_PCM
;
break
;
case
SND_SOC_DAIFMT_DSP_A
:
case
SND_SOC_DAIFMT_DSP_B
:
if
(
mmcc
&
MS_MASTER
)
{
if
(
mmcc
&
CS42L73_
MS_MASTER
)
{
dev_err
(
codec
->
dev
,
"PCM format in slave mode only
\n
"
);
return
-
EINVAL
;
...
...
@@ -1085,25 +1077,25 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
"PCM format is not supported on ASP port
\n
"
);
return
-
EINVAL
;
}
spc
|=
SPDIF_PCM
;
spc
|=
CS42L73_
SPDIF_PCM
;
break
;
default:
return
-
EINVAL
;
}
if
(
spc
&
SPDIF_PCM
)
{
if
(
spc
&
CS42L73_
SPDIF_PCM
)
{
/* Clear PCM mode, clear PCM_BIT_ORDER bit for MSB->LSB */
spc
&=
~
(
PCM_MODE_MASK
|
PCM_BIT_ORDER
);
spc
&=
~
(
CS42L73_PCM_MODE_MASK
|
CS42L73_
PCM_BIT_ORDER
);
switch
(
format
)
{
case
SND_SOC_DAIFMT_DSP_B
:
if
(
inv
==
SND_SOC_DAIFMT_IB_IF
)
spc
|=
PCM_MODE0
;
spc
|=
CS42L73_
PCM_MODE0
;
if
(
inv
==
SND_SOC_DAIFMT_IB_NF
)
spc
|=
PCM_MODE1
;
spc
|=
CS42L73_
PCM_MODE1
;
break
;
case
SND_SOC_DAIFMT_DSP_A
:
if
(
inv
==
SND_SOC_DAIFMT_IB_IF
)
spc
|=
PCM_MODE1
;
spc
|=
CS42L73_
PCM_MODE1
;
break
;
default:
return
-
EINVAL
;
...
...
@@ -1163,7 +1155,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
int
mclk_coeff
;
int
srate
=
params_rate
(
params
);
if
(
priv
->
config
[
id
].
mmcc
&
MS_MASTER
)
{
if
(
priv
->
config
[
id
].
mmcc
&
CS42L73_
MS_MASTER
)
{
/* CS42L73 Master */
/* MCLK -> srate */
mclk_coeff
=
...
...
@@ -1182,13 +1174,13 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
priv
->
config
[
id
].
spc
&=
0xFC
;
/* Use SCLK=64*Fs if internal MCLK >= 6.4MHz */
if
(
priv
->
mclk
>=
6400000
)
priv
->
config
[
id
].
spc
|=
MCK_SCLK_64FS
;
priv
->
config
[
id
].
spc
|=
CS42L73_
MCK_SCLK_64FS
;
else
priv
->
config
[
id
].
spc
|=
MCK_SCLK_MCLK
;
priv
->
config
[
id
].
spc
|=
CS42L73_
MCK_SCLK_MCLK
;
}
else
{
/* CS42L73 Slave */
priv
->
config
[
id
].
spc
&=
0xFC
;
priv
->
config
[
id
].
spc
|=
MCK_SCLK_64FS
;
priv
->
config
[
id
].
spc
|=
CS42L73_
MCK_SCLK_64FS
;
}
/* Update ASRCs */
priv
->
config
[
id
].
srate
=
srate
;
...
...
@@ -1208,8 +1200,8 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
switch
(
level
)
{
case
SND_SOC_BIAS_ON
:
snd_soc_update_bits
(
codec
,
CS42L73_DMMCC
,
MCLKDIS
,
0
);
snd_soc_update_bits
(
codec
,
CS42L73_PWRCTL1
,
PDN
,
0
);
snd_soc_update_bits
(
codec
,
CS42L73_DMMCC
,
CS42L73_
MCLKDIS
,
0
);
snd_soc_update_bits
(
codec
,
CS42L73_PWRCTL1
,
CS42L73_
PDN
,
0
);
break
;
case
SND_SOC_BIAS_PREPARE
:
...
...
@@ -1220,11 +1212,11 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
regcache_cache_only
(
cs42l73
->
regmap
,
false
);
regcache_sync
(
cs42l73
->
regmap
);
}
snd_soc_update_bits
(
codec
,
CS42L73_PWRCTL1
,
PDN
,
1
);
snd_soc_update_bits
(
codec
,
CS42L73_PWRCTL1
,
CS42L73_
PDN
,
1
);
break
;
case
SND_SOC_BIAS_OFF
:
snd_soc_update_bits
(
codec
,
CS42L73_PWRCTL1
,
PDN
,
1
);
snd_soc_update_bits
(
codec
,
CS42L73_PWRCTL1
,
CS42L73_
PDN
,
1
);
if
(
cs42l73
->
shutdwn_delay
>
0
)
{
mdelay
(
cs42l73
->
shutdwn_delay
);
cs42l73
->
shutdwn_delay
=
0
;
...
...
@@ -1233,7 +1225,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
* down.
*/
}
snd_soc_update_bits
(
codec
,
CS42L73_DMMCC
,
MCLKDIS
,
1
);
snd_soc_update_bits
(
codec
,
CS42L73_DMMCC
,
CS42L73_
MCLKDIS
,
1
);
break
;
}
codec
->
dapm
.
bias_level
=
level
;
...
...
@@ -1367,11 +1359,16 @@ static int cs42l73_probe(struct snd_soc_codec *codec)
return
ret
;
}
regcache_cache_only
(
cs42l73
->
regmap
,
true
);
cs42l73_set_bias_level
(
codec
,
SND_SOC_BIAS_STANDBY
);
cs42l73
->
mclksel
=
CS42L73_CLKID_MCLK1
;
/* MCLK1 as master clk */
/* Set Charge Pump Frequency */
if
(
cs42l73
->
pdata
.
chgfreq
)
snd_soc_update_bits
(
codec
,
CS42L73_CPFCHC
,
CS42L73_CHARGEPUMP_MASK
,
cs42l73
->
pdata
.
chgfreq
<<
4
);
/* MCLK1 as master clk */
cs42l73
->
mclksel
=
CS42L73_CLKID_MCLK1
;
cs42l73
->
mclk
=
0
;
return
ret
;
...
...
@@ -1415,9 +1412,11 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
const
struct
i2c_device_id
*
id
)
{
struct
cs42l73_private
*
cs42l73
;
struct
cs42l73_platform_data
*
pdata
=
dev_get_platdata
(
&
i2c_client
->
dev
);
int
ret
;
unsigned
int
devid
=
0
;
unsigned
int
reg
;
u32
val32
;
cs42l73
=
devm_kzalloc
(
&
i2c_client
->
dev
,
sizeof
(
struct
cs42l73_private
),
GFP_KERNEL
);
...
...
@@ -1426,14 +1425,49 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
return
-
ENOMEM
;
}
i2c_set_clientdata
(
i2c_client
,
cs42l73
);
cs42l73
->
regmap
=
devm_regmap_init_i2c
(
i2c_client
,
&
cs42l73_regmap
);
if
(
IS_ERR
(
cs42l73
->
regmap
))
{
ret
=
PTR_ERR
(
cs42l73
->
regmap
);
dev_err
(
&
i2c_client
->
dev
,
"regmap_init() failed: %d
\n
"
,
ret
);
return
ret
;
}
if
(
pdata
)
{
cs42l73
->
pdata
=
*
pdata
;
}
else
{
pdata
=
devm_kzalloc
(
&
i2c_client
->
dev
,
sizeof
(
struct
cs42l73_platform_data
),
GFP_KERNEL
);
if
(
!
pdata
)
{
dev_err
(
&
i2c_client
->
dev
,
"could not allocate pdata
\n
"
);
return
-
ENOMEM
;
}
if
(
i2c_client
->
dev
.
of_node
)
{
if
(
of_property_read_u32
(
i2c_client
->
dev
.
of_node
,
"chgfreq"
,
&
val32
)
>=
0
)
pdata
->
chgfreq
=
val32
;
}
pdata
->
reset_gpio
=
of_get_named_gpio
(
i2c_client
->
dev
.
of_node
,
"reset-gpio"
,
0
);
cs42l73
->
pdata
=
*
pdata
;
}
i2c_set_clientdata
(
i2c_client
,
cs42l73
);
if
(
cs42l73
->
pdata
.
reset_gpio
)
{
ret
=
gpio_request_one
(
cs42l73
->
pdata
.
reset_gpio
,
GPIOF_OUT_INIT_HIGH
,
"CS42L73 /RST"
);
if
(
ret
<
0
)
{
dev_err
(
&
i2c_client
->
dev
,
"Failed to request /RST %d: %d
\n
"
,
cs42l73
->
pdata
.
reset_gpio
,
ret
);
return
ret
;
}
gpio_set_value_cansleep
(
cs42l73
->
pdata
.
reset_gpio
,
0
);
gpio_set_value_cansleep
(
cs42l73
->
pdata
.
reset_gpio
,
1
);
}
regcache_cache_bypass
(
cs42l73
->
regmap
,
true
);
/* initialize codec */
ret
=
regmap_read
(
cs42l73
->
regmap
,
CS42L73_DEVID_AB
,
&
reg
);
devid
=
(
reg
&
0xFF
)
<<
12
;
...
...
@@ -1444,7 +1478,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
ret
=
regmap_read
(
cs42l73
->
regmap
,
CS42L73_DEVID_E
,
&
reg
);
devid
|=
(
reg
&
0xF0
)
>>
4
;
if
(
devid
!=
CS42L73_DEVID
)
{
ret
=
-
ENODEV
;
dev_err
(
&
i2c_client
->
dev
,
...
...
@@ -1462,7 +1495,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
dev_info
(
&
i2c_client
->
dev
,
"Cirrus Logic CS42L73, Revision: %02X
\n
"
,
reg
&
0xFF
);
regcache_cache_
only
(
cs42l73
->
regmap
,
tru
e
);
regcache_cache_
bypass
(
cs42l73
->
regmap
,
fals
e
);
ret
=
snd_soc_register_codec
(
&
i2c_client
->
dev
,
&
soc_codec_dev_cs42l73
,
cs42l73_dai
,
...
...
@@ -1478,6 +1511,12 @@ static int cs42l73_i2c_remove(struct i2c_client *client)
return
0
;
}
static
const
struct
of_device_id
cs42l73_of_match
[]
=
{
{
.
compatible
=
"cirrus,cs42l73"
,
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
cs42l73_of_match
);
static
const
struct
i2c_device_id
cs42l73_id
[]
=
{
{
"cs42l73"
,
0
},
{}
...
...
@@ -1489,6 +1528,7 @@ static struct i2c_driver cs42l73_i2c_driver = {
.
driver
=
{
.
name
=
"cs42l73"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
cs42l73_of_match
,
},
.
id_table
=
cs42l73_id
,
.
probe
=
cs42l73_i2c_probe
,
...
...
sound/soc/codecs/cs42l73.h
View file @
85eb5dce
...
...
@@ -128,59 +128,60 @@
/* Bitfield Definitions */
/* CS42L73_PWRCTL1 */
#define PDN_ADCB (1 << 7)
#define PDN_DMICB (1 << 6)
#define PDN_ADCA (1 << 5)
#define PDN_DMICA (1 << 4)
#define PDN_LDO (1 << 2)
#define DISCHG_FILT (1 << 1)
#define PDN (1 << 0)
#define
CS42L73_
PDN_ADCB (1 << 7)
#define
CS42L73_
PDN_DMICB (1 << 6)
#define
CS42L73_
PDN_ADCA (1 << 5)
#define
CS42L73_
PDN_DMICA (1 << 4)
#define
CS42L73_
PDN_LDO (1 << 2)
#define
CS42L73_
DISCHG_FILT (1 << 1)
#define
CS42L73_
PDN (1 << 0)
/* CS42L73_PWRCTL2 */
#define PDN_MIC2_BIAS (1 << 7)
#define PDN_MIC1_BIAS (1 << 6)
#define PDN_VSP (1 << 4)
#define PDN_ASP_SDOUT (1 << 3)
#define PDN_ASP_SDIN (1 << 2)
#define PDN_XSP_SDOUT (1 << 1)
#define PDN_XSP_SDIN (1 << 0)
#define
CS42L73_
PDN_MIC2_BIAS (1 << 7)
#define
CS42L73_
PDN_MIC1_BIAS (1 << 6)
#define
CS42L73_
PDN_VSP (1 << 4)
#define
CS42L73_
PDN_ASP_SDOUT (1 << 3)
#define
CS42L73_
PDN_ASP_SDIN (1 << 2)
#define
CS42L73_
PDN_XSP_SDOUT (1 << 1)
#define
CS42L73_
PDN_XSP_SDIN (1 << 0)
/* CS42L73_PWRCTL3 */
#define PDN_THMS (1 << 5)
#define PDN_SPKLO (1 << 4)
#define PDN_EAR (1 << 3)
#define PDN_SPK (1 << 2)
#define PDN_LO (1 << 1)
#define PDN_HP (1 << 0)
#define
CS42L73_
PDN_THMS (1 << 5)
#define
CS42L73_
PDN_SPKLO (1 << 4)
#define
CS42L73_
PDN_EAR (1 << 3)
#define
CS42L73_
PDN_SPK (1 << 2)
#define
CS42L73_
PDN_LO (1 << 1)
#define
CS42L73_
PDN_HP (1 << 0)
/* Thermal Overload Detect. Requires interrupt ... */
#define THMOVLD_150C 0
#define THMOVLD_132C 1
#define THMOVLD_115C 2
#define THMOVLD_098C 3
#define
CS42L73_
THMOVLD_150C 0
#define
CS42L73_
THMOVLD_132C 1
#define
CS42L73_
THMOVLD_115C 2
#define
CS42L73_
THMOVLD_098C 3
#define CS42L73_CHARGEPUMP_MASK (0xF0)
/* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */
#define SP_3ST (1 << 7)
#define SPDIF_I2S (0 << 6)
#define SPDIF_PCM (1 << 6)
#define PCM_MODE0 (0 << 4)
#define PCM_MODE1 (1 << 4)
#define PCM_MODE2 (2 << 4)
#define PCM_MODE_MASK (3 << 4)
#define PCM_BIT_ORDER (1 << 3)
#define MCK_SCLK_64FS (0 << 0)
#define MCK_SCLK_MCLK (2 << 0)
#define MCK_SCLK_PREMCLK (3 << 0)
#define
CS42L73_
SP_3ST (1 << 7)
#define
CS42L73_
SPDIF_I2S (0 << 6)
#define
CS42L73_
SPDIF_PCM (1 << 6)
#define
CS42L73_
PCM_MODE0 (0 << 4)
#define
CS42L73_
PCM_MODE1 (1 << 4)
#define
CS42L73_
PCM_MODE2 (2 << 4)
#define
CS42L73_
PCM_MODE_MASK (3 << 4)
#define
CS42L73_
PCM_BIT_ORDER (1 << 3)
#define
CS42L73_
MCK_SCLK_64FS (0 << 0)
#define
CS42L73_
MCK_SCLK_MCLK (2 << 0)
#define
CS42L73_
MCK_SCLK_PREMCLK (3 << 0)
/* CS42L73_xSPMMCC */
#define MS_MASTER (1 << 7)
#define
CS42L73_
MS_MASTER (1 << 7)
/* CS42L73_DMMCC */
#define MCLKDIS (1 << 0)
#define MCLKSEL_MCLK2 (1 << 4)
#define MCLKSEL_MCLK1 (0 << 4)
#define
CS42L73_
MCLKDIS (1 << 0)
#define
CS42L73_
MCLKSEL_MCLK2 (1 << 4)
#define
CS42L73_
MCLKSEL_MCLK1 (0 << 4)
/* CS42L73 MCLK derived from MCLK1 or MCLK2 */
#define CS42L73_CLKID_MCLK1 0
...
...
@@ -194,28 +195,26 @@
#define CS42L73_VSP 2
/* IS1, IM1 */
#define MIC2_SDET (1 << 6)
#define THMOVLD (1 << 4)
#define DIGMIXOVFL (1 << 3)
#define IPBOVFL (1 << 1)
#define IPAOVFL (1 << 0)
#define
CS42L73_
MIC2_SDET (1 << 6)
#define
CS42L73_
THMOVLD (1 << 4)
#define
CS42L73_
DIGMIXOVFL (1 << 3)
#define
CS42L73_
IPBOVFL (1 << 1)
#define
CS42L73_
IPAOVFL (1 << 0)
/* Analog Softramp */
#define ANLGOSFT (1 << 0)
#define
CS42L73_
ANLGOSFT (1 << 0)
/* HP A/B Analog Mute */
#define HPA_MUTE (1 << 7)
#define
CS42L73_
HPA_MUTE (1 << 7)
/* LO A/B Analog Mute */
#define LOA_MUTE (1 << 7)
#define
CS42L73_
LOA_MUTE (1 << 7)
/* Digital Mute */
#define HLAD_MUTE (1 << 0)
#define HLBD_MUTE (1 << 1)
#define SPKD_MUTE (1 << 2)
#define ESLD_MUTE (1 << 3)
#define
CS42L73_
HLAD_MUTE (1 << 0)
#define
CS42L73_
HLBD_MUTE (1 << 1)
#define
CS42L73_
SPKD_MUTE (1 << 2)
#define
CS42L73_
ESLD_MUTE (1 << 3)
/* Misc defines for codec */
#define CS42L73_RESET_GPIO 143
#define CS42L73_DEVID 0x00042A73
#define CS42L73_MCLKX_MIN 5644800
#define CS42L73_MCLKX_MAX 38400000
...
...
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