Commit 5f189089 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/hda-probe-defer' into for-next

This branch fixes the stall during probing the HD-audio driver when
the specified "patch" firmware doesn't exist.  It's basically a long-
standing issue, but mostly harmless until the recent rework of
firmware loader base code.
parents 793ea49c 5cb543db
...@@ -332,13 +332,12 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, ...@@ -332,13 +332,12 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
if (cfg->dig_outs) if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n", snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]); cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
snd_printd(" inputs:"); snd_printd(" inputs:\n");
for (i = 0; i < cfg->num_inputs; i++) { for (i = 0; i < cfg->num_inputs; i++) {
snd_printd(" %s=0x%x", snd_printd(" %s=0x%x\n",
hda_get_autocfg_input_label(codec, cfg, i), hda_get_autocfg_input_label(codec, cfg, i),
cfg->inputs[i].pin); cfg->inputs[i].pin);
} }
snd_printd("\n");
if (cfg->dig_in_pin) if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
......
...@@ -4184,7 +4184,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) ...@@ -4184,7 +4184,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
* *
* This function returns 0 if successful, or a negative error code. * This function returns 0 if successful, or a negative error code.
*/ */
int __devinit snd_hda_build_pcms(struct hda_bus *bus) int snd_hda_build_pcms(struct hda_bus *bus)
{ {
struct hda_codec *codec; struct hda_codec *codec;
......
...@@ -1072,7 +1072,7 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {} ...@@ -1072,7 +1072,7 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {}
/* /*
* patch firmware * patch firmware
*/ */
int snd_hda_load_patch(struct hda_bus *bus, const char *patch); int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif #endif
/* /*
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/firmware.h>
#include <linux/export.h> #include <linux/export.h>
#include <sound/core.h> #include <sound/core.h>
#include "hda_codec.h" #include "hda_codec.h"
...@@ -747,18 +746,21 @@ static int parse_line_mode(char *buf, struct hda_bus *bus) ...@@ -747,18 +746,21 @@ static int parse_line_mode(char *buf, struct hda_bus *bus)
* *
* the spaces at the beginning and the end of the line are stripped * the spaces at the beginning and the end of the line are stripped
*/ */
static int get_line_from_fw(char *buf, int size, struct firmware *fw) static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
const void **fw_data_p)
{ {
int len; int len;
const char *p = fw->data; size_t fw_size = *fw_size_p;
while (isspace(*p) && fw->size) { const char *p = *fw_data_p;
while (isspace(*p) && fw_size) {
p++; p++;
fw->size--; fw_size--;
} }
if (!fw->size) if (!fw_size)
return 0; return 0;
for (len = 0; len < fw->size; len++) { for (len = 0; len < fw_size; len++) {
if (!*p) if (!*p)
break; break;
if (*p == '\n') { if (*p == '\n') {
...@@ -770,8 +772,8 @@ static int get_line_from_fw(char *buf, int size, struct firmware *fw) ...@@ -770,8 +772,8 @@ static int get_line_from_fw(char *buf, int size, struct firmware *fw)
*buf++ = *p++; *buf++ = *p++;
} }
*buf = 0; *buf = 0;
fw->size -= len; *fw_size_p = fw_size - len;
fw->data = p; *fw_data_p = p;
remove_trail_spaces(buf); remove_trail_spaces(buf);
return 1; return 1;
} }
...@@ -779,29 +781,15 @@ static int get_line_from_fw(char *buf, int size, struct firmware *fw) ...@@ -779,29 +781,15 @@ static int get_line_from_fw(char *buf, int size, struct firmware *fw)
/* /*
* load a "patch" firmware file and parse it * load a "patch" firmware file and parse it
*/ */
int snd_hda_load_patch(struct hda_bus *bus, const char *patch) int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
{ {
int err;
const struct firmware *fw;
struct firmware tmp;
char buf[128]; char buf[128];
struct hda_codec *codec; struct hda_codec *codec;
int line_mode; int line_mode;
struct device *dev = bus->card->dev;
if (snd_BUG_ON(!dev))
return -ENODEV;
err = request_firmware(&fw, patch, dev);
if (err < 0) {
printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
patch);
return err;
}
tmp = *fw;
line_mode = LINE_MODE_NONE; line_mode = LINE_MODE_NONE;
codec = NULL; codec = NULL;
while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) { while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
if (!*buf || *buf == '#' || *buf == '\n') if (!*buf || *buf == '#' || *buf == '\n')
continue; continue;
if (*buf == '[') if (*buf == '[')
...@@ -810,7 +798,6 @@ int snd_hda_load_patch(struct hda_bus *bus, const char *patch) ...@@ -810,7 +798,6 @@ int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
(codec || !patch_items[line_mode].need_codec)) (codec || !patch_items[line_mode].need_codec))
patch_items[line_mode].parser(buf, bus, &codec); patch_items[line_mode].parser(buf, bus, &codec);
} }
release_firmware(fw);
return 0; return 0;
} }
EXPORT_SYMBOL_HDA(snd_hda_load_patch); EXPORT_SYMBOL_HDA(snd_hda_load_patch);
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include <sound/initval.h> #include <sound/initval.h>
#include <linux/vgaarb.h> #include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h> #include <linux/vga_switcheroo.h>
#include <linux/firmware.h>
#include "hda_codec.h" #include "hda_codec.h"
...@@ -470,6 +471,10 @@ struct azx { ...@@ -470,6 +471,10 @@ struct azx {
struct snd_dma_buffer rb; struct snd_dma_buffer rb;
struct snd_dma_buffer posbuf; struct snd_dma_buffer posbuf;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
const struct firmware *fw;
#endif
/* flags */ /* flags */
int position_fix[2]; /* for both playback/capture streams */ int position_fix[2]; /* for both playback/capture streams */
int poll_count; int poll_count;
...@@ -559,13 +564,17 @@ enum { ...@@ -559,13 +564,17 @@ enum {
* VGA-switcher support * VGA-switcher support
*/ */
#ifdef SUPPORT_VGA_SWITCHEROO #ifdef SUPPORT_VGA_SWITCHEROO
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
#else
#define use_vga_switcheroo(chip) 0
#endif
#if defined(SUPPORT_VGA_SWITCHEROO) || defined(CONFIG_SND_HDA_PATCH_LOADER)
#define DELAYED_INIT_MARK #define DELAYED_INIT_MARK
#define DELAYED_INITDATA_MARK #define DELAYED_INITDATA_MARK
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
#else #else
#define DELAYED_INIT_MARK __devinit #define DELAYED_INIT_MARK __devinit
#define DELAYED_INITDATA_MARK __devinitdata #define DELAYED_INITDATA_MARK __devinitdata
#define use_vga_switcheroo(chip) 0
#endif #endif
static char *driver_short_names[] DELAYED_INITDATA_MARK = { static char *driver_short_names[] DELAYED_INITDATA_MARK = {
...@@ -2639,6 +2648,10 @@ static int azx_free(struct azx *chip) ...@@ -2639,6 +2648,10 @@ static int azx_free(struct azx *chip)
pci_release_regions(chip->pci); pci_release_regions(chip->pci);
pci_disable_device(chip->pci); pci_disable_device(chip->pci);
kfree(chip->azx_dev); kfree(chip->azx_dev);
#ifdef CONFIG_SND_HDA_PATCH_LOADER
if (chip->fw)
release_firmware(chip->fw);
#endif
kfree(chip); kfree(chip);
return 0; return 0;
...@@ -3146,12 +3159,38 @@ static void power_down_all_codecs(struct azx *chip) ...@@ -3146,12 +3159,38 @@ static void power_down_all_codecs(struct azx *chip)
#endif #endif
} }
/* callback from request_firmware_nowait() */
static void azx_firmware_cb(const struct firmware *fw, void *context)
{
struct snd_card *card = context;
struct azx *chip = card->private_data;
struct pci_dev *pci = chip->pci;
if (!fw) {
snd_printk(KERN_ERR SFX "Cannot load firmware, aborting\n");
goto error;
}
chip->fw = fw;
if (!chip->disabled) {
/* continue probing */
if (azx_probe_continue(chip))
goto error;
}
return; /* OK */
error:
snd_card_free(card);
pci_set_drvdata(pci, NULL);
}
static int __devinit azx_probe(struct pci_dev *pci, static int __devinit azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id) const struct pci_device_id *pci_id)
{ {
static int dev; static int dev;
struct snd_card *card; struct snd_card *card;
struct azx *chip; struct azx *chip;
bool probe_now;
int err; int err;
if (dev >= SNDRV_CARDS) if (dev >= SNDRV_CARDS)
...@@ -3167,15 +3206,28 @@ static int __devinit azx_probe(struct pci_dev *pci, ...@@ -3167,15 +3206,28 @@ static int __devinit azx_probe(struct pci_dev *pci,
return err; return err;
} }
/* set this here since it's referred in snd_hda_load_patch() */
snd_card_set_dev(card, &pci->dev); snd_card_set_dev(card, &pci->dev);
err = azx_create(card, pci, dev, pci_id->driver_data, &chip); err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0) if (err < 0)
goto out_free; goto out_free;
card->private_data = chip; card->private_data = chip;
probe_now = !chip->disabled;
if (!chip->disabled) { #ifdef CONFIG_SND_HDA_PATCH_LOADER
if (patch[dev] && *patch[dev]) {
snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
patch[dev]);
err = request_firmware_nowait(THIS_MODULE, true, patch[dev],
&pci->dev, GFP_KERNEL, card,
azx_firmware_cb);
if (err < 0)
goto out_free;
probe_now = false; /* continued in azx_firmware_cb() */
}
#endif /* CONFIG_SND_HDA_PATCH_LOADER */
if (probe_now) {
err = azx_probe_continue(chip); err = azx_probe_continue(chip);
if (err < 0) if (err < 0)
goto out_free; goto out_free;
...@@ -3205,12 +3257,13 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip) ...@@ -3205,12 +3257,13 @@ static int DELAYED_INIT_MARK azx_probe_continue(struct azx *chip)
if (err < 0) if (err < 0)
goto out_free; goto out_free;
#ifdef CONFIG_SND_HDA_PATCH_LOADER #ifdef CONFIG_SND_HDA_PATCH_LOADER
if (patch[dev] && *patch[dev]) { if (chip->fw) {
snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n", err = snd_hda_load_patch(chip->bus, chip->fw->size,
patch[dev]); chip->fw->data);
err = snd_hda_load_patch(chip->bus, patch[dev]);
if (err < 0) if (err < 0)
goto out_free; goto out_free;
release_firmware(chip->fw); /* no longer needed */
chip->fw = NULL;
} }
#endif #endif
if ((probe_only[dev] & 1) == 0) { if ((probe_only[dev] & 1) == 0) {
......
...@@ -2967,12 +2967,10 @@ static const char * const cxt5066_models[CXT5066_MODELS] = { ...@@ -2967,12 +2967,10 @@ static const char * const cxt5066_models[CXT5066_MODELS] = {
}; };
static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT5066_AUTO),
SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD), SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD),
...@@ -2988,14 +2986,10 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { ...@@ -2988,14 +2986,10 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T510", CXT5066_AUTO),
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO),
SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO),
{} {}
}; };
......
...@@ -877,8 +877,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, ...@@ -877,8 +877,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
struct hdmi_eld *eld; struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL; struct hdmi_spec_per_cvt *per_cvt = NULL;
hinfo->nid = 0; /* clear the leftover value */
/* Validate hinfo */ /* Validate hinfo */
pin_idx = hinfo_to_pin_index(spec, hinfo); pin_idx = hinfo_to_pin_index(spec, hinfo);
if (snd_BUG_ON(pin_idx < 0)) if (snd_BUG_ON(pin_idx < 0))
...@@ -1163,6 +1161,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, ...@@ -1163,6 +1161,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
} }
static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
snd_hda_codec_cleanup_stream(codec, hinfo->nid);
return 0;
}
static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec, struct hda_codec *codec,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
...@@ -1202,6 +1208,7 @@ static const struct hda_pcm_ops generic_ops = { ...@@ -1202,6 +1208,7 @@ static const struct hda_pcm_ops generic_ops = {
.open = hdmi_pcm_open, .open = hdmi_pcm_open,
.close = hdmi_pcm_close, .close = hdmi_pcm_close,
.prepare = generic_hdmi_playback_pcm_prepare, .prepare = generic_hdmi_playback_pcm_prepare,
.cleanup = generic_hdmi_playback_pcm_cleanup,
}; };
static int generic_hdmi_build_pcms(struct hda_codec *codec) static int generic_hdmi_build_pcms(struct hda_codec *codec)
...@@ -1220,7 +1227,6 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) ...@@ -1220,7 +1227,6 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
pstr->substreams = 1; pstr->substreams = 1;
pstr->ops = generic_ops; pstr->ops = generic_ops;
pstr->nid = 1; /* FIXME: just for avoiding a debug WARNING */
/* other pstr fields are set in open */ /* other pstr fields are set in open */
} }
......
...@@ -6099,6 +6099,8 @@ static const struct alc_fixup alc269_fixups[] = { ...@@ -6099,6 +6099,8 @@ static const struct alc_fixup alc269_fixups[] = {
[ALC269_FIXUP_PCM_44K] = { [ALC269_FIXUP_PCM_44K] = {
.type = ALC_FIXUP_FUNC, .type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pcm_44k, .v.func = alc269_fixup_pcm_44k,
.chained = true,
.chain_id = ALC269_FIXUP_QUANTA_MUTE
}, },
[ALC269_FIXUP_STEREO_DMIC] = { [ALC269_FIXUP_STEREO_DMIC] = {
.type = ALC_FIXUP_FUNC, .type = ALC_FIXUP_FUNC,
...@@ -6207,9 +6209,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { ...@@ -6207,9 +6209,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_QUANTA_MUTE), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
#if 0 #if 0
......
...@@ -1752,6 +1752,14 @@ static int via_suspend(struct hda_codec *codec) ...@@ -1752,6 +1752,14 @@ static int via_suspend(struct hda_codec *codec)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
vt1708_stop_hp_work(spec); vt1708_stop_hp_work(spec);
if (spec->codec_type == VT1802) {
/* Fix pop noise on headphones */
int i;
for (i = 0; i < spec->autocfg.hp_outs; i++)
snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0);
}
return 0; return 0;
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment