Commit 42e50290 authored by Michael Tretter's avatar Michael Tretter Committed by Mauro Carvalho Chehab

media: allegro: add support for allegro firmware 2019.2

Encode messages as necessary for the firmware 2019.2 and, thus, support
the more recent firmware version in the driver, too.
Signed-off-by: default avatarMichael Tretter <m.tretter@pengutronix.de>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 11146a75
...@@ -292,6 +292,15 @@ static const struct fw_info supported_firmware[] = { ...@@ -292,6 +292,15 @@ static const struct fw_info supported_firmware[] = {
.mailbox_size = 0x400 - 0x8, .mailbox_size = 0x400 - 0x8,
.mailbox_version = MCU_MSG_VERSION_2018_2, .mailbox_version = MCU_MSG_VERSION_2018_2,
.suballocator_size = SZ_16M, .suballocator_size = SZ_16M,
}, {
.id = 14680,
.id_codec = 126572,
.version = "v2019.2",
.mailbox_cmd = 0x7000,
.mailbox_status = 0x7800,
.mailbox_size = 0x800 - 0x8,
.mailbox_version = MCU_MSG_VERSION_2019_2,
.suballocator_size = SZ_32M,
}, },
}; };
...@@ -930,10 +939,13 @@ static int fill_create_channel_param(struct allegro_channel *channel, ...@@ -930,10 +939,13 @@ static int fill_create_channel_param(struct allegro_channel *channel,
param->dbf_ovr_en = 1; param->dbf_ovr_en = 1;
param->rdo_cost_mode = 1; param->rdo_cost_mode = 1;
param->custom_lda = 1;
param->lf = 1; param->lf = 1;
param->lf_x_tile = 1; param->lf_x_tile = 1;
param->lf_x_slice = 1; param->lf_x_slice = 1;
param->src_bit_depth = 8;
param->beta_offset = -1; param->beta_offset = -1;
param->tc_offset = -1; param->tc_offset = -1;
param->num_slices = 1; param->num_slices = 1;
...@@ -974,13 +986,26 @@ static int fill_create_channel_param(struct allegro_channel *channel, ...@@ -974,13 +986,26 @@ static int fill_create_channel_param(struct allegro_channel *channel,
param->golden_ref_frequency = 10; param->golden_ref_frequency = 10;
param->rate_control_option = 0x00000000; param->rate_control_option = 0x00000000;
param->gop_ctrl_mode = 0x00000000; param->num_pixel = channel->width + channel->height;
param->max_psnr = 4200;
param->max_pixel_value = 255;
param->gop_ctrl_mode = 0x00000002;
param->freq_idr = channel->gop_size; param->freq_idr = channel->gop_size;
param->freq_lt = 0; param->freq_lt = 0;
param->gdr_mode = 0x00000000; param->gdr_mode = 0x00000000;
param->gop_length = channel->gop_size; param->gop_length = channel->gop_size;
param->subframe_latency = 0x00000000; param->subframe_latency = 0x00000000;
param->lda_factors[0] = 51;
param->lda_factors[1] = 90;
param->lda_factors[2] = 151;
param->lda_factors[3] = 151;
param->lda_factors[4] = 151;
param->lda_factors[5] = 151;
param->max_num_merge_cand = 5;
return 0; return 0;
} }
......
...@@ -44,6 +44,7 @@ static ssize_t ...@@ -44,6 +44,7 @@ static ssize_t
allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg)
{ {
unsigned int i = 0; unsigned int i = 0;
enum mcu_msg_version version = msg->header.version;
dst[i++] = msg->reserved0; dst[i++] = msg->reserved0;
dst[i++] = msg->suballoc_dma; dst[i++] = msg->suballoc_dma;
...@@ -51,33 +52,55 @@ allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg) ...@@ -51,33 +52,55 @@ allegro_enc_init(u32 *dst, struct mcu_msg_init_request *msg)
dst[i++] = msg->l2_cache[0]; dst[i++] = msg->l2_cache[0];
dst[i++] = msg->l2_cache[1]; dst[i++] = msg->l2_cache[1];
dst[i++] = msg->l2_cache[2]; dst[i++] = msg->l2_cache[2];
if (version >= MCU_MSG_VERSION_2019_2) {
dst[i++] = -1;
dst[i++] = 0;
}
return i * sizeof(*dst); return i * sizeof(*dst);
} }
static inline u32 settings_get_mcu_codec(struct create_channel_param *param) static inline u32 settings_get_mcu_codec(struct create_channel_param *param)
{ {
enum mcu_msg_version version = param->version;
u32 pixelformat = param->codec; u32 pixelformat = param->codec;
switch (pixelformat) { if (version < MCU_MSG_VERSION_2019_2) {
case V4L2_PIX_FMT_H264: switch (pixelformat) {
default: case V4L2_PIX_FMT_H264:
return 1; default:
return 1;
}
} else {
switch (pixelformat) {
case V4L2_PIX_FMT_H264:
default:
return 0;
}
} }
} }
ssize_t ssize_t
allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) allegro_encode_config_blob(u32 *dst, struct create_channel_param *param)
{ {
enum mcu_msg_version version = param->version;
unsigned int i = 0; unsigned int i = 0;
unsigned int j = 0;
u32 val; u32 val;
unsigned int codec = settings_get_mcu_codec(param); unsigned int codec = settings_get_mcu_codec(param);
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->layer_id;
dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) | dst[i++] = FIELD_PREP(GENMASK(31, 16), param->height) |
FIELD_PREP(GENMASK(15, 0), param->width); FIELD_PREP(GENMASK(15, 0), param->width);
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->videomode;
dst[i++] = param->format; dst[i++] = param->format;
dst[i++] = param->colorspace; if (version < MCU_MSG_VERSION_2019_2)
dst[i++] = param->colorspace;
dst[i++] = param->src_mode; dst[i++] = param->src_mode;
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->src_bit_depth;
dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) | dst[i++] = FIELD_PREP(GENMASK(31, 24), codec) |
FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) | FIELD_PREP(GENMASK(23, 8), param->constraint_set_flags) |
FIELD_PREP(GENMASK(7, 0), param->profile); FIELD_PREP(GENMASK(7, 0), param->profile);
...@@ -94,19 +117,31 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) ...@@ -94,19 +117,31 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param)
val |= param->dbf_ovr_en ? BIT(2) : 0; val |= param->dbf_ovr_en ? BIT(2) : 0;
dst[i++] = val; dst[i++] = val;
val = 0; if (version >= MCU_MSG_VERSION_2019_2) {
val |= param->lf ? BIT(2) : 0; val = 0;
val |= param->lf_x_tile ? BIT(3) : 0; val |= param->custom_lda ? BIT(2) : 0;
val |= param->lf_x_slice ? BIT(4) : 0; val |= param->rdo_cost_mode ? BIT(20) : 0;
val |= param->rdo_cost_mode ? BIT(20) : 0; dst[i++] = val;
dst[i++] = val;
val = 0;
val |= param->lf ? BIT(2) : 0;
val |= param->lf_x_tile ? BIT(3) : 0;
val |= param->lf_x_slice ? BIT(4) : 0;
dst[i++] = val;
} else {
val = 0;
dst[i++] = val;
}
dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) | dst[i++] = FIELD_PREP(GENMASK(15, 8), param->beta_offset) |
FIELD_PREP(GENMASK(7, 0), param->tc_offset); FIELD_PREP(GENMASK(7, 0), param->tc_offset);
dst[i++] = param->unknown11; dst[i++] = param->unknown11;
dst[i++] = param->unknown12; dst[i++] = param->unknown12;
dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) | if (version >= MCU_MSG_VERSION_2019_2)
FIELD_PREP(GENMASK(15, 0), param->num_slices); dst[i++] = param->num_slices;
else
dst[i++] = FIELD_PREP(GENMASK(31, 16), param->prefetch_auto) |
FIELD_PREP(GENMASK(15, 0), param->num_slices);
dst[i++] = param->prefetch_mem_offset; dst[i++] = param->prefetch_mem_offset;
dst[i++] = param->prefetch_mem_size; dst[i++] = param->prefetch_mem_size;
dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) | dst[i++] = FIELD_PREP(GENMASK(31, 16), param->clip_vrt_range) |
...@@ -139,19 +174,51 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) ...@@ -139,19 +174,51 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param)
FIELD_PREP(GENMASK(15, 0), param->pb_delta); FIELD_PREP(GENMASK(15, 0), param->pb_delta);
dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) | dst[i++] = FIELD_PREP(GENMASK(31, 16), param->golden_ref_frequency) |
FIELD_PREP(GENMASK(15, 0), param->golden_delta); FIELD_PREP(GENMASK(15, 0), param->golden_delta);
dst[i++] = param->rate_control_option; if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->rate_control_option;
else
dst[i++] = 0;
if (version >= MCU_MSG_VERSION_2019_2) {
dst[i++] = param->num_pixel;
dst[i++] = FIELD_PREP(GENMASK(31, 16), param->max_pixel_value) |
FIELD_PREP(GENMASK(15, 0), param->max_psnr);
for (j = 0; j < 3; j++)
dst[i++] = param->maxpicturesize[j];
}
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->gop_ctrl_mode;
else
dst[i++] = 0;
dst[i++] = param->gop_ctrl_mode; if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) |
FIELD_PREP(GENMASK(23, 16), param->num_b) |
FIELD_PREP(GENMASK(15, 0), param->gop_length);
dst[i++] = param->freq_idr; dst[i++] = param->freq_idr;
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->enable_lt;
dst[i++] = param->freq_lt; dst[i++] = param->freq_lt;
dst[i++] = param->gdr_mode; dst[i++] = param->gdr_mode;
dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) | if (version < MCU_MSG_VERSION_2019_2)
FIELD_PREP(GENMASK(23, 16), param->num_b) | dst[i++] = FIELD_PREP(GENMASK(31, 24), param->freq_golden_ref) |
FIELD_PREP(GENMASK(15, 0), param->gop_length); FIELD_PREP(GENMASK(23, 16), param->num_b) |
FIELD_PREP(GENMASK(15, 0), param->gop_length);
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = param->tmpdqp;
dst[i++] = param->subframe_latency; dst[i++] = param->subframe_latency;
dst[i++] = param->lda_control_mode; dst[i++] = param->lda_control_mode;
dst[i++] = param->unknown41; if (version < MCU_MSG_VERSION_2019_2)
dst[i++] = param->unknown41;
if (version >= MCU_MSG_VERSION_2019_2) {
for (j = 0; j < 6; j++)
dst[i++] = param->lda_factors[j];
dst[i++] = param->max_num_merge_cand;
}
return i * sizeof(*dst); return i * sizeof(*dst);
} }
...@@ -159,12 +226,20 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param) ...@@ -159,12 +226,20 @@ allegro_encode_config_blob(u32 *dst, struct create_channel_param *param)
static ssize_t static ssize_t
allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg) allegro_enc_create_channel(u32 *dst, struct mcu_msg_create_channel *msg)
{ {
enum mcu_msg_version version = msg->header.version;
unsigned int i = 0; unsigned int i = 0;
dst[i++] = msg->user_id; dst[i++] = msg->user_id;
memcpy(&dst[i], msg->blob, msg->blob_size); if (version >= MCU_MSG_VERSION_2019_2) {
i += msg->blob_size / sizeof(*dst); dst[i++] = msg->blob_mcu_addr;
} else {
memcpy(&dst[i], msg->blob, msg->blob_size);
i += msg->blob_size / sizeof(*dst);
}
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = msg->ep1_addr;
return i * sizeof(*dst); return i * sizeof(*dst);
} }
...@@ -173,8 +248,15 @@ ssize_t allegro_decode_config_blob(struct create_channel_param *param, ...@@ -173,8 +248,15 @@ ssize_t allegro_decode_config_blob(struct create_channel_param *param,
struct mcu_msg_create_channel_response *msg, struct mcu_msg_create_channel_response *msg,
u32 *src) u32 *src)
{ {
param->num_ref_idx_l0 = msg->num_ref_idx_l0; enum mcu_msg_version version = msg->header.version;
param->num_ref_idx_l1 = msg->num_ref_idx_l1;
if (version >= MCU_MSG_VERSION_2019_2) {
param->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[9]);
param->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[9]);
} else {
param->num_ref_idx_l0 = msg->num_ref_idx_l0;
param->num_ref_idx_l1 = msg->num_ref_idx_l1;
}
return 0; return 0;
} }
...@@ -229,6 +311,7 @@ allegro_enc_put_stream_buffer(u32 *dst, ...@@ -229,6 +311,7 @@ allegro_enc_put_stream_buffer(u32 *dst,
static ssize_t static ssize_t
allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg)
{ {
enum mcu_msg_version version = msg->header.version;
unsigned int i = 0; unsigned int i = 0;
dst[i++] = msg->channel_id; dst[i++] = msg->channel_id;
...@@ -237,6 +320,14 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) ...@@ -237,6 +320,14 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg)
dst[i++] = msg->encoding_options; dst[i++] = msg->encoding_options;
dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) | dst[i++] = FIELD_PREP(GENMASK(31, 16), msg->padding) |
FIELD_PREP(GENMASK(15, 0), msg->pps_qp); FIELD_PREP(GENMASK(15, 0), msg->pps_qp);
if (version >= MCU_MSG_VERSION_2019_2) {
dst[i++] = 0;
dst[i++] = 0;
dst[i++] = 0;
dst[i++] = 0;
}
dst[i++] = lower_32_bits(msg->user_param); dst[i++] = lower_32_bits(msg->user_param);
dst[i++] = upper_32_bits(msg->user_param); dst[i++] = upper_32_bits(msg->user_param);
dst[i++] = lower_32_bits(msg->src_handle); dst[i++] = lower_32_bits(msg->src_handle);
...@@ -244,7 +335,11 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg) ...@@ -244,7 +335,11 @@ allegro_enc_encode_frame(u32 *dst, struct mcu_msg_encode_frame *msg)
dst[i++] = msg->request_options; dst[i++] = msg->request_options;
dst[i++] = msg->src_y; dst[i++] = msg->src_y;
dst[i++] = msg->src_uv; dst[i++] = msg->src_uv;
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = msg->is_10_bit;
dst[i++] = msg->stride; dst[i++] = msg->stride;
if (version >= MCU_MSG_VERSION_2019_2)
dst[i++] = msg->format;
dst[i++] = msg->ep2; dst[i++] = msg->ep2;
dst[i++] = lower_32_bits(msg->ep2_v); dst[i++] = lower_32_bits(msg->ep2_v);
dst[i++] = upper_32_bits(msg->ep2_v); dst[i++] = upper_32_bits(msg->ep2_v);
...@@ -266,14 +361,21 @@ static ssize_t ...@@ -266,14 +361,21 @@ static ssize_t
allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg, allegro_dec_create_channel(struct mcu_msg_create_channel_response *msg,
u32 *src) u32 *src)
{ {
enum mcu_msg_version version = msg->header.version;
unsigned int i = 0; unsigned int i = 0;
msg->channel_id = src[i++]; msg->channel_id = src[i++];
msg->user_id = src[i++]; msg->user_id = src[i++];
msg->options = src[i++]; /*
msg->num_core = src[i++]; * Version >= MCU_MSG_VERSION_2019_2 is handled in
msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]); * allegro_decode_config_blob().
msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]); */
if (version < MCU_MSG_VERSION_2019_2) {
msg->options = src[i++];
msg->num_core = src[i++];
msg->num_ref_idx_l0 = FIELD_GET(GENMASK(7, 4), src[i]);
msg->num_ref_idx_l1 = FIELD_GET(GENMASK(11, 8), src[i++]);
}
msg->int_buffers_count = src[i++]; msg->int_buffers_count = src[i++];
msg->int_buffers_size = src[i++]; msg->int_buffers_size = src[i++];
msg->rec_buffers_count = src[i++]; msg->rec_buffers_count = src[i++];
...@@ -298,6 +400,7 @@ allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg, ...@@ -298,6 +400,7 @@ allegro_dec_destroy_channel(struct mcu_msg_destroy_channel_response *msg,
static ssize_t static ssize_t
allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src)
{ {
enum mcu_msg_version version = msg->header.version;
unsigned int i = 0; unsigned int i = 0;
unsigned int j; unsigned int j;
...@@ -341,6 +444,12 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src) ...@@ -341,6 +444,12 @@ allegro_dec_encode_frame(struct mcu_msg_encode_frame_response *msg, u32 *src)
msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]); msg->pps_qp = FIELD_GET(GENMASK(15, 0), src[i++]);
msg->reserved2 = src[i++]; msg->reserved2 = src[i++];
if (version >= MCU_MSG_VERSION_2019_2) {
msg->reserved3 = src[i++];
msg->reserved4 = src[i++];
msg->reserved5 = src[i++];
msg->reserved6 = src[i++];
}
return i * sizeof(*src); return i * sizeof(*src);
} }
......
...@@ -22,6 +22,7 @@ enum mcu_msg_type { ...@@ -22,6 +22,7 @@ enum mcu_msg_type {
enum mcu_msg_version { enum mcu_msg_version {
MCU_MSG_VERSION_2018_2, MCU_MSG_VERSION_2018_2,
MCU_MSG_VERSION_2019_2,
}; };
const char *msg_type_name(enum mcu_msg_type type); const char *msg_type_name(enum mcu_msg_type type);
...@@ -46,11 +47,14 @@ struct mcu_msg_init_response { ...@@ -46,11 +47,14 @@ struct mcu_msg_init_response {
struct create_channel_param { struct create_channel_param {
enum mcu_msg_version version; enum mcu_msg_version version;
u32 layer_id;
u16 width; u16 width;
u16 height; u16 height;
u32 videomode;
u32 format; u32 format;
u32 colorspace; u32 colorspace;
u32 src_mode; u32 src_mode;
u32 src_bit_depth;
u8 profile; u8 profile;
u16 constraint_set_flags; u16 constraint_set_flags;
u32 codec; u32 codec;
...@@ -59,9 +63,11 @@ struct create_channel_param { ...@@ -59,9 +63,11 @@ struct create_channel_param {
u32 log2_max_poc; u32 log2_max_poc;
u32 log2_max_frame_num; u32 log2_max_frame_num;
u32 temporal_mvp_enable; u32 temporal_mvp_enable;
u32 enable_reordering;
u32 dbf_ovr_en; u32 dbf_ovr_en;
u32 num_ref_idx_l0; u32 num_ref_idx_l0;
u32 num_ref_idx_l1; u32 num_ref_idx_l1;
u32 custom_lda;
u32 rdo_cost_mode; u32 rdo_cost_mode;
u32 lf; u32 lf;
u32 lf_x_tile; u32 lf_x_tile;
...@@ -105,6 +111,10 @@ struct create_channel_param { ...@@ -105,6 +111,10 @@ struct create_channel_param {
u16 golden_delta; u16 golden_delta;
u16 golden_ref_frequency; u16 golden_ref_frequency;
u32 rate_control_option; u32 rate_control_option;
u32 num_pixel;
u16 max_psnr;
u16 max_pixel_value;
u32 maxpicturesize[3];
/* gop param */ /* gop param */
u32 gop_ctrl_mode; u32 gop_ctrl_mode;
...@@ -114,10 +124,16 @@ struct create_channel_param { ...@@ -114,10 +124,16 @@ struct create_channel_param {
u16 gop_length; u16 gop_length;
u8 num_b; u8 num_b;
u8 freq_golden_ref; u8 freq_golden_ref;
u32 enable_lt;
u32 tmpdqp;
u32 subframe_latency; u32 subframe_latency;
u32 lda_control_mode; u32 lda_control_mode;
u32 unknown41; u32 unknown41;
u32 lda_factors[6];
u32 max_num_merge_cand;
}; };
struct mcu_msg_create_channel { struct mcu_msg_create_channel {
...@@ -126,6 +142,7 @@ struct mcu_msg_create_channel { ...@@ -126,6 +142,7 @@ struct mcu_msg_create_channel {
u32 *blob; u32 *blob;
size_t blob_size; size_t blob_size;
u32 blob_mcu_addr; u32 blob_mcu_addr;
u32 ep1_addr;
}; };
struct mcu_msg_create_channel_response { struct mcu_msg_create_channel_response {
...@@ -203,9 +220,12 @@ struct mcu_msg_encode_frame { ...@@ -203,9 +220,12 @@ struct mcu_msg_encode_frame {
/* u32 scene_change_delay (optional) */ /* u32 scene_change_delay (optional) */
/* rate control param (optional) */ /* rate control param (optional) */
/* gop param (optional) */ /* gop param (optional) */
/* dynamic resolution params (optional) */
u32 src_y; u32 src_y;
u32 src_uv; u32 src_uv;
u32 is_10_bit;
u32 stride; u32 stride;
u32 format;
u32 ep2; u32 ep2;
u64 ep2_v; u64 ep2_v;
}; };
...@@ -249,6 +269,10 @@ struct mcu_msg_encode_frame_response { ...@@ -249,6 +269,10 @@ struct mcu_msg_encode_frame_response {
u16 pps_qp; u16 pps_qp;
u16 reserved1; u16 reserved1;
u32 reserved2; u32 reserved2;
u32 reserved3;
u32 reserved4;
u32 reserved5;
u32 reserved6;
}; };
union mcu_msg_response { union mcu_msg_response {
......
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