Commit 1fae6dfe authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

   This pull request includes i80 interface support, module auto-loading
   ipp consolidation, and trivail fixups and cleanups.

Summary:
- Add i80 interface support. For this, we added some features to
  Exynos drm framework, which don't affect any other SoC and common
  framework because they are specific to Exynos drm.
- Add module auto-loading support. For this, sub drivers of Exynos drm
  exports their of match tables to userspace. This allows modules to be
  loaded automatically based on devicetree information
- Consolidate ipp driver. This patch just just includes cleanups and
  a littl bit refactoring codes.

If there is any problem, please kindly let me know.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: (38 commits)
  drm/exynos: g2d: let exynos_g2d_get_ver_ioctl fail
  drm/exynos: g2d: make ioctls more robust
  drm/exynos: hdmi: add null check for hdmiphy_port
  drm/exynos: control blending of mixer graphic layer 0
  drm/exynos: Add MODULE_DEVICE_TABLE entries for various components
  Subject: Revert "drm/exynos: remove MODULE_DEVICE_TABLE definitions"
  Subject: Revert "drm/exynos: fix module build error"
  drm/exynos/ipp: simplify ipp_find_driver
  drm/exynos/ipp: simplify ipp_create_id
  drm/exynos/ipp: remove redundant messages
  drm/exynos/ipp: simplify ipp_find_obj
  drm/exynos/ipp: remove useless registration checks
  drm/exynos/ipp: simplify memory check function
  drm/exynos/ipp: remove incorrect checks of list_first_entry result
  drm/exynos/ipp: remove temporary variable
  drm/exynos/ipp: correct address type
  drm/exynos/ipp: remove struct exynos_drm_ipp_private
  drm/exynos/ipp: remove unused field from exynos_drm_ipp_private
  drm/exynos/ipp: remove type casting
  drm/exynos: g2d: add exynos4212 as a compatible device.
  ...
