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
1ab4f851
Commit
1ab4f851
authored
Dec 23, 2015
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/sunxi' into asoc-next
parents
10330401
6b803c61
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
233 additions
and
49 deletions
+233
-49
Documentation/devicetree/bindings/sound/sun4i-codec.txt
Documentation/devicetree/bindings/sound/sun4i-codec.txt
+3
-0
sound/soc/sunxi/sun4i-codec.c
sound/soc/sunxi/sun4i-codec.c
+230
-49
No files found.
Documentation/devicetree/bindings/sound/sun4i-codec.txt
View file @
1ab4f851
...
...
@@ -14,6 +14,9 @@ Required properties:
- "apb": the parent APB clock for this controller
- "codec": the parent module clock
Optional properties:
- allwinner,pa-gpios: gpio to enable external amplifier
Example:
codec: codec@01c22c00 {
#sound-dai-cells = <0>;
...
...
sound/soc/sunxi/sun4i-codec.c
View file @
1ab4f851
...
...
@@ -28,6 +28,7 @@
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
...
...
@@ -70,6 +71,7 @@
/* Codec ADC register offsets and bit fields */
#define SUN4I_CODEC_ADC_FIFOC (0x1c)
#define SUN4I_CODEC_ADC_FIFOC_ADC_FS (29)
#define SUN4I_CODEC_ADC_FIFOC_EN_AD (28)
#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE (24)
#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL (8)
...
...
@@ -102,17 +104,14 @@ struct sun4i_codec {
struct
regmap
*
regmap
;
struct
clk
*
clk_apb
;
struct
clk
*
clk_module
;
struct
gpio_desc
*
gpio_pa
;
struct
snd_dmaengine_dai_dma_data
capture_dma_data
;
struct
snd_dmaengine_dai_dma_data
playback_dma_data
;
};
static
void
sun4i_codec_start_playback
(
struct
sun4i_codec
*
scodec
)
{
/*
* FIXME: according to the BSP, we might need to drive a PA
* GPIO high here on some boards
*/
/* Flush TX FIFO */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_DAC_FIFOC
,
BIT
(
SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH
),
...
...
@@ -126,37 +125,50 @@ static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
static
void
sun4i_codec_stop_playback
(
struct
sun4i_codec
*
scodec
)
{
/*
* FIXME: according to the BSP, we might need to drive a PA
* GPIO low here on some boards
*/
/* Disable DAC DRQ */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_DAC_FIFOC
,
BIT
(
SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN
),
0
);
}
static
void
sun4i_codec_start_capture
(
struct
sun4i_codec
*
scodec
)
{
/* Enable ADC DRQ */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
BIT
(
SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN
),
BIT
(
SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN
));
}
static
void
sun4i_codec_stop_capture
(
struct
sun4i_codec
*
scodec
)
{
/* Disable ADC DRQ */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
BIT
(
SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN
),
0
);
}
static
int
sun4i_codec_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
if
(
substream
->
stream
!=
SNDRV_PCM_STREAM_PLAYBACK
)
return
-
ENOTSUPP
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
case
SNDRV_PCM_TRIGGER_RESUME
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
sun4i_codec_start_playback
(
scodec
);
else
sun4i_codec_start_capture
(
scodec
);
break
;
case
SNDRV_PCM_TRIGGER_STOP
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
sun4i_codec_stop_playback
(
scodec
);
else
sun4i_codec_stop_capture
(
scodec
);
break
;
default:
...
...
@@ -166,15 +178,54 @@ static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
return
0
;
}
static
int
sun4i_codec_prepare
(
struct
snd_pcm_substream
*
substream
,
static
int
sun4i_codec_prepare
_capture
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
u32
val
;
if
(
substream
->
stream
!=
SNDRV_PCM_STREAM_PLAYBACK
)
return
-
ENOTSUPP
;
/* Flush RX FIFO */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
BIT
(
SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH
),
BIT
(
SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH
));
/* Set RX FIFO trigger level */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
0xf
<<
SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL
,
0x7
<<
SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL
);
/*
* FIXME: Undocumented in the datasheet, but
* Allwinner's code mentions that it is related
* related to microphone gain
*/
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_ACTL
,
0x3
<<
25
,
0x1
<<
25
);
if
(
of_device_is_compatible
(
scodec
->
dev
->
of_node
,
"allwinner,sun7i-a20-codec"
))
/* FIXME: Undocumented bits */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_DAC_TUNE
,
0x3
<<
8
,
0x1
<<
8
);
/* Fill most significant bits with valid data MSB */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
BIT
(
SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE
),
BIT
(
SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE
));
return
0
;
}
static
int
sun4i_codec_prepare_playback
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
u32
val
;
/* Flush the TX FIFO */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_DAC_FIFOC
,
...
...
@@ -203,6 +254,15 @@ static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
0
);
return
0
;
};
static
int
sun4i_codec_prepare
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
return
sun4i_codec_prepare_playback
(
substream
,
dai
);
return
sun4i_codec_prepare_capture
(
substream
,
dai
);
}
static
unsigned
long
sun4i_codec_get_mod_freq
(
struct
snd_pcm_hw_params
*
params
)
...
...
@@ -277,30 +337,32 @@ static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
}
}
static
int
sun4i_codec_hw_params
(
struct
snd_pcm_substream
*
substream
,
static
int
sun4i_codec_hw_params
_capture
(
struct
sun4i_codec
*
scodec
,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
)
unsigned
int
hwrate
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
unsigned
long
clk_freq
;
int
ret
,
hwrate
;
u32
val
;
/* Set ADC sample rate */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
7
<<
SUN4I_CODEC_ADC_FIFOC_ADC_FS
,
hwrate
<<
SUN4I_CODEC_ADC_FIFOC_ADC_FS
);
if
(
substream
->
stream
!=
SNDRV_PCM_STREAM_PLAYBACK
)
return
-
ENOTSUPP
;
clk_freq
=
sun4i_codec_get_mod_freq
(
params
);
if
(
!
clk_freq
)
return
-
EINVAL
;
/* Set the number of channels we want to use */
if
(
params_channels
(
params
)
==
1
)
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
BIT
(
SUN4I_CODEC_ADC_FIFOC_MONO_EN
),
BIT
(
SUN4I_CODEC_ADC_FIFOC_MONO_EN
));
else
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_ADC_FIFOC
,
BIT
(
SUN4I_CODEC_ADC_FIFOC_MONO_EN
),
0
);
ret
=
clk_set_rate
(
scodec
->
clk_module
,
clk_freq
);
if
(
ret
)
return
ret
;
return
0
;
}
hwrate
=
sun4i_codec_get_hw_rate
(
params
);
if
(
hwrate
<
0
)
return
hwrate
;
static
int
sun4i_codec_hw_params_playback
(
struct
sun4i_codec
*
scodec
,
struct
snd_pcm_hw_params
*
params
,
unsigned
int
hwrate
)
{
u32
val
;
/* Set DAC sample rate */
regmap_update_bits
(
scodec
->
regmap
,
SUN4I_CODEC_DAC_FIFOC
,
...
...
@@ -345,6 +407,35 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
return
0
;
}
static
int
sun4i_codec_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
params
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_pcm_runtime
*
rtd
=
substream
->
private_data
;
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
rtd
->
card
);
unsigned
long
clk_freq
;
int
ret
,
hwrate
;
clk_freq
=
sun4i_codec_get_mod_freq
(
params
);
if
(
!
clk_freq
)
return
-
EINVAL
;
ret
=
clk_set_rate
(
scodec
->
clk_module
,
clk_freq
);
if
(
ret
)
return
ret
;
hwrate
=
sun4i_codec_get_hw_rate
(
params
);
if
(
hwrate
<
0
)
return
hwrate
;
if
(
substream
->
stream
==
SNDRV_PCM_STREAM_PLAYBACK
)
return
sun4i_codec_hw_params_playback
(
scodec
,
params
,
hwrate
);
return
sun4i_codec_hw_params_capture
(
scodec
,
params
,
hwrate
);
}
static
int
sun4i_codec_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
...
...
@@ -395,6 +486,20 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
SNDRV_PCM_FMTBIT_S32_LE
,
.
sig_bits
=
24
,
},
.
capture
=
{
.
stream_name
=
"Codec Capture"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rate_min
=
8000
,
.
rate_max
=
192000
,
.
rates
=
SNDRV_PCM_RATE_8000_48000
|
SNDRV_PCM_RATE_96000
|
SNDRV_PCM_RATE_192000
|
SNDRV_PCM_RATE_KNOT
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
|
SNDRV_PCM_FMTBIT_S32_LE
,
.
sig_bits
=
24
,
},
};
/*** Codec ***/
...
...
@@ -429,12 +534,23 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
SUN4I_CODEC_DAC_ACTL_MIXPAS
,
1
,
0
),
};
static
const
struct
snd_soc_dapm_widget
sun4i_codec_dapm_widgets
[]
=
{
static
const
struct
snd_soc_dapm_widget
sun4i_codec_codec_dapm_widgets
[]
=
{
/* Digital parts of the ADCs */
SND_SOC_DAPM_SUPPLY
(
"ADC"
,
SUN4I_CODEC_ADC_FIFOC
,
SUN4I_CODEC_ADC_FIFOC_EN_AD
,
0
,
NULL
,
0
),
/* Digital parts of the DACs */
SND_SOC_DAPM_SUPPLY
(
"DAC"
,
SUN4I_CODEC_DAC_DPC
,
SUN4I_CODEC_DAC_DPC_EN_DA
,
0
,
NULL
,
0
),
/* Analog parts of the ADCs */
SND_SOC_DAPM_ADC
(
"Left ADC"
,
"Codec Capture"
,
SUN4I_CODEC_ADC_ACTL
,
SUN4I_CODEC_ADC_ACTL_ADC_L_EN
,
0
),
SND_SOC_DAPM_ADC
(
"Right ADC"
,
"Codec Capture"
,
SUN4I_CODEC_ADC_ACTL
,
SUN4I_CODEC_ADC_ACTL_ADC_R_EN
,
0
),
/* Analog parts of the DACs */
SND_SOC_DAPM_DAC
(
"Left DAC"
,
"Codec Playback"
,
SUN4I_CODEC_DAC_ACTL
,
SUN4I_CODEC_DAC_ACTL_DACAENL
,
0
),
...
...
@@ -453,6 +569,14 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY
(
"Mixer Enable"
,
SUN4I_CODEC_DAC_ACTL
,
SUN4I_CODEC_DAC_ACTL_MIXEN
,
0
,
NULL
,
0
),
/* VMIC */
SND_SOC_DAPM_SUPPLY
(
"VMIC"
,
SUN4I_CODEC_ADC_ACTL
,
SUN4I_CODEC_ADC_ACTL_VMICEN
,
0
,
NULL
,
0
),
/* Mic Pre-Amplifiers */
SND_SOC_DAPM_PGA
(
"MIC1 Pre-Amplifier"
,
SUN4I_CODEC_ADC_ACTL
,
SUN4I_CODEC_ADC_ACTL_PREG1EN
,
0
,
NULL
,
0
),
/* Power Amplifier */
SND_SOC_DAPM_MIXER
(
"Power Amplifier"
,
SUN4I_CODEC_ADC_ACTL
,
SUN4I_CODEC_ADC_ACTL_PA_EN
,
0
,
...
...
@@ -461,15 +585,19 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH
(
"Power Amplifier Mute"
,
SND_SOC_NOPM
,
0
,
0
,
&
sun4i_codec_pa_mute
),
SND_SOC_DAPM_INPUT
(
"Mic1"
),
SND_SOC_DAPM_OUTPUT
(
"HP Right"
),
SND_SOC_DAPM_OUTPUT
(
"HP Left"
),
};
static
const
struct
snd_soc_dapm_route
sun4i_codec_dapm_routes
[]
=
{
/* Left DAC Routes */
static
const
struct
snd_soc_dapm_route
sun4i_codec_codec_dapm_routes
[]
=
{
/* Left ADC / DAC Routes */
{
"Left ADC"
,
NULL
,
"ADC"
},
{
"Left DAC"
,
NULL
,
"DAC"
},
/* Right DAC Routes */
/* Right ADC / DAC Routes */
{
"Right ADC"
,
NULL
,
"ADC"
},
{
"Right DAC"
,
NULL
,
"DAC"
},
/* Right Mixer Routes */
...
...
@@ -491,15 +619,21 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
{
"Power Amplifier Mute"
,
"Switch"
,
"Power Amplifier"
},
{
"HP Right"
,
NULL
,
"Power Amplifier Mute"
},
{
"HP Left"
,
NULL
,
"Power Amplifier Mute"
},
/* Mic1 Routes */
{
"Left ADC"
,
NULL
,
"MIC1 Pre-Amplifier"
},
{
"Right ADC"
,
NULL
,
"MIC1 Pre-Amplifier"
},
{
"MIC1 Pre-Amplifier"
,
NULL
,
"Mic1"
},
{
"Mic1"
,
NULL
,
"VMIC"
},
};
static
struct
snd_soc_codec_driver
sun4i_codec_codec
=
{
.
controls
=
sun4i_codec_widgets
,
.
num_controls
=
ARRAY_SIZE
(
sun4i_codec_widgets
),
.
dapm_widgets
=
sun4i_codec_dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
sun4i_codec_dapm_widgets
),
.
dapm_routes
=
sun4i_codec_dapm_routes
,
.
num_dapm_routes
=
ARRAY_SIZE
(
sun4i_codec_dapm_routes
),
.
dapm_widgets
=
sun4i_codec_
codec_
dapm_widgets
,
.
num_dapm_widgets
=
ARRAY_SIZE
(
sun4i_codec_
codec_
dapm_widgets
),
.
dapm_routes
=
sun4i_codec_
codec_
dapm_routes
,
.
num_dapm_routes
=
ARRAY_SIZE
(
sun4i_codec_
codec_
dapm_routes
),
};
static
const
struct
snd_soc_component_driver
sun4i_codec_component
=
{
...
...
@@ -516,7 +650,7 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
card
);
snd_soc_dai_init_dma_data
(
dai
,
&
scodec
->
playback_dma_data
,
NULL
);
&
scodec
->
capture_dma_data
);
return
0
;
}
...
...
@@ -532,6 +666,14 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
.
formats
=
SUN4I_CODEC_FORMATS
,
.
sig_bits
=
24
,
},
.
capture
=
{
.
stream_name
=
"Capture"
,
.
channels_min
=
1
,
.
channels_max
=
2
,
.
rates
=
SUN4I_CODEC_RATES
,
.
formats
=
SUN4I_CODEC_FORMATS
,
.
sig_bits
=
24
,
},
};
static
const
struct
regmap_config
sun4i_codec_regmap_config
=
{
...
...
@@ -569,6 +711,27 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
return
link
;
};
static
int
sun4i_codec_spk_event
(
struct
snd_soc_dapm_widget
*
w
,
struct
snd_kcontrol
*
k
,
int
event
)
{
struct
sun4i_codec
*
scodec
=
snd_soc_card_get_drvdata
(
w
->
dapm
->
card
);
if
(
scodec
->
gpio_pa
)
gpiod_set_value_cansleep
(
scodec
->
gpio_pa
,
!!
SND_SOC_DAPM_EVENT_ON
(
event
));
return
0
;
}
static
const
struct
snd_soc_dapm_widget
sun4i_codec_card_dapm_widgets
[]
=
{
SND_SOC_DAPM_SPK
(
"Speaker"
,
sun4i_codec_spk_event
),
};
static
const
struct
snd_soc_dapm_route
sun4i_codec_card_dapm_routes
[]
=
{
{
"Speaker"
,
NULL
,
"HP Right"
},
{
"Speaker"
,
NULL
,
"HP Left"
},
};
static
struct
snd_soc_card
*
sun4i_codec_create_card
(
struct
device
*
dev
)
{
struct
snd_soc_card
*
card
;
...
...
@@ -583,6 +746,10 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
card
->
dev
=
dev
;
card
->
name
=
"sun4i-codec"
;
card
->
dapm_widgets
=
sun4i_codec_card_dapm_widgets
;
card
->
num_dapm_widgets
=
ARRAY_SIZE
(
sun4i_codec_card_dapm_widgets
);
card
->
dapm_routes
=
sun4i_codec_card_dapm_routes
;
card
->
num_dapm_routes
=
ARRAY_SIZE
(
sun4i_codec_card_dapm_routes
);
return
card
;
};
...
...
@@ -634,11 +801,25 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return
-
EINVAL
;
}
scodec
->
gpio_pa
=
devm_gpiod_get_optional
(
&
pdev
->
dev
,
"allwinner,pa"
,
GPIOD_OUT_LOW
);
if
(
IS_ERR
(
scodec
->
gpio_pa
))
{
ret
=
PTR_ERR
(
scodec
->
gpio_pa
);
if
(
ret
!=
-
EPROBE_DEFER
)
dev_err
(
&
pdev
->
dev
,
"Failed to get pa gpio: %d
\n
"
,
ret
);
return
ret
;
}
/* DMA configuration for TX FIFO */
scodec
->
playback_dma_data
.
addr
=
res
->
start
+
SUN4I_CODEC_DAC_TXDATA
;
scodec
->
playback_dma_data
.
maxburst
=
4
;
scodec
->
playback_dma_data
.
addr_width
=
DMA_SLAVE_BUSWIDTH_2_BYTES
;
/* DMA configuration for RX FIFO */
scodec
->
capture_dma_data
.
addr
=
res
->
start
+
SUN4I_CODEC_ADC_RXDATA
;
scodec
->
capture_dma_data
.
maxburst
=
4
;
scodec
->
capture_dma_data
.
addr_width
=
DMA_SLAVE_BUSWIDTH_2_BYTES
;
ret
=
snd_soc_register_codec
(
&
pdev
->
dev
,
&
sun4i_codec_codec
,
&
sun4i_codec_dai
,
1
);
if
(
ret
)
{
...
...
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