parents 08d645c1 ef7ce055
Exynos MIPI DSI Master
Required properties:
- compatible: "samsung,exynos4210-mipi-dsi"
- compatible: value should be one of the following
"samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */
"samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */
- reg: physical base address and length of the registers set for the device
- interrupts: should contain DSI interrupt
- clocks: list of clock specifiers, must contain an entry for each required
......
......@@ -4,8 +4,9 @@ Required properties:
- compatible: value should be one of the following:
1) "samsung,exynos5-mixer" <DEPRECATED>
2) "samsung,exynos4210-mixer"
3) "samsung,exynos5250-mixer"
4) "samsung,exynos5420-mixer"
3) "samsung,exynos4212-mixer"
4) "samsung,exynos5250-mixer"
5) "samsung,exynos5420-mixer"
- reg: physical base address of the mixer and length of memory mapped
region.
......
......@@ -44,6 +44,34 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document [1].
Can be used in case timings cannot be provided otherwise
or to override timings provided by the panel.
- samsung,sysreg: handle to syscon used to control the system registers
- i80-if-timings: timing configuration for lcd i80 interface support.
- cs-setup: clock cycles for the active period of address signal is enabled
until chip select is enabled.
If not specified, the default value(0) will be used.
- wr-setup: clock cycles for the active period of CS signal is enabled until
write signal is enabled.
If not specified, the default value(0) will be used.
- wr-active: clock cycles for the active period of CS is enabled.
If not specified, the default value(1) will be used.
- wr-hold: clock cycles for the active period of CS is disabled until write
signal is disabled.
If not specified, the default value(0) will be used.
The parameters are defined as:
VCLK(internal) __|??????|_____|??????|_____|??????|_____|??????|_____|??
: : : : :
Address Output --:<XXXXXXXXXXX:XXXXXXXXXXXX:XXXXXXXXXXXX:XXXXXXXXXXXX:XX
| cs-setup+1 | : : :
|<---------->| : : :
Chip Select ???????????????|____________:____________:____________|??
| wr-setup+1 | | wr-hold+1 |
|<---------->| |<---------->|
Write Enable ????????????????????????????|____________|???????????????
| wr-active+1|
|<---------->|
Video Data ----------------------------<XXXXXXXXXXXXXXXXXXXXXXXXX>--
The device node can contain 'port' child nodes according to the bindings defined
in [2]. The following are properties specific to those nodes:
......
......@@ -608,6 +608,7 @@ fimd: fimd@11c00000 {
clocks = <&clock CLK_SCLK_FIMD0>, <&clock CLK_FIMD0>;
clock-names = "sclk_fimd", "fimd";
samsung,power-domain = <&pd_lcd0>;
samsung,sysreg = <&sys_reg>;
status = "disabled";
};
};
......@@ -87,6 +87,7 @@ fimd@14400000 {
reg = <0x14400000 0x40000>;
interrupt-names = "fifo", "vsync", "lcd_sys";
interrupts = <18 4>, <18 5>, <18 6>;
samsung,sysreg = <&sysreg_system_controller>;
status = "disabled";
};
......
......@@ -517,6 +517,26 @@ dp: dp-controller@145B0000 {
phy-names = "dp";
};
mipi_phy: video-phy@10040714 {
compatible = "samsung,s5pv210-mipi-video-phy";
reg = <0x10040714 12>;
#phy-cells = <1>;
};
dsi@14500000 {
compatible = "samsung,exynos5410-mipi-dsi";
reg = <0x14500000 0x10000>;
interrupts = <0 82 0>;
samsung,power-domain = <&disp_pd>;
phys = <&mipi_phy 1>;
phy-names = "dsim";
clocks = <&clock CLK_DSIM1>, <&clock CLK_SCLK_MIPI1>;
clock-names = "bus_clk", "pll_clk";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
fimd: fimd@14400000 {
samsung,power-domain = <&disp_pd>;
clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>;
......
......@@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS
select MFD_SYSCON
help
Choose this option if you want to use Exynos FIMD for DRM.
......
......@@ -1376,6 +1376,7 @@ static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_dp_match);
struct platform_driver dp_driver = {
.probe = exynos_dp_probe,
......@@ -1390,4 +1391,4 @@ struct platform_driver dp_driver = {
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DP Driver");
MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL v2");
......@@ -69,8 +69,10 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
if (mode > DRM_MODE_DPMS_ON) {
/* wait for the completion of page flip. */
wait_event(exynos_crtc->pending_flip_queue,
atomic_read(&exynos_crtc->pending_flip) == 0);
if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
!atomic_read(&exynos_crtc->pending_flip),
HZ/20))
atomic_set(&exynos_crtc->pending_flip, 0);
drm_vblank_off(crtc->dev, exynos_crtc->pipe);
}
......@@ -259,6 +261,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
spin_lock_irq(&dev->event_lock);
drm_vblank_put(dev, exynos_crtc->pipe);
list_del(&event->base.link);
atomic_set(&exynos_crtc->pending_flip, 0);
spin_unlock_irq(&dev->event_lock);
goto out;
......@@ -508,3 +511,11 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
return -EPERM;
}
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
{
struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
if (manager->ops->te_handler)
manager->ops->te_handler(manager);
}
......@@ -36,4 +36,11 @@ void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
unsigned int out_type);
/*
* This function calls the crtc device(manager)'s te_handler() callback
* to trigger to transfer video image at the tearing effect synchronization
* signal.
*/
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
#endif
......@@ -358,7 +358,7 @@ static int exynos_drm_sys_suspend(struct device *dev)
struct drm_device *drm_dev = dev_get_drvdata(dev);
pm_message_t message;
if (pm_runtime_suspended(dev))
if (pm_runtime_suspended(dev) || !drm_dev)
return 0;
message.event = PM_EVENT_SUSPEND;
......@@ -369,7 +369,7 @@ static int exynos_drm_sys_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
if (pm_runtime_suspended(dev))
if (pm_runtime_suspended(dev) || !drm_dev)
return 0;
return exynos_drm_resume(drm_dev);
......
......@@ -186,6 +186,8 @@ struct exynos_drm_display {
* @win_commit: apply hardware specific overlay data to registers.
* @win_enable: enable hardware specific overlay.
* @win_disable: disable hardware specific overlay.
* @te_handler: trigger to transfer video image at the tearing effect
* synchronization signal if there is a page flip request.
*/
struct exynos_drm_manager;
struct exynos_drm_manager_ops {
......@@ -204,6 +206,7 @@ struct exynos_drm_manager_ops {
void (*win_commit)(struct exynos_drm_manager *mgr, int zpos);
void (*win_enable)(struct exynos_drm_manager *mgr, int zpos);
void (*win_disable)(struct exynos_drm_manager *mgr, int zpos);
void (*te_handler)(struct exynos_drm_manager *mgr);
};
/*
......@@ -234,14 +237,9 @@ struct exynos_drm_g2d_private {
struct list_head userptr_list;
};
struct exynos_drm_ipp_private {
struct device *dev;
struct list_head event_list;
};
struct drm_exynos_file_private {
struct exynos_drm_g2d_private *g2d_priv;
struct exynos_drm_ipp_private *ipp_priv;
struct device *ipp_dev;
struct file *anon_filp;
};
......
This diff is collapsed.
......@@ -1887,6 +1887,7 @@ static const struct of_device_id fimc_of_match[] = {
{ .compatible = "samsung,exynos4212-fimc" },
{ },
};
MODULE_DEVICE_TABLE(of, fimc_of_match);
struct platform_driver fimc_driver = {
.probe = fimc_probe,
......
This diff is collapsed.
......@@ -1042,8 +1042,23 @@ static int g2d_check_reg_offset(struct device *dev,
int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct device *dev;
struct g2d_data *g2d;
struct drm_exynos_g2d_get_ver *ver = data;
if (!g2d_priv)
return -ENODEV;
dev = g2d_priv->dev;
if (!dev)
return -ENODEV;
g2d = dev_get_drvdata(dev);
if (!g2d)
return -EFAULT;
ver->major = G2D_HW_MAJOR_VER;
ver->minor = G2D_HW_MINOR_VER;
......@@ -1056,7 +1071,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct device *dev = g2d_priv->dev;
struct device *dev;
struct g2d_data *g2d;
struct drm_exynos_g2d_set_cmdlist *req = data;
struct drm_exynos_g2d_cmd *cmd;
......@@ -1067,6 +1082,10 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
int size;
int ret;
if (!g2d_priv)
return -ENODEV;
dev = g2d_priv->dev;
if (!dev)
return -ENODEV;
......@@ -1223,13 +1242,17 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct device *dev = g2d_priv->dev;
struct device *dev;
struct g2d_data *g2d;
struct drm_exynos_g2d_exec *req = data;
struct g2d_runqueue_node *runqueue_node;
struct list_head *run_cmdlist;
struct list_head *event_list;
if (!g2d_priv)
return -ENODEV;
dev = g2d_priv->dev;
if (!dev)
return -ENODEV;
......@@ -1544,8 +1567,10 @@ static const struct dev_pm_ops g2d_pm_ops = {
static const struct of_device_id exynos_g2d_match[] = {
{ .compatible = "samsung,exynos5250-g2d" },
{ .compatible = "samsung,exynos4212-g2d" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_g2d_match);
struct platform_driver g2d_driver = {
.probe = g2d_probe,
......
......@@ -301,7 +301,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, filp, gem_handle);
......@@ -310,8 +309,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
return;
}
exynos_gem_obj = to_exynos_gem_obj(obj);
drm_gem_object_unreference_unlocked(obj);
/*
......
This diff is collapsed.
......@@ -48,7 +48,7 @@ struct drm_exynos_ipp_cmd_work {
/*
* A structure of command node.
*
* @priv: IPP private information.
* @dev: IPP device.
* @list: list head to command queue information.
* @event_list: list head of event.
* @mem_list: list head to source,destination memory queue information.
......@@ -64,7 +64,7 @@ struct drm_exynos_ipp_cmd_work {
* @state: state of command node.
*/
struct drm_exynos_ipp_cmd_node {
struct exynos_drm_ipp_private *priv;
struct device *dev;
struct list_head list;
struct list_head event_list;
struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
......
......@@ -691,6 +691,7 @@ static const struct of_device_id exynos_rotator_match[] = {
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_rotator_match);
static int rotator_probe(struct platform_device *pdev)
{
......
......@@ -84,6 +84,7 @@ struct hdmi_resources {
struct clk *sclk_hdmiphy;
struct clk *mout_hdmi;
struct regulator_bulk_data *regul_bulk;
struct regulator *reg_hdmi_en;
int regul_count;
};
......@@ -592,6 +593,13 @@ static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.is_apb_phy = 0,
};
static struct hdmi_driver_data exynos4210_hdmi_driver_data = {
.type = HDMI_TYPE13,
.phy_confs = hdmiphy_v13_configs,
.phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
.is_apb_phy = 0,
};
static struct hdmi_driver_data exynos5_hdmi_driver_data = {
.type = HDMI_TYPE14,
.phy_confs = hdmiphy_v13_configs,
......@@ -1241,14 +1249,13 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
static void hdmi_audio_init(struct hdmi_context *hdata)
{
u32 sample_rate, bits_per_sample, frame_size_code;
u32 sample_rate, bits_per_sample;
u32 data_num, bit_ch, sample_frq;
u32 val;
u8 acr[7];
sample_rate = 44100;
bits_per_sample = 16;
frame_size_code = 0;
switch (bits_per_sample) {
case 20:
......@@ -2168,7 +2175,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
struct device *dev = hdata->dev;
struct hdmi_resources *res = &hdata->res;
static char *supply[] = {
"hdmi-en",
"vdd",
"vdd_osc",
"vdd_pll",
......@@ -2228,6 +2234,20 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
}
res->regul_count = ARRAY_SIZE(supply);
res->reg_hdmi_en = devm_regulator_get(dev, "hdmi-en");
if (IS_ERR(res->reg_hdmi_en) && PTR_ERR(res->reg_hdmi_en) != -ENOENT) {
DRM_ERROR("failed to get hdmi-en regulator\n");
return PTR_ERR(res->reg_hdmi_en);
}
if (!IS_ERR(res->reg_hdmi_en)) {
ret = regulator_enable(res->reg_hdmi_en);
if (ret) {
DRM_ERROR("failed to enable hdmi-en regulator\n");
return ret;
}
} else
res->reg_hdmi_en = NULL;
return ret;
fail:
DRM_ERROR("HDMI resource init - failed\n");
......@@ -2262,6 +2282,9 @@ static struct of_device_id hdmi_match_types[] = {
{
.compatible = "samsung,exynos5-hdmi",
.data = &exynos5_hdmi_driver_data,
}, {
.compatible = "samsung,exynos4210-hdmi",
.data = &exynos4210_hdmi_driver_data,
}, {
.compatible = "samsung,exynos4212-hdmi",
.data = &exynos4212_hdmi_driver_data,
......@@ -2272,6 +2295,7 @@ static struct of_device_id hdmi_match_types[] = {
/* end node */
}
};
MODULE_DEVICE_TABLE (of, hdmi_match_types);
static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
......@@ -2494,6 +2518,10 @@ static int hdmi_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&hdata->hotplug_work);
if (hdata->res.reg_hdmi_en)
regulator_disable(hdata->res.reg_hdmi_en);
if (hdata->hdmiphy_port)
put_device(&hdata->hdmiphy_port->dev);
put_device(&hdata->ddc_adpt->dev);
......
......@@ -76,7 +76,7 @@ struct mixer_resources {
struct clk *vp;
struct clk *sclk_mixer;
struct clk *sclk_hdmi;
struct clk *sclk_dac;
struct clk *mout_mixer;
};
enum mixer_version_id {
......@@ -93,6 +93,7 @@ struct mixer_context {
bool interlace;
bool powered;
bool vp_enabled;
bool has_sclk;
u32 int_en;
struct mutex mixer_mutex;
......@@ -106,6 +107,7 @@ struct mixer_context {
struct mixer_drv_data {
enum mixer_version_id version;
bool is_vp_enabled;
bool has_sclk;
};
static const u8 filter_y_horiz_tap8[] = {
......@@ -363,6 +365,11 @@ static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
mixer_reg_writemask(res, MXR_CFG, val,
MXR_CFG_VP_ENABLE);
/* control blending of graphic layer 0 */
mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
MXR_GRP_CFG_BLEND_PRE_MUL |
MXR_GRP_CFG_PIXEL_BLEND_EN);
}
break;
}
......@@ -809,19 +816,23 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
dev_err(dev, "failed to get clock 'vp'\n");
return -ENODEV;
}
if (mixer_ctx->has_sclk) {
mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
if (IS_ERR(mixer_res->sclk_mixer)) {
dev_err(dev, "failed to get clock 'sclk_mixer'\n");
return -ENODEV;
}
mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
if (IS_ERR(mixer_res->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
if (IS_ERR(mixer_res->mout_mixer)) {
dev_err(dev, "failed to get clock 'mout_mixer'\n");
return -ENODEV;
}
if (mixer_res->sclk_hdmi)
clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
clk_set_parent(mixer_res->mout_mixer,
mixer_res->sclk_hdmi);
}
res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
if (res == NULL) {
......@@ -1082,6 +1093,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
clk_prepare_enable(res->mixer);
if (ctx->vp_enabled) {
clk_prepare_enable(res->vp);
if (ctx->has_sclk)
clk_prepare_enable(res->sclk_mixer);
}
......@@ -1121,6 +1133,7 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr)
clk_disable_unprepare(res->mixer);
if (ctx->vp_enabled) {
clk_disable_unprepare(res->vp);
if (ctx->has_sclk)
clk_disable_unprepare(res->sclk_mixer);
}
......@@ -1189,9 +1202,15 @@ static struct mixer_drv_data exynos5250_mxr_drv_data = {
.is_vp_enabled = 0,
};
static struct mixer_drv_data exynos4212_mxr_drv_data = {
.version = MXR_VER_0_0_0_16,
.is_vp_enabled = 1,
};
static struct mixer_drv_data exynos4210_mxr_drv_data = {
.version = MXR_VER_0_0_0_16,
.is_vp_enabled = 1,
.has_sclk = 1,
};
static struct platform_device_id mixer_driver_types[] = {
......@@ -1208,6 +1227,12 @@ static struct platform_device_id mixer_driver_types[] = {
static struct of_device_id mixer_match_types[] = {
{
.compatible = "samsung,exynos4210-mixer",
.data = &exynos4210_mxr_drv_data,
}, {
.compatible = "samsung,exynos4212-mixer",
.data = &exynos4212_mxr_drv_data,
}, {
.compatible = "samsung,exynos5-mixer",
.data = &exynos5250_mxr_drv_data,
}, {
......@@ -1220,6 +1245,7 @@ static struct of_device_id mixer_match_types[] = {
/* end node */
}
};
MODULE_DEVICE_TABLE(of, mixer_match_types);
static int mixer_bind(struct device *dev, struct device *manager, void *data)
{
......@@ -1251,6 +1277,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
ctx->pdev = pdev;
ctx->dev = dev;
ctx->vp_enabled = drv->is_vp_enabled;
ctx->has_sclk = drv->has_sclk;
ctx->mxr_ver = drv->version;
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
......
......@@ -19,6 +19,7 @@
/* VIDCON0 */
#define VIDCON0 0x00
#define VIDCON0_DSI_EN (1 << 30)
#define VIDCON0_INTERLACE (1 << 29)
#define VIDCON0_VIDOUT_MASK (0x7 << 26)
#define VIDCON0_VIDOUT_SHIFT 26
......@@ -355,7 +356,7 @@
#define VIDINTCON0_INT_ENABLE (1 << 0)
#define VIDINTCON1 0x134
#define VIDINTCON1_INT_I180 (1 << 2)
#define VIDINTCON1_INT_I80 (1 << 2)
#define VIDINTCON1_INT_FRAME (1 << 1)
#define VIDINTCON1_INT_FIFO (1 << 0)
......
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