提交 0364d4fe 编写于 作者: D 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

  Add Exynos4415 SoC support, some fixups and cleanups.

   Summary:
   - Resolve kernel lockup issue incurred by probe request in probe context.
     . For this, it moves all register codes of sub drivers into init function
       and adds component binding support for vidi driver.
   - Add Exynos4415 SoC support.
   - Make each manager and display object to be embedded
     in each driver context.
   - Fix and clean up FIMD and MIPI-DSI drivers.
   - Clean up unnecesary or wrong descriptions.
   - And trivial cleanups.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos: (58 commits)
  drm/exynos: avoid leak if exynos_dpi_probe() fails
  drm/exynos: Fix exynos_dpi_remove() parameter
  drm/exynos: vidi: add component support
  drm/exynos: fix exynos_drm_component_del
  drm/exynos/ipp: fix error return code
  drm/exynos: clean up machine compatible string check
  drm/exynos: move Exynos platform drivers registration to init
  Revert "drm/exynos: fix null pointer dereference issue"
  drm/exynos/dpi: stop using display->ctx pointer
  drm/exynos/dpi: embed display into private context
  drm/exynos/dp: stop using display->ctx pointer
  drm/exynos/dp: embed display into private context
  drm/exynos/vidi: stop using display->ctx pointer
  drm/exynos/vidi: embed display into private context
  drm/exynos/hdmi: stop using display->ctx pointer
  drm/exynos/hdmi: embed display into private context
  drm/exynos/fimd: stop using manager->ctx pointer
  drm/exynos/fimd: embed manager into private context
  drm/exynos/vidi: stop using manager->ctx pointer
  drm/exynos/vidi: embed manager into private context
  ...
...@@ -4,6 +4,7 @@ Required properties: ...@@ -4,6 +4,7 @@ Required properties:
- compatible: value should be one of the following - compatible: value should be one of the following
"samsung,exynos3250-mipi-dsi" /* for Exynos3250/3472 SoCs */ "samsung,exynos3250-mipi-dsi" /* for Exynos3250/3472 SoCs */
"samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */
"samsung,exynos4415-mipi-dsi" /* for Exynos4415 SoC */
"samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */ "samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */
- reg: physical base address and length of the registers set for the device - reg: physical base address and length of the registers set for the device
- interrupts: should contain DSI interrupt - interrupts: should contain DSI interrupt
......
...@@ -11,6 +11,7 @@ Required properties: ...@@ -11,6 +11,7 @@ Required properties:
"samsung,s5pv210-fimd"; /* for S5PV210 SoC */ "samsung,s5pv210-fimd"; /* for S5PV210 SoC */
"samsung,exynos3250-fimd"; /* for Exynos3250/3472 SoCs */ "samsung,exynos3250-fimd"; /* for Exynos3250/3472 SoCs */
"samsung,exynos4210-fimd"; /* for Exynos4 SoCs */ "samsung,exynos4210-fimd"; /* for Exynos4 SoCs */
"samsung,exynos4415-fimd"; /* for Exynos4415 SoC */
"samsung,exynos5250-fimd"; /* for Exynos5 SoCs */ "samsung,exynos5250-fimd"; /* for Exynos5 SoCs */
- reg: physical base address and length of the FIMD registers set. - reg: physical base address and length of the FIMD registers set.
......
...@@ -30,12 +30,17 @@ ...@@ -30,12 +30,17 @@
#include <drm/drm_panel.h> #include <drm/drm_panel.h>
#include <drm/bridge/ptn3460.h> #include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h"
#include "exynos_dp_core.h" #include "exynos_dp_core.h"
#define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \ #define ctx_from_connector(c) container_of(c, struct exynos_dp_device, \
connector) connector)
static inline struct exynos_dp_device *
display_to_dp(struct exynos_drm_display *d)
{
return container_of(d, struct exynos_dp_device, display);
}
struct bridge_init { struct bridge_init {
struct i2c_client *client; struct i2c_client *client;
struct device_node *node; struct device_node *node;
...@@ -882,7 +887,7 @@ static void exynos_dp_hotplug(struct work_struct *work) ...@@ -882,7 +887,7 @@ static void exynos_dp_hotplug(struct work_struct *work)
static void exynos_dp_commit(struct exynos_drm_display *display) static void exynos_dp_commit(struct exynos_drm_display *display)
{ {
struct exynos_dp_device *dp = display->ctx; struct exynos_dp_device *dp = display_to_dp(display);
int ret; int ret;
/* Keep the panel disabled while we configure video */ /* Keep the panel disabled while we configure video */
...@@ -1020,7 +1025,7 @@ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev, ...@@ -1020,7 +1025,7 @@ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
static int exynos_dp_create_connector(struct exynos_drm_display *display, static int exynos_dp_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct exynos_dp_device *dp = display->ctx; struct exynos_dp_device *dp = display_to_dp(display);
struct drm_connector *connector = &dp->connector; struct drm_connector *connector = &dp->connector;
int ret; int ret;
...@@ -1052,33 +1057,19 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, ...@@ -1052,33 +1057,19 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
static void exynos_dp_phy_init(struct exynos_dp_device *dp) static void exynos_dp_phy_init(struct exynos_dp_device *dp)
{ {
if (dp->phy) { if (dp->phy)
phy_power_on(dp->phy); phy_power_on(dp->phy);
} else if (dp->phy_addr) {
u32 reg;
reg = __raw_readl(dp->phy_addr);
reg |= dp->enable_mask;
__raw_writel(reg, dp->phy_addr);
}
} }
static void exynos_dp_phy_exit(struct exynos_dp_device *dp) static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
{ {
if (dp->phy) { if (dp->phy)
phy_power_off(dp->phy); phy_power_off(dp->phy);
} else if (dp->phy_addr) {
u32 reg;
reg = __raw_readl(dp->phy_addr);
reg &= ~(dp->enable_mask);
__raw_writel(reg, dp->phy_addr);
}
} }
static void exynos_dp_poweron(struct exynos_drm_display *display) static void exynos_dp_poweron(struct exynos_drm_display *display)
{ {
struct exynos_dp_device *dp = display->ctx; struct exynos_dp_device *dp = display_to_dp(display);
if (dp->dpms_mode == DRM_MODE_DPMS_ON) if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return; return;
...@@ -1099,7 +1090,7 @@ static void exynos_dp_poweron(struct exynos_drm_display *display) ...@@ -1099,7 +1090,7 @@ static void exynos_dp_poweron(struct exynos_drm_display *display)
static void exynos_dp_poweroff(struct exynos_drm_display *display) static void exynos_dp_poweroff(struct exynos_drm_display *display)
{ {
struct exynos_dp_device *dp = display->ctx; struct exynos_dp_device *dp = display_to_dp(display);
if (dp->dpms_mode != DRM_MODE_DPMS_ON) if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return; return;
...@@ -1124,7 +1115,7 @@ static void exynos_dp_poweroff(struct exynos_drm_display *display) ...@@ -1124,7 +1115,7 @@ static void exynos_dp_poweroff(struct exynos_drm_display *display)
static void exynos_dp_dpms(struct exynos_drm_display *display, int mode) static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
{ {
struct exynos_dp_device *dp = display->ctx; struct exynos_dp_device *dp = display_to_dp(display);
switch (mode) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
...@@ -1147,11 +1138,6 @@ static struct exynos_drm_display_ops exynos_dp_display_ops = { ...@@ -1147,11 +1138,6 @@ static struct exynos_drm_display_ops exynos_dp_display_ops = {
.commit = exynos_dp_commit, .commit = exynos_dp_commit,
}; };
static struct exynos_drm_display exynos_dp_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dp_display_ops,
};
static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev) static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
{ {
struct device_node *dp_node = dev->of_node; struct device_node *dp_node = dev->of_node;
...@@ -1210,44 +1196,6 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev) ...@@ -1210,44 +1196,6 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
return dp_video_config; return dp_video_config;
} }
static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
{
struct device_node *dp_phy_node = of_node_get(dp->dev->of_node);
u32 phy_base;
int ret = 0;
dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
if (!dp_phy_node) {
dp->phy = devm_phy_get(dp->dev, "dp");
return PTR_ERR_OR_ZERO(dp->phy);
}
if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
dev_err(dp->dev, "failed to get reg for dptx-phy\n");
ret = -EINVAL;
goto err;
}
if (of_property_read_u32(dp_phy_node, "samsung,enable-mask",
&dp->enable_mask)) {
dev_err(dp->dev, "failed to get enable-mask for dptx-phy\n");
ret = -EINVAL;
goto err;
}
dp->phy_addr = ioremap(phy_base, SZ_4);
if (!dp->phy_addr) {
dev_err(dp->dev, "failed to ioremap dp-phy\n");
ret = -ENOMEM;
goto err;
}
err:
of_node_put(dp_phy_node);
return ret;
}
static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp) static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
{ {
int ret; int ret;
...@@ -1263,10 +1211,10 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp) ...@@ -1263,10 +1211,10 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
static int exynos_dp_bind(struct device *dev, struct device *master, void *data) static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
{ {
struct exynos_dp_device *dp = dev_get_drvdata(dev);
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct resource *res; struct resource *res;
struct exynos_dp_device *dp = exynos_dp_display.ctx;
unsigned int irq_flags; unsigned int irq_flags;
int ret = 0; int ret = 0;
...@@ -1277,9 +1225,21 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) ...@@ -1277,9 +1225,21 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(dp->video_info)) if (IS_ERR(dp->video_info))
return PTR_ERR(dp->video_info); return PTR_ERR(dp->video_info);
ret = exynos_dp_dt_parse_phydata(dp); dp->phy = devm_phy_get(dp->dev, "dp");
if (ret) if (IS_ERR(dp->phy)) {
return ret; dev_err(dp->dev, "no DP phy configured\n");
ret = PTR_ERR(dp->phy);
if (ret) {
/*
* phy itself is not enabled, so we can move forward
* assigning NULL to phy pointer.
*/
if (ret == -ENOSYS || ret == -ENODEV)
dp->phy = NULL;
else
return ret;
}
}
if (!dp->panel) { if (!dp->panel) {
ret = exynos_dp_dt_parse_panel(dp); ret = exynos_dp_dt_parse_panel(dp);
...@@ -1346,17 +1306,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) ...@@ -1346,17 +1306,15 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
dp->drm_dev = drm_dev; dp->drm_dev = drm_dev;
platform_set_drvdata(pdev, &exynos_dp_display); return exynos_drm_create_enc_conn(drm_dev, &dp->display);
return exynos_drm_create_enc_conn(drm_dev, &exynos_dp_display);
} }
static void exynos_dp_unbind(struct device *dev, struct device *master, static void exynos_dp_unbind(struct device *dev, struct device *master,
void *data) void *data)
{ {
struct exynos_drm_display *display = dev_get_drvdata(dev); struct exynos_dp_device *dp = dev_get_drvdata(dev);
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF); exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF);
} }
static const struct component_ops exynos_dp_ops = { static const struct component_ops exynos_dp_ops = {
...@@ -1371,16 +1329,20 @@ static int exynos_dp_probe(struct platform_device *pdev) ...@@ -1371,16 +1329,20 @@ static int exynos_dp_probe(struct platform_device *pdev)
struct exynos_dp_device *dp; struct exynos_dp_device *dp;
int ret; int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
exynos_dp_display.type);
if (ret)
return ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL); GFP_KERNEL);
if (!dp) if (!dp)
return -ENOMEM; return -ENOMEM;
dp->display.type = EXYNOS_DISPLAY_TYPE_LCD;
dp->display.ops = &exynos_dp_display_ops;
platform_set_drvdata(pdev, dp);
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
dp->display.type);
if (ret)
return ret;
panel_node = of_parse_phandle(dev->of_node, "panel", 0); panel_node = of_parse_phandle(dev->of_node, "panel", 0);
if (panel_node) { if (panel_node) {
dp->panel = of_drm_find_panel(panel_node); dp->panel = of_drm_find_panel(panel_node);
...@@ -1389,8 +1351,6 @@ static int exynos_dp_probe(struct platform_device *pdev) ...@@ -1389,8 +1351,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
return -EPROBE_DEFER; return -EPROBE_DEFER;
} }
exynos_dp_display.ctx = dp;
ret = component_add(&pdev->dev, &exynos_dp_ops); ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret) if (ret)
exynos_drm_component_del(&pdev->dev, exynos_drm_component_del(&pdev->dev,
...@@ -1410,19 +1370,17 @@ static int exynos_dp_remove(struct platform_device *pdev) ...@@ -1410,19 +1370,17 @@ static int exynos_dp_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int exynos_dp_suspend(struct device *dev) static int exynos_dp_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct exynos_dp_device *dp = dev_get_drvdata(dev);
struct exynos_drm_display *display = platform_get_drvdata(pdev);
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF); exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_OFF);
return 0; return 0;
} }
static int exynos_dp_resume(struct device *dev) static int exynos_dp_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct exynos_dp_device *dp = dev_get_drvdata(dev);
struct exynos_drm_display *display = platform_get_drvdata(pdev);
exynos_dp_dpms(display, DRM_MODE_DPMS_ON); exynos_dp_dpms(&dp->display, DRM_MODE_DPMS_ON);
return 0; return 0;
} }
#endif #endif
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#define DP_TIMEOUT_LOOP_COUNT 100 #define DP_TIMEOUT_LOOP_COUNT 100
#define MAX_CR_LOOP 5 #define MAX_CR_LOOP 5
#define MAX_EQ_LOOP 5 #define MAX_EQ_LOOP 5
...@@ -145,6 +147,7 @@ struct link_train { ...@@ -145,6 +147,7 @@ struct link_train {
}; };
struct exynos_dp_device { struct exynos_dp_device {
struct exynos_drm_display display;
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct drm_connector connector; struct drm_connector connector;
...@@ -153,8 +156,6 @@ struct exynos_dp_device { ...@@ -153,8 +156,6 @@ struct exynos_dp_device {
struct clk *clock; struct clk *clock;
unsigned int irq; unsigned int irq;
void __iomem *reg_base; void __iomem *reg_base;
void __iomem *phy_addr;
unsigned int enable_mask;
struct video_info *video_info; struct video_info *video_info;
struct link_train link_train; struct link_train link_train;
......
...@@ -15,10 +15,7 @@ ...@@ -15,10 +15,7 @@
#ifndef _EXYNOS_DRM_CRTC_H_ #ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_ #define _EXYNOS_DRM_CRTC_H_
struct drm_device; #include "exynos_drm_drv.h"
struct drm_crtc;
struct exynos_drm_manager;
struct exynos_drm_overlay;
int exynos_drm_crtc_create(struct exynos_drm_manager *manager); int exynos_drm_crtc_create(struct exynos_drm_manager *manager);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
struct exynos_dpi { struct exynos_dpi {
struct exynos_drm_display display;
struct device *dev; struct device *dev;
struct device_node *panel_node; struct device_node *panel_node;
...@@ -35,6 +36,11 @@ struct exynos_dpi { ...@@ -35,6 +36,11 @@ struct exynos_dpi {
#define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector) #define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
static inline struct exynos_dpi *display_to_dpi(struct exynos_drm_display *d)
{
return container_of(d, struct exynos_dpi, display);
}
static enum drm_connector_status static enum drm_connector_status
exynos_dpi_detect(struct drm_connector *connector, bool force) exynos_dpi_detect(struct drm_connector *connector, bool force)
{ {
...@@ -100,7 +106,7 @@ static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { ...@@ -100,7 +106,7 @@ static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
static int exynos_dpi_create_connector(struct exynos_drm_display *display, static int exynos_dpi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct exynos_dpi *ctx = display->ctx; struct exynos_dpi *ctx = display_to_dpi(display);
struct drm_connector *connector = &ctx->connector; struct drm_connector *connector = &ctx->connector;
int ret; int ret;
...@@ -141,7 +147,7 @@ static void exynos_dpi_poweroff(struct exynos_dpi *ctx) ...@@ -141,7 +147,7 @@ static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode) static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
{ {
struct exynos_dpi *ctx = display->ctx; struct exynos_dpi *ctx = display_to_dpi(display);
switch (mode) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
...@@ -165,11 +171,6 @@ static struct exynos_drm_display_ops exynos_dpi_display_ops = { ...@@ -165,11 +171,6 @@ static struct exynos_drm_display_ops exynos_dpi_display_ops = {
.dpms = exynos_dpi_dpms .dpms = exynos_dpi_dpms
}; };
static struct exynos_drm_display exynos_dpi_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dpi_display_ops,
};
/* of_* functions will be removed after merge of of_graph patches */ /* of_* functions will be removed after merge of of_graph patches */
static struct device_node * static struct device_node *
of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg) of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg)
...@@ -299,20 +300,21 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev) ...@@ -299,20 +300,21 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
struct exynos_dpi *ctx; struct exynos_dpi *ctx;
int ret; int ret;
ret = exynos_drm_component_add(dev,
EXYNOS_DEVICE_TYPE_CONNECTOR,
exynos_dpi_display.type);
if (ret)
return ERR_PTR(ret);
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) if (!ctx)
goto err_del_component; return ERR_PTR(-ENOMEM);
ctx->display.type = EXYNOS_DISPLAY_TYPE_LCD;
ctx->display.ops = &exynos_dpi_display_ops;
ctx->dev = dev; ctx->dev = dev;
exynos_dpi_display.ctx = ctx;
ctx->dpms_mode = DRM_MODE_DPMS_OFF; ctx->dpms_mode = DRM_MODE_DPMS_OFF;
ret = exynos_drm_component_add(dev,
EXYNOS_DEVICE_TYPE_CONNECTOR,
ctx->display.type);
if (ret)
return ERR_PTR(ret);
ret = exynos_dpi_parse_dt(ctx); ret = exynos_dpi_parse_dt(ctx);
if (ret < 0) { if (ret < 0) {
devm_kfree(dev, ctx); devm_kfree(dev, ctx);
...@@ -328,7 +330,7 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev) ...@@ -328,7 +330,7 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
} }
} }
return &exynos_dpi_display; return &ctx->display;
err_del_component: err_del_component:
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR); exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
...@@ -336,16 +338,16 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev) ...@@ -336,16 +338,16 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
return NULL; return NULL;
} }
int exynos_dpi_remove(struct device *dev) int exynos_dpi_remove(struct exynos_drm_display *display)
{ {
struct exynos_dpi *ctx = exynos_dpi_display.ctx; struct exynos_dpi *ctx = display_to_dpi(display);
exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF); exynos_dpi_dpms(&ctx->display, DRM_MODE_DPMS_OFF);
if (ctx->panel) if (ctx->panel)
drm_panel_detach(ctx->panel); drm_panel_detach(ctx->panel);
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR); exynos_drm_component_del(ctx->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0; return 0;
} }
...@@ -203,8 +203,6 @@ static int exynos_drm_resume(struct drm_device *dev) ...@@ -203,8 +203,6 @@ static int exynos_drm_resume(struct drm_device *dev)
} }
drm_modeset_unlock_all(dev); drm_modeset_unlock_all(dev);
drm_helper_resume_force_mode(dev);
return 0; return 0;
} }
...@@ -475,8 +473,6 @@ void exynos_drm_component_del(struct device *dev, ...@@ -475,8 +473,6 @@ void exynos_drm_component_del(struct device *dev,
list_del(&cdev->list); list_del(&cdev->list);
kfree(cdev); kfree(cdev);
} }
break;
} }
mutex_unlock(&drm_component_lock); mutex_unlock(&drm_component_lock);
...@@ -495,6 +491,12 @@ static struct component_match *exynos_drm_match_add(struct device *dev) ...@@ -495,6 +491,12 @@ static struct component_match *exynos_drm_match_add(struct device *dev)
mutex_lock(&drm_component_lock); mutex_lock(&drm_component_lock);
/* Do not retry to probe if there is no any kms driver regitered. */
if (list_empty(&drm_component_list)) {
mutex_unlock(&drm_component_lock);
return ERR_PTR(-ENODEV);
}
list_for_each_entry(cdev, &drm_component_list, list) { list_for_each_entry(cdev, &drm_component_list, list) {
/* /*
* Add components to master only in case that crtc and * Add components to master only in case that crtc and
...@@ -550,183 +552,68 @@ static const struct component_master_ops exynos_drm_ops = { ...@@ -550,183 +552,68 @@ static const struct component_master_ops exynos_drm_ops = {
.unbind = exynos_drm_unbind, .unbind = exynos_drm_unbind,
}; };
static int exynos_drm_platform_probe(struct platform_device *pdev) static struct platform_driver *const exynos_drm_kms_drivers[] = {
{
struct component_match *match;
int ret;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
#ifdef CONFIG_DRM_EXYNOS_FIMD #ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver); &fimd_driver,
if (ret < 0)
return ret;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_DP #ifdef CONFIG_DRM_EXYNOS_DP
ret = platform_driver_register(&dp_driver); &dp_driver,
if (ret < 0)
goto err_unregister_fimd_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_DSI #ifdef CONFIG_DRM_EXYNOS_DSI
ret = platform_driver_register(&dsi_driver); &dsi_driver,
if (ret < 0)
goto err_unregister_dp_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_HDMI #ifdef CONFIG_DRM_EXYNOS_HDMI
ret = platform_driver_register(&mixer_driver); &mixer_driver,
if (ret < 0) &hdmi_driver,
goto err_unregister_dsi_drv;
ret = platform_driver_register(&hdmi_driver);
if (ret < 0)
goto err_unregister_mixer_drv;
#endif #endif
};
static struct platform_driver *const exynos_drm_non_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_G2D #ifdef CONFIG_DRM_EXYNOS_G2D
ret = platform_driver_register(&g2d_driver); &g2d_driver,
if (ret < 0)
goto err_unregister_hdmi_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_FIMC #ifdef CONFIG_DRM_EXYNOS_FIMC
ret = platform_driver_register(&fimc_driver); &fimc_driver,
if (ret < 0)
goto err_unregister_g2d_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR #ifdef CONFIG_DRM_EXYNOS_ROTATOR
ret = platform_driver_register(&rotator_driver); &rotator_driver,
if (ret < 0)
goto err_unregister_fimc_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_GSC #ifdef CONFIG_DRM_EXYNOS_GSC
ret = platform_driver_register(&gsc_driver); &gsc_driver,
if (ret < 0)
goto err_unregister_rotator_drv;
#endif #endif
#ifdef CONFIG_DRM_EXYNOS_IPP #ifdef CONFIG_DRM_EXYNOS_IPP
ret = platform_driver_register(&ipp_driver); &ipp_driver,
if (ret < 0)
goto err_unregister_gsc_drv;
ret = exynos_platform_device_ipp_register();
if (ret < 0)
goto err_unregister_ipp_drv;
#endif #endif
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
{
struct component_match *match;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
match = exynos_drm_match_add(&pdev->dev); match = exynos_drm_match_add(&pdev->dev);
if (IS_ERR(match)) { if (IS_ERR(match)) {
ret = PTR_ERR(match); return PTR_ERR(match);
goto err_unregister_resources;
} }
ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops, return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
match); match);
if (ret < 0)
goto err_unregister_resources;
return ret;
err_unregister_resources:
#ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister();
err_unregister_ipp_drv:
platform_driver_unregister(&ipp_driver);
err_unregister_gsc_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_GSC
platform_driver_unregister(&gsc_driver);
err_unregister_rotator_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
platform_driver_unregister(&rotator_driver);
err_unregister_fimc_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMC
platform_driver_unregister(&fimc_driver);
err_unregister_g2d_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_G2D
platform_driver_unregister(&g2d_driver);
err_unregister_hdmi_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&hdmi_driver);
err_unregister_mixer_drv:
platform_driver_unregister(&mixer_driver);
err_unregister_dsi_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
err_unregister_dp_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
err_unregister_fimd_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
return ret;
} }
static int exynos_drm_platform_remove(struct platform_device *pdev) static int exynos_drm_platform_remove(struct platform_device *pdev)
{ {
#ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister();
platform_driver_unregister(&ipp_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_GSC
platform_driver_unregister(&gsc_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
platform_driver_unregister(&rotator_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMC
platform_driver_unregister(&fimc_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_G2D
platform_driver_unregister(&g2d_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&mixer_driver);
platform_driver_unregister(&hdmi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
#endif
component_master_del(&pdev->dev, &exynos_drm_ops); component_master_del(&pdev->dev, &exynos_drm_ops);
return 0; return 0;
} }
static const char * const strings[] = {
"samsung,exynos3",
"samsung,exynos4",
"samsung,exynos5",
};
static struct platform_driver exynos_drm_platform_driver = { static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe, .probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove, .remove = exynos_drm_platform_remove,
...@@ -739,31 +626,75 @@ static struct platform_driver exynos_drm_platform_driver = { ...@@ -739,31 +626,75 @@ static struct platform_driver exynos_drm_platform_driver = {
static int exynos_drm_init(void) static int exynos_drm_init(void)
{ {
int ret; bool is_exynos = false;
int ret, i, j;
/*
* Register device object only in case of Exynos SoC.
*
* Below codes resolves temporarily infinite loop issue incurred
* by Exynos drm driver when using multi-platform kernel.
* So these codes will be replaced with more generic way later.
*/
for (i = 0; i < ARRAY_SIZE(strings); i++) {
if (of_machine_is_compatible(strings[i])) {
is_exynos = true;
break;
}
}
if (!is_exynos)
return -ENODEV;
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1, exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
NULL, 0); NULL, 0);
if (IS_ERR(exynos_drm_pdev)) if (IS_ERR(exynos_drm_pdev))
return PTR_ERR(exynos_drm_pdev); return PTR_ERR(exynos_drm_pdev);
#ifdef CONFIG_DRM_EXYNOS_VIDI
ret = exynos_drm_probe_vidi(); ret = exynos_drm_probe_vidi();
if (ret < 0) if (ret < 0)
goto err_unregister_pd; goto err_unregister_pd;
for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
ret = platform_driver_register(exynos_drm_kms_drivers[i]);
if (ret < 0)
goto err_unregister_kms_drivers;
}
for (j = 0; j < ARRAY_SIZE(exynos_drm_non_kms_drivers); ++j) {
ret = platform_driver_register(exynos_drm_non_kms_drivers[j]);
if (ret < 0)
goto err_unregister_non_kms_drivers;
}
#ifdef CONFIG_DRM_EXYNOS_IPP
ret = exynos_platform_device_ipp_register();
if (ret < 0)
goto err_unregister_non_kms_drivers;
#endif #endif
ret = platform_driver_register(&exynos_drm_platform_driver); ret = platform_driver_register(&exynos_drm_platform_driver);
if (ret) if (ret)
goto err_remove_vidi; goto err_unregister_resources;
return 0; return 0;
err_remove_vidi: err_unregister_resources:
#ifdef CONFIG_DRM_EXYNOS_VIDI #ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister();
#endif
err_unregister_non_kms_drivers:
while (--j >= 0)
platform_driver_unregister(exynos_drm_non_kms_drivers[j]);
err_unregister_kms_drivers:
while (--i >= 0)
platform_driver_unregister(exynos_drm_kms_drivers[i]);
exynos_drm_remove_vidi(); exynos_drm_remove_vidi();
err_unregister_pd: err_unregister_pd:
#endif
platform_device_unregister(exynos_drm_pdev); platform_device_unregister(exynos_drm_pdev);
return ret; return ret;
...@@ -771,10 +702,22 @@ static int exynos_drm_init(void) ...@@ -771,10 +702,22 @@ static int exynos_drm_init(void)
static void exynos_drm_exit(void) static void exynos_drm_exit(void)
{ {
int i;
#ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister();
#endif
for (i = ARRAY_SIZE(exynos_drm_non_kms_drivers) - 1; i >= 0; --i)
platform_driver_unregister(exynos_drm_non_kms_drivers[i]);
for (i = ARRAY_SIZE(exynos_drm_kms_drivers) - 1; i >= 0; --i)
platform_driver_unregister(exynos_drm_kms_drivers[i]);
platform_driver_unregister(&exynos_drm_platform_driver); platform_driver_unregister(&exynos_drm_platform_driver);
#ifdef CONFIG_DRM_EXYNOS_VIDI
exynos_drm_remove_vidi(); exynos_drm_remove_vidi();
#endif
platform_device_unregister(exynos_drm_pdev); platform_device_unregister(exynos_drm_pdev);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#ifndef _EXYNOS_DRM_DRV_H_ #ifndef _EXYNOS_DRM_DRV_H_
#define _EXYNOS_DRM_DRV_H_ #define _EXYNOS_DRM_DRV_H_
#include <drm/drmP.h>
#include <linux/module.h> #include <linux/module.h>
#define MAX_CRTC 3 #define MAX_CRTC 3
...@@ -22,24 +23,6 @@ ...@@ -22,24 +23,6 @@
#define MAX_FB_BUFFER 4 #define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1 #define DEFAULT_ZPOS -1
#define _wait_for(COND, MS) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
int ret__ = 0; \
while (!(COND)) { \
if (time_after(jiffies, timeout__)) { \
ret__ = -ETIMEDOUT; \
break; \
} \
} \
ret__; \
})
#define wait_for(COND, MS) _wait_for(COND, MS)
struct drm_device;
struct exynos_drm_overlay;
struct drm_connector;
/* This enumerates device type. */ /* This enumerates device type. */
enum exynos_drm_device_type { enum exynos_drm_device_type {
EXYNOS_DEVICE_TYPE_NONE, EXYNOS_DEVICE_TYPE_NONE,
...@@ -83,10 +66,10 @@ enum exynos_drm_output_type { ...@@ -83,10 +66,10 @@ enum exynos_drm_output_type {
* @dma_addr: array of bus(accessed by dma) address to the memory region * @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay. * allocated for a overlay.
* @zpos: order of overlay layer(z position). * @zpos: order of overlay layer(z position).
* @default_win: a window to be enabled.
* @color_key: color key on or off.
* @index_color: if using color key feature then this value would be used * @index_color: if using color key feature then this value would be used
* as index color. * as index color.
* @default_win: a window to be enabled.
* @color_key: color key on or off.
* @local_path: in case of lcd type, local path mode on or off. * @local_path: in case of lcd type, local path mode on or off.
* @transparency: transparency on or off. * @transparency: transparency on or off.
* @activated: activated or not. * @activated: activated or not.
...@@ -114,19 +97,20 @@ struct exynos_drm_overlay { ...@@ -114,19 +97,20 @@ struct exynos_drm_overlay {
uint32_t pixel_format; uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER]; dma_addr_t dma_addr[MAX_FB_BUFFER];
int zpos; int zpos;
bool default_win;
bool color_key;
unsigned int index_color; unsigned int index_color;
bool local_path;
bool transparency; bool default_win:1;
bool activated; bool color_key:1;
bool local_path:1;
bool transparency:1;
bool activated:1;
}; };
/* /*
* Exynos DRM Display Structure. * Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel. * - this structure is common to analog tv, digital tv and lcd panel.
* *
* @create_connector: initialize and register a new connector
* @remove: cleans up the display for removal * @remove: cleans up the display for removal
* @mode_fixup: fix mode data comparing to hw specific display mode. * @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and * @mode_set: convert drm_display_mode to hw specific display mode and
...@@ -168,7 +152,6 @@ struct exynos_drm_display { ...@@ -168,7 +152,6 @@ struct exynos_drm_display {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector *connector; struct drm_connector *connector;
struct exynos_drm_display_ops *ops; struct exynos_drm_display_ops *ops;
void *ctx;
}; };
/* /*
...@@ -227,7 +210,6 @@ struct exynos_drm_manager { ...@@ -227,7 +210,6 @@ struct exynos_drm_manager {
struct drm_crtc *crtc; struct drm_crtc *crtc;
int pipe; int pipe;
struct exynos_drm_manager_ops *ops; struct exynos_drm_manager_ops *ops;
void *ctx;
}; };
struct exynos_drm_g2d_private { struct exynos_drm_g2d_private {
...@@ -279,8 +261,6 @@ struct exynos_drm_private { ...@@ -279,8 +261,6 @@ struct exynos_drm_private {
* @dev: pointer to device object for subdrv device driver. * @dev: pointer to device object for subdrv device driver.
* @drm_dev: pointer to drm_device and this pointer would be set * @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register(). * when sub driver calls exynos_drm_subdrv_register().
* @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager.
* @probe: this callback would be called by exynos drm driver after * @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it. * subdrv is registered to it.
* @remove: this callback is used to release resources created * @remove: this callback is used to release resources created
...@@ -312,45 +292,34 @@ int exynos_drm_device_subdrv_remove(struct drm_device *dev); ...@@ -312,45 +292,34 @@ int exynos_drm_device_subdrv_remove(struct drm_device *dev);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file); int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file); void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
/* #ifdef CONFIG_DRM_EXYNOS_IPP
* this function registers exynos drm hdmi platform device. It ensures only one
* instance of the device is created.
*/
int exynos_platform_device_hdmi_register(void);
/*
* this function unregisters exynos drm hdmi platform device if it exists.
*/
void exynos_platform_device_hdmi_unregister(void);
/*
* this function registers exynos drm ipp platform device.
*/
int exynos_platform_device_ipp_register(void); int exynos_platform_device_ipp_register(void);
/*
* this function unregisters exynos drm ipp platform device if it exists.
*/
void exynos_platform_device_ipp_unregister(void); void exynos_platform_device_ipp_unregister(void);
#else
static inline int exynos_platform_device_ipp_register(void) { return 0; }
static inline void exynos_platform_device_ipp_unregister(void) {}
#endif
#ifdef CONFIG_DRM_EXYNOS_DPI #ifdef CONFIG_DRM_EXYNOS_DPI
struct exynos_drm_display * exynos_dpi_probe(struct device *dev); struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
int exynos_dpi_remove(struct device *dev); int exynos_dpi_remove(struct exynos_drm_display *display);
#else #else
static inline struct exynos_drm_display * static inline struct exynos_drm_display *
exynos_dpi_probe(struct device *dev) { return NULL; } exynos_dpi_probe(struct device *dev) { return NULL; }
static inline int exynos_dpi_remove(struct device *dev) { return 0; } static inline int exynos_dpi_remove(struct exynos_drm_display *display)
{
return 0;
}
#endif #endif
/* #ifdef CONFIG_DRM_EXYNOS_VIDI
* this function registers exynos drm vidi platform device/driver.
*/
int exynos_drm_probe_vidi(void); int exynos_drm_probe_vidi(void);
/*
* this function unregister exynos drm vidi platform device/driver.
*/
void exynos_drm_remove_vidi(void); void exynos_drm_remove_vidi(void);
#else
static inline int exynos_drm_probe_vidi(void) { return 0; }
static inline void exynos_drm_remove_vidi(void) {}
#endif
/* This function creates a encoder and a connector, and initializes them. */ /* This function creates a encoder and a connector, and initializes them. */
int exynos_drm_create_enc_conn(struct drm_device *dev, int exynos_drm_create_enc_conn(struct drm_device *dev,
......
...@@ -268,9 +268,9 @@ struct exynos_dsi_driver_data { ...@@ -268,9 +268,9 @@ struct exynos_dsi_driver_data {
}; };
struct exynos_dsi { struct exynos_dsi {
struct exynos_drm_display display;
struct mipi_dsi_host dsi_host; struct mipi_dsi_host dsi_host;
struct drm_connector connector; struct drm_connector connector;
struct drm_encoder *encoder;
struct device_node *panel_node; struct device_node *panel_node;
struct drm_panel *panel; struct drm_panel *panel;
struct device *dev; struct device *dev;
...@@ -304,6 +304,11 @@ struct exynos_dsi { ...@@ -304,6 +304,11 @@ struct exynos_dsi {
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
static inline struct exynos_dsi *display_to_dsi(struct exynos_drm_display *d)
{
return container_of(d, struct exynos_dsi, display);
}
static struct exynos_dsi_driver_data exynos3_dsi_driver_data = { static struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
.plltmr_reg = 0x50, .plltmr_reg = 0x50,
.has_freqband = 1, .has_freqband = 1,
...@@ -316,6 +321,11 @@ static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { ...@@ -316,6 +321,11 @@ static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
.has_clklane_stop = 1, .has_clklane_stop = 1,
}; };
static struct exynos_dsi_driver_data exynos4415_dsi_driver_data = {
.plltmr_reg = 0x58,
.has_clklane_stop = 1,
};
static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
.plltmr_reg = 0x58, .plltmr_reg = 0x58,
}; };
...@@ -325,6 +335,8 @@ static struct of_device_id exynos_dsi_of_match[] = { ...@@ -325,6 +335,8 @@ static struct of_device_id exynos_dsi_of_match[] = {
.data = &exynos3_dsi_driver_data }, .data = &exynos3_dsi_driver_data },
{ .compatible = "samsung,exynos4210-mipi-dsi", { .compatible = "samsung,exynos4210-mipi-dsi",
.data = &exynos4_dsi_driver_data }, .data = &exynos4_dsi_driver_data },
{ .compatible = "samsung,exynos4415-mipi-dsi",
.data = &exynos4415_dsi_driver_data },
{ .compatible = "samsung,exynos5410-mipi-dsi", { .compatible = "samsung,exynos5410-mipi-dsi",
.data = &exynos5_dsi_driver_data }, .data = &exynos5_dsi_driver_data },
{ } { }
...@@ -1104,7 +1116,7 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) ...@@ -1104,7 +1116,7 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id) static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
{ {
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id; struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
struct drm_encoder *encoder = dsi->encoder; struct drm_encoder *encoder = dsi->display.encoder;
if (dsi->state & DSIM_STATE_ENABLED) if (dsi->state & DSIM_STATE_ENABLED)
exynos_drm_crtc_te_handler(encoder->crtc); exynos_drm_crtc_te_handler(encoder->crtc);
...@@ -1143,6 +1155,7 @@ static int exynos_dsi_init(struct exynos_dsi *dsi) ...@@ -1143,6 +1155,7 @@ static int exynos_dsi_init(struct exynos_dsi *dsi)
static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi) static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
{ {
int ret; int ret;
int te_gpio_irq;
dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0); dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
if (!gpio_is_valid(dsi->te_gpio)) { if (!gpio_is_valid(dsi->te_gpio)) {
...@@ -1157,14 +1170,10 @@ static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi) ...@@ -1157,14 +1170,10 @@ static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
goto out; goto out;
} }
/* te_gpio_irq = gpio_to_irq(dsi->te_gpio);
* This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel
* calls drm_panel_init() first then calls mipi_dsi_attach() in probe(). irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
* It means that te_gpio is invalid when exynos_dsi_enable_irq() is ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
* called by drm_panel_init() before panel is attached.
*/
ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio),
exynos_dsi_te_irq_handler, NULL,
IRQF_TRIGGER_RISING, "TE", dsi); IRQF_TRIGGER_RISING, "TE", dsi);
if (ret) { if (ret) {
dev_err(dsi->dev, "request interrupt failed with %d\n", ret); dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
...@@ -1195,9 +1204,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, ...@@ -1195,9 +1204,6 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
dsi->mode_flags = device->mode_flags; dsi->mode_flags = device->mode_flags;
dsi->panel_node = device->dev.of_node; dsi->panel_node = device->dev.of_node;
if (dsi->connector.dev)
drm_helper_hpd_irq_event(dsi->connector.dev);
/* /*
* This is a temporary solution and should be made by more generic way. * This is a temporary solution and should be made by more generic way.
* *
...@@ -1211,6 +1217,9 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, ...@@ -1211,6 +1217,9 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
return ret; return ret;
} }
if (dsi->connector.dev)
drm_helper_hpd_irq_event(dsi->connector.dev);
return 0; return 0;
} }
...@@ -1369,16 +1378,17 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi) ...@@ -1369,16 +1378,17 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
exynos_dsi_set_display_mode(dsi); exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true); exynos_dsi_set_display_enable(dsi, true);
dsi->state |= DSIM_STATE_ENABLED;
ret = drm_panel_enable(dsi->panel); ret = drm_panel_enable(dsi->panel);
if (ret < 0) { if (ret < 0) {
dsi->state &= ~DSIM_STATE_ENABLED;
exynos_dsi_set_display_enable(dsi, false); exynos_dsi_set_display_enable(dsi, false);
drm_panel_unprepare(dsi->panel); drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi); exynos_dsi_poweroff(dsi);
return ret; return ret;
} }
dsi->state |= DSIM_STATE_ENABLED;
return 0; return 0;
} }
...@@ -1397,7 +1407,7 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi) ...@@ -1397,7 +1407,7 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode) static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode)
{ {
struct exynos_dsi *dsi = display->ctx; struct exynos_dsi *dsi = display_to_dsi(display);
if (dsi->panel) { if (dsi->panel) {
switch (mode) { switch (mode) {
...@@ -1474,7 +1484,7 @@ exynos_dsi_best_encoder(struct drm_connector *connector) ...@@ -1474,7 +1484,7 @@ exynos_dsi_best_encoder(struct drm_connector *connector)
{ {
struct exynos_dsi *dsi = connector_to_dsi(connector); struct exynos_dsi *dsi = connector_to_dsi(connector);
return dsi->encoder; return dsi->display.encoder;
} }
static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
...@@ -1486,12 +1496,10 @@ static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { ...@@ -1486,12 +1496,10 @@ static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
static int exynos_dsi_create_connector(struct exynos_drm_display *display, static int exynos_dsi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct exynos_dsi *dsi = display->ctx; struct exynos_dsi *dsi = display_to_dsi(display);
struct drm_connector *connector = &dsi->connector; struct drm_connector *connector = &dsi->connector;
int ret; int ret;
dsi->encoder = encoder;
connector->polled = DRM_CONNECTOR_POLL_HPD; connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector, ret = drm_connector_init(encoder->dev, connector,
...@@ -1512,7 +1520,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display, ...@@ -1512,7 +1520,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,
static void exynos_dsi_mode_set(struct exynos_drm_display *display, static void exynos_dsi_mode_set(struct exynos_drm_display *display,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct exynos_dsi *dsi = display->ctx; struct exynos_dsi *dsi = display_to_dsi(display);
struct videomode *vm = &dsi->vm; struct videomode *vm = &dsi->vm;
vm->hactive = mode->hdisplay; vm->hactive = mode->hdisplay;
...@@ -1531,10 +1539,6 @@ static struct exynos_drm_display_ops exynos_dsi_display_ops = { ...@@ -1531,10 +1539,6 @@ static struct exynos_drm_display_ops exynos_dsi_display_ops = {
.dpms = exynos_dsi_dpms .dpms = exynos_dsi_dpms
}; };
static struct exynos_drm_display exynos_dsi_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dsi_display_ops,
};
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match); MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
/* of_* functions will be removed after merge of of_graph patches */ /* of_* functions will be removed after merge of of_graph patches */
...@@ -1640,28 +1644,28 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) ...@@ -1640,28 +1644,28 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
static int exynos_dsi_bind(struct device *dev, struct device *master, static int exynos_dsi_bind(struct device *dev, struct device *master,
void *data) void *data)
{ {
struct exynos_drm_display *display = dev_get_drvdata(dev);
struct exynos_dsi *dsi = display_to_dsi(display);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct exynos_dsi *dsi;
int ret; int ret;
ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display); ret = exynos_drm_create_enc_conn(drm_dev, display);
if (ret) { if (ret) {
DRM_ERROR("Encoder create [%d] failed with %d\n", DRM_ERROR("Encoder create [%d] failed with %d\n",
exynos_dsi_display.type, ret); display->type, ret);
return ret; return ret;
} }
dsi = exynos_dsi_display.ctx;
return mipi_dsi_host_register(&dsi->dsi_host); return mipi_dsi_host_register(&dsi->dsi_host);
} }
static void exynos_dsi_unbind(struct device *dev, struct device *master, static void exynos_dsi_unbind(struct device *dev, struct device *master,
void *data) void *data)
{ {
struct exynos_dsi *dsi = exynos_dsi_display.ctx; struct exynos_drm_display *display = dev_get_drvdata(dev);
struct exynos_dsi *dsi = display_to_dsi(display);
exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF); exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF);
mipi_dsi_host_unregister(&dsi->dsi_host); mipi_dsi_host_unregister(&dsi->dsi_host);
} }
...@@ -1673,22 +1677,23 @@ static const struct component_ops exynos_dsi_component_ops = { ...@@ -1673,22 +1677,23 @@ static const struct component_ops exynos_dsi_component_ops = {
static int exynos_dsi_probe(struct platform_device *pdev) static int exynos_dsi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct resource *res; struct resource *res;
struct exynos_dsi *dsi; struct exynos_dsi *dsi;
int ret; int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR, dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
exynos_dsi_display.type); if (!dsi)
return -ENOMEM;
dsi->display.type = EXYNOS_DISPLAY_TYPE_LCD;
dsi->display.ops = &exynos_dsi_display_ops;
ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
dsi->display.type);
if (ret) if (ret)
return ret; return ret;
dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi) {
dev_err(&pdev->dev, "failed to allocate dsi object.\n");
ret = -ENOMEM;
goto err_del_component;
}
/* To be checked as invalid one */ /* To be checked as invalid one */
dsi->te_gpio = -ENOENT; dsi->te_gpio = -ENOENT;
...@@ -1697,9 +1702,9 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1697,9 +1702,9 @@ static int exynos_dsi_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&dsi->transfer_list); INIT_LIST_HEAD(&dsi->transfer_list);
dsi->dsi_host.ops = &exynos_dsi_ops; dsi->dsi_host.ops = &exynos_dsi_ops;
dsi->dsi_host.dev = &pdev->dev; dsi->dsi_host.dev = dev;
dsi->dev = &pdev->dev; dsi->dev = dev;
dsi->driver_data = exynos_dsi_get_driver_data(pdev); dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi); ret = exynos_dsi_parse_dt(dsi);
...@@ -1708,70 +1713,68 @@ static int exynos_dsi_probe(struct platform_device *pdev) ...@@ -1708,70 +1713,68 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->supplies[0].supply = "vddcore"; dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio"; dsi->supplies[1].supply = "vddio";
ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(dsi->supplies), ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
dsi->supplies); dsi->supplies);
if (ret) { if (ret) {
dev_info(&pdev->dev, "failed to get regulators: %d\n", ret); dev_info(dev, "failed to get regulators: %d\n", ret);
return -EPROBE_DEFER; return -EPROBE_DEFER;
} }
dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk"); dsi->pll_clk = devm_clk_get(dev, "pll_clk");
if (IS_ERR(dsi->pll_clk)) { if (IS_ERR(dsi->pll_clk)) {
dev_info(&pdev->dev, "failed to get dsi pll input clock\n"); dev_info(dev, "failed to get dsi pll input clock\n");
ret = PTR_ERR(dsi->pll_clk); ret = PTR_ERR(dsi->pll_clk);
goto err_del_component; goto err_del_component;
} }
dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk"); dsi->bus_clk = devm_clk_get(dev, "bus_clk");
if (IS_ERR(dsi->bus_clk)) { if (IS_ERR(dsi->bus_clk)) {
dev_info(&pdev->dev, "failed to get dsi bus clock\n"); dev_info(dev, "failed to get dsi bus clock\n");
ret = PTR_ERR(dsi->bus_clk); ret = PTR_ERR(dsi->bus_clk);
goto err_del_component; goto err_del_component;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->reg_base = devm_ioremap_resource(&pdev->dev, res); dsi->reg_base = devm_ioremap_resource(dev, res);
if (IS_ERR(dsi->reg_base)) { if (IS_ERR(dsi->reg_base)) {
dev_err(&pdev->dev, "failed to remap io region\n"); dev_err(dev, "failed to remap io region\n");
ret = PTR_ERR(dsi->reg_base); ret = PTR_ERR(dsi->reg_base);
goto err_del_component; goto err_del_component;
} }
dsi->phy = devm_phy_get(&pdev->dev, "dsim"); dsi->phy = devm_phy_get(dev, "dsim");
if (IS_ERR(dsi->phy)) { if (IS_ERR(dsi->phy)) {
dev_info(&pdev->dev, "failed to get dsim phy\n"); dev_info(dev, "failed to get dsim phy\n");
ret = PTR_ERR(dsi->phy); ret = PTR_ERR(dsi->phy);
goto err_del_component; goto err_del_component;
} }
dsi->irq = platform_get_irq(pdev, 0); dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) { if (dsi->irq < 0) {
dev_err(&pdev->dev, "failed to request dsi irq resource\n"); dev_err(dev, "failed to request dsi irq resource\n");
ret = dsi->irq; ret = dsi->irq;
goto err_del_component; goto err_del_component;
} }
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN); irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL, ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
exynos_dsi_irq, IRQF_ONESHOT, exynos_dsi_irq, IRQF_ONESHOT,
dev_name(&pdev->dev), dsi); dev_name(dev), dsi);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to request dsi irq\n"); dev_err(dev, "failed to request dsi irq\n");
goto err_del_component; goto err_del_component;
} }
exynos_dsi_display.ctx = dsi; platform_set_drvdata(pdev, &dsi->display);
platform_set_drvdata(pdev, &exynos_dsi_display);
ret = component_add(&pdev->dev, &exynos_dsi_component_ops); ret = component_add(dev, &exynos_dsi_component_ops);
if (ret) if (ret)
goto err_del_component; goto err_del_component;
return ret; return ret;
err_del_component: err_del_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return ret; return ret;
} }
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#ifndef _EXYNOS_DRM_ENCODER_H_ #ifndef _EXYNOS_DRM_ENCODER_H_
#define _EXYNOS_DRM_ENCODER_H_ #define _EXYNOS_DRM_ENCODER_H_
struct exynos_drm_manager;
void exynos_drm_encoder_setup(struct drm_device *dev); void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_display *mgr, struct exynos_drm_display *mgr,
......
...@@ -84,8 +84,6 @@ ...@@ -84,8 +84,6 @@
/* FIMD has totally five hardware windows. */ /* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5 #define WINDOWS_NR 5
#define get_fimd_manager(mgr) platform_get_drvdata(to_platform_device(dev))
struct fimd_driver_data { struct fimd_driver_data {
unsigned int timing_base; unsigned int timing_base;
unsigned int lcdblk_offset; unsigned int lcdblk_offset;
...@@ -96,6 +94,7 @@ struct fimd_driver_data { ...@@ -96,6 +94,7 @@ struct fimd_driver_data {
unsigned int has_clksel:1; unsigned int has_clksel:1;
unsigned int has_limited_fmt:1; unsigned int has_limited_fmt:1;
unsigned int has_vidoutcon:1; unsigned int has_vidoutcon:1;
unsigned int has_vtsel:1;
}; };
static struct fimd_driver_data s3c64xx_fimd_driver_data = { static struct fimd_driver_data s3c64xx_fimd_driver_data = {
...@@ -118,6 +117,17 @@ static struct fimd_driver_data exynos4_fimd_driver_data = { ...@@ -118,6 +117,17 @@ static struct fimd_driver_data exynos4_fimd_driver_data = {
.lcdblk_vt_shift = 10, .lcdblk_vt_shift = 10,
.lcdblk_bypass_shift = 1, .lcdblk_bypass_shift = 1,
.has_shadowcon = 1, .has_shadowcon = 1,
.has_vtsel = 1,
};
static struct fimd_driver_data exynos4415_fimd_driver_data = {
.timing_base = 0x20000,
.lcdblk_offset = 0x210,
.lcdblk_vt_shift = 10,
.lcdblk_bypass_shift = 1,
.has_shadowcon = 1,
.has_vidoutcon = 1,
.has_vtsel = 1,
}; };
static struct fimd_driver_data exynos5_fimd_driver_data = { static struct fimd_driver_data exynos5_fimd_driver_data = {
...@@ -127,6 +137,7 @@ static struct fimd_driver_data exynos5_fimd_driver_data = { ...@@ -127,6 +137,7 @@ static struct fimd_driver_data exynos5_fimd_driver_data = {
.lcdblk_bypass_shift = 15, .lcdblk_bypass_shift = 15,
.has_shadowcon = 1, .has_shadowcon = 1,
.has_vidoutcon = 1, .has_vidoutcon = 1,
.has_vtsel = 1,
}; };
struct fimd_win_data { struct fimd_win_data {
...@@ -146,6 +157,7 @@ struct fimd_win_data { ...@@ -146,6 +157,7 @@ struct fimd_win_data {
}; };
struct fimd_context { struct fimd_context {
struct exynos_drm_manager manager;
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct clk *bus_clk; struct clk *bus_clk;
...@@ -173,6 +185,11 @@ struct fimd_context { ...@@ -173,6 +185,11 @@ struct fimd_context {
struct exynos_drm_display *display; struct exynos_drm_display *display;
}; };
static inline struct fimd_context *mgr_to_fimd(struct exynos_drm_manager *mgr)
{
return container_of(mgr, struct fimd_context, manager);
}
static const struct of_device_id fimd_driver_dt_match[] = { static const struct of_device_id fimd_driver_dt_match[] = {
{ .compatible = "samsung,s3c6400-fimd", { .compatible = "samsung,s3c6400-fimd",
.data = &s3c64xx_fimd_driver_data }, .data = &s3c64xx_fimd_driver_data },
...@@ -180,6 +197,8 @@ static const struct of_device_id fimd_driver_dt_match[] = { ...@@ -180,6 +197,8 @@ static const struct of_device_id fimd_driver_dt_match[] = {
.data = &exynos3_fimd_driver_data }, .data = &exynos3_fimd_driver_data },
{ .compatible = "samsung,exynos4210-fimd", { .compatible = "samsung,exynos4210-fimd",
.data = &exynos4_fimd_driver_data }, .data = &exynos4_fimd_driver_data },
{ .compatible = "samsung,exynos4415-fimd",
.data = &exynos4415_fimd_driver_data },
{ .compatible = "samsung,exynos5250-fimd", { .compatible = "samsung,exynos5250-fimd",
.data = &exynos5_fimd_driver_data }, .data = &exynos5_fimd_driver_data },
{}, {},
...@@ -197,7 +216,7 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data( ...@@ -197,7 +216,7 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
if (ctx->suspended) if (ctx->suspended)
return; return;
...@@ -214,9 +233,35 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) ...@@ -214,9 +233,35 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
DRM_DEBUG_KMS("vblank wait timed out.\n"); DRM_DEBUG_KMS("vblank wait timed out.\n");
} }
static void fimd_enable_video_output(struct fimd_context *ctx, int win,
bool enable)
{
u32 val = readl(ctx->regs + WINCON(win));
if (enable)
val |= WINCONx_ENWIN;
else
val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win));
}
static void fimd_enable_shadow_channel_path(struct fimd_context *ctx, int win,
bool enable)
{
u32 val = readl(ctx->regs + SHADOWCON);
if (enable)
val |= SHADOWCON_CHx_ENABLE(win);
else
val &= ~SHADOWCON_CHx_ENABLE(win);
writel(val, ctx->regs + SHADOWCON);
}
static void fimd_clear_channel(struct exynos_drm_manager *mgr) static void fimd_clear_channel(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
int win, ch_enabled = 0; int win, ch_enabled = 0;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
...@@ -226,16 +271,12 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr) ...@@ -226,16 +271,12 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr)
u32 val = readl(ctx->regs + WINCON(win)); u32 val = readl(ctx->regs + WINCON(win));
if (val & WINCONx_ENWIN) { if (val & WINCONx_ENWIN) {
/* wincon */ fimd_enable_video_output(ctx, win, false);
val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win)); if (ctx->driver_data->has_shadowcon)
fimd_enable_shadow_channel_path(ctx, win,
/* unprotect windows */ false);
if (ctx->driver_data->has_shadowcon) {
val = readl(ctx->regs + SHADOWCON);
val &= ~SHADOWCON_CHx_ENABLE(win);
writel(val, ctx->regs + SHADOWCON);
}
ch_enabled = 1; ch_enabled = 1;
} }
} }
...@@ -253,7 +294,7 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr) ...@@ -253,7 +294,7 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr)
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev) struct drm_device *drm_dev)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct exynos_drm_private *priv; struct exynos_drm_private *priv;
priv = drm_dev->dev_private; priv = drm_dev->dev_private;
...@@ -275,7 +316,7 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, ...@@ -275,7 +316,7 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
static void fimd_mgr_remove(struct exynos_drm_manager *mgr) static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
/* detach this sub driver from iommu mapping if supported. */ /* detach this sub driver from iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev)) if (is_drm_iommu_supported(ctx->drm_dev))
...@@ -315,14 +356,14 @@ static bool fimd_mode_fixup(struct exynos_drm_manager *mgr, ...@@ -315,14 +356,14 @@ static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
static void fimd_mode_set(struct exynos_drm_manager *mgr, static void fimd_mode_set(struct exynos_drm_manager *mgr,
const struct drm_display_mode *in_mode) const struct drm_display_mode *in_mode)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
drm_mode_copy(&ctx->mode, in_mode); drm_mode_copy(&ctx->mode, in_mode);
} }
static void fimd_commit(struct exynos_drm_manager *mgr) static void fimd_commit(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct drm_display_mode *mode = &ctx->mode; struct drm_display_mode *mode = &ctx->mode;
struct fimd_driver_data *driver_data = ctx->driver_data; struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base; void *timing_base = ctx->regs + driver_data->timing_base;
...@@ -343,7 +384,8 @@ static void fimd_commit(struct exynos_drm_manager *mgr) ...@@ -343,7 +384,8 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
writel(0, timing_base + I80IFCONFBx(0)); writel(0, timing_base + I80IFCONFBx(0));
/* set video type selection to I80 interface */ /* set video type selection to I80 interface */
if (ctx->sysreg && regmap_update_bits(ctx->sysreg, if (driver_data->has_vtsel && ctx->sysreg &&
regmap_update_bits(ctx->sysreg,
driver_data->lcdblk_offset, driver_data->lcdblk_offset,
0x3 << driver_data->lcdblk_vt_shift, 0x3 << driver_data->lcdblk_vt_shift,
0x1 << driver_data->lcdblk_vt_shift)) { 0x1 << driver_data->lcdblk_vt_shift)) {
...@@ -421,7 +463,7 @@ static void fimd_commit(struct exynos_drm_manager *mgr) ...@@ -421,7 +463,7 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
static int fimd_enable_vblank(struct exynos_drm_manager *mgr) static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
u32 val; u32 val;
if (ctx->suspended) if (ctx->suspended)
...@@ -431,12 +473,19 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr) ...@@ -431,12 +473,19 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
val = readl(ctx->regs + VIDINTCON0); val = readl(ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE; val |= VIDINTCON0_INT_ENABLE;
val |= VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_FRAMESEL0_MASK; if (ctx->i80_if) {
val |= VIDINTCON0_FRAMESEL0_VSYNC; val |= VIDINTCON0_INT_I80IFDONE;
val &= ~VIDINTCON0_FRAMESEL1_MASK; val |= VIDINTCON0_INT_SYSMAINCON;
val |= VIDINTCON0_FRAMESEL1_NONE; val &= ~VIDINTCON0_INT_SYSSUBCON;
} else {
val |= VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_FRAMESEL0_MASK;
val |= VIDINTCON0_FRAMESEL0_VSYNC;
val &= ~VIDINTCON0_FRAMESEL1_MASK;
val |= VIDINTCON0_FRAMESEL1_NONE;
}
writel(val, ctx->regs + VIDINTCON0); writel(val, ctx->regs + VIDINTCON0);
} }
...@@ -446,7 +495,7 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr) ...@@ -446,7 +495,7 @@ static int fimd_enable_vblank(struct exynos_drm_manager *mgr)
static void fimd_disable_vblank(struct exynos_drm_manager *mgr) static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
u32 val; u32 val;
if (ctx->suspended) if (ctx->suspended)
...@@ -455,9 +504,15 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr) ...@@ -455,9 +504,15 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
if (test_and_clear_bit(0, &ctx->irq_flags)) { if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0); val = readl(ctx->regs + VIDINTCON0);
val &= ~VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_INT_ENABLE; val &= ~VIDINTCON0_INT_ENABLE;
if (ctx->i80_if) {
val &= ~VIDINTCON0_INT_I80IFDONE;
val &= ~VIDINTCON0_INT_SYSMAINCON;
val &= ~VIDINTCON0_INT_SYSSUBCON;
} else
val &= ~VIDINTCON0_INT_FRAME;
writel(val, ctx->regs + VIDINTCON0); writel(val, ctx->regs + VIDINTCON0);
} }
} }
...@@ -465,7 +520,7 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr) ...@@ -465,7 +520,7 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
static void fimd_win_mode_set(struct exynos_drm_manager *mgr, static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay) struct exynos_drm_overlay *overlay)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int win; int win;
unsigned long offset; unsigned long offset;
...@@ -623,7 +678,7 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx, ...@@ -623,7 +678,7 @@ static void fimd_shadow_protect_win(struct fimd_context *ctx,
static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int win = zpos; int win = zpos;
unsigned long val, alpha, size; unsigned long val, alpha, size;
...@@ -730,20 +785,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) ...@@ -730,20 +785,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
if (win != 0) if (win != 0)
fimd_win_set_colkey(ctx, win); fimd_win_set_colkey(ctx, win);
/* wincon */ fimd_enable_video_output(ctx, win, true);
val = readl(ctx->regs + WINCON(win));
val |= WINCONx_ENWIN; if (ctx->driver_data->has_shadowcon)
writel(val, ctx->regs + WINCON(win)); fimd_enable_shadow_channel_path(ctx, win, true);
/* Enable DMA channel and unprotect windows */ /* Enable DMA channel and unprotect windows */
fimd_shadow_protect_win(ctx, win, false); fimd_shadow_protect_win(ctx, win, false);
if (ctx->driver_data->has_shadowcon) {
val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_CHx_ENABLE(win);
writel(val, ctx->regs + SHADOWCON);
}
win_data->enabled = true; win_data->enabled = true;
if (ctx->i80_if) if (ctx->i80_if)
...@@ -752,10 +801,9 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) ...@@ -752,10 +801,9 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int win = zpos; int win = zpos;
u32 val;
if (win == DEFAULT_ZPOS) if (win == DEFAULT_ZPOS)
win = ctx->default_win; win = ctx->default_win;
...@@ -774,18 +822,12 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) ...@@ -774,18 +822,12 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
/* protect windows */ /* protect windows */
fimd_shadow_protect_win(ctx, win, true); fimd_shadow_protect_win(ctx, win, true);
/* wincon */ fimd_enable_video_output(ctx, win, false);
val = readl(ctx->regs + WINCON(win));
val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win));
/* unprotect windows */ if (ctx->driver_data->has_shadowcon)
if (ctx->driver_data->has_shadowcon) { fimd_enable_shadow_channel_path(ctx, win, false);
val = readl(ctx->regs + SHADOWCON);
val &= ~SHADOWCON_CHx_ENABLE(win);
writel(val, ctx->regs + SHADOWCON);
}
/* unprotect windows */
fimd_shadow_protect_win(ctx, win, false); fimd_shadow_protect_win(ctx, win, false);
win_data->enabled = false; win_data->enabled = false;
...@@ -793,7 +835,7 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) ...@@ -793,7 +835,7 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
static void fimd_window_suspend(struct exynos_drm_manager *mgr) static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int i; int i;
...@@ -803,12 +845,11 @@ static void fimd_window_suspend(struct exynos_drm_manager *mgr) ...@@ -803,12 +845,11 @@ static void fimd_window_suspend(struct exynos_drm_manager *mgr)
if (win_data->enabled) if (win_data->enabled)
fimd_win_disable(mgr, i); fimd_win_disable(mgr, i);
} }
fimd_wait_for_vblank(mgr);
} }
static void fimd_window_resume(struct exynos_drm_manager *mgr) static void fimd_window_resume(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int i; int i;
...@@ -821,7 +862,7 @@ static void fimd_window_resume(struct exynos_drm_manager *mgr) ...@@ -821,7 +862,7 @@ static void fimd_window_resume(struct exynos_drm_manager *mgr)
static void fimd_apply(struct exynos_drm_manager *mgr) static void fimd_apply(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
struct fimd_win_data *win_data; struct fimd_win_data *win_data;
int i; int i;
...@@ -838,7 +879,7 @@ static void fimd_apply(struct exynos_drm_manager *mgr) ...@@ -838,7 +879,7 @@ static void fimd_apply(struct exynos_drm_manager *mgr)
static int fimd_poweron(struct exynos_drm_manager *mgr) static int fimd_poweron(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
int ret; int ret;
if (!ctx->suspended) if (!ctx->suspended)
...@@ -886,7 +927,7 @@ static int fimd_poweron(struct exynos_drm_manager *mgr) ...@@ -886,7 +927,7 @@ static int fimd_poweron(struct exynos_drm_manager *mgr)
static int fimd_poweroff(struct exynos_drm_manager *mgr) static int fimd_poweroff(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
if (ctx->suspended) if (ctx->suspended)
return 0; return 0;
...@@ -928,39 +969,41 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) ...@@ -928,39 +969,41 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
static void fimd_trigger(struct device *dev) static void fimd_trigger(struct device *dev)
{ {
struct exynos_drm_manager *mgr = get_fimd_manager(dev); struct fimd_context *ctx = dev_get_drvdata(dev);
struct fimd_context *ctx = mgr->ctx;
struct fimd_driver_data *driver_data = ctx->driver_data; struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base; void *timing_base = ctx->regs + driver_data->timing_base;
u32 reg; u32 reg;
atomic_set(&ctx->triggering, 1); /*
* Skips triggering if in triggering state, because multiple triggering
* requests can cause panel reset.
*/
if (atomic_read(&ctx->triggering))
return;
reg = readl(ctx->regs + VIDINTCON0); /* Enters triggering mode */
reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE | atomic_set(&ctx->triggering, 1);
VIDINTCON0_INT_SYSMAINCON);
writel(reg, ctx->regs + VIDINTCON0);
reg = readl(timing_base + TRIGCON); reg = readl(timing_base + TRIGCON);
reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE); reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
writel(reg, timing_base + TRIGCON); writel(reg, timing_base + TRIGCON);
/*
* Exits triggering mode if vblank is not enabled yet, because when the
* VIDINTCON0 register is not set, it can not exit from triggering mode.
*/
if (!test_bit(0, &ctx->irq_flags))
atomic_set(&ctx->triggering, 0);
} }
static void fimd_te_handler(struct exynos_drm_manager *mgr) static void fimd_te_handler(struct exynos_drm_manager *mgr)
{ {
struct fimd_context *ctx = mgr->ctx; struct fimd_context *ctx = mgr_to_fimd(mgr);
/* Checks the crtc is detached already from encoder */ /* Checks the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev) if (ctx->pipe < 0 || !ctx->drm_dev)
return; return;
/*
* Skips to trigger if in triggering state, because multiple triggering
* requests can cause panel reset.
*/
if (atomic_read(&ctx->triggering))
return;
/* /*
* If there is a page flip request, triggers and handles the page flip * If there is a page flip request, triggers and handles the page flip
* event so that current fb can be updated into panel GRAM. * event so that current fb can be updated into panel GRAM.
...@@ -972,10 +1015,10 @@ static void fimd_te_handler(struct exynos_drm_manager *mgr) ...@@ -972,10 +1015,10 @@ static void fimd_te_handler(struct exynos_drm_manager *mgr)
if (atomic_read(&ctx->wait_vsync_event)) { if (atomic_read(&ctx->wait_vsync_event)) {
atomic_set(&ctx->wait_vsync_event, 0); atomic_set(&ctx->wait_vsync_event, 0);
wake_up(&ctx->wait_vsync_queue); wake_up(&ctx->wait_vsync_queue);
if (!atomic_read(&ctx->triggering))
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
} }
if (test_bit(0, &ctx->irq_flags))
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
} }
static struct exynos_drm_manager_ops fimd_manager_ops = { static struct exynos_drm_manager_ops fimd_manager_ops = {
...@@ -992,11 +1035,6 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { ...@@ -992,11 +1035,6 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
.te_handler = fimd_te_handler, .te_handler = fimd_te_handler,
}; };
static struct exynos_drm_manager fimd_manager = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &fimd_manager_ops,
};
static irqreturn_t fimd_irq_handler(int irq, void *dev_id) static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{ {
struct fimd_context *ctx = (struct fimd_context *)dev_id; struct fimd_context *ctx = (struct fimd_context *)dev_id;
...@@ -1013,16 +1051,10 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) ...@@ -1013,16 +1051,10 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
goto out; goto out;
if (ctx->i80_if) { if (ctx->i80_if) {
/* unset I80 frame done interrupt */ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
val = readl(ctx->regs + VIDINTCON0);
val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
writel(val, ctx->regs + VIDINTCON0);
/* exit triggering mode */ /* Exits triggering mode */
atomic_set(&ctx->triggering, 0); atomic_set(&ctx->triggering, 0);
drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
} else { } else {
drm_handle_vblank(ctx->drm_dev, ctx->pipe); drm_handle_vblank(ctx->drm_dev, ctx->pipe);
exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
...@@ -1040,11 +1072,11 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) ...@@ -1040,11 +1072,11 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
static int fimd_bind(struct device *dev, struct device *master, void *data) static int fimd_bind(struct device *dev, struct device *master, void *data)
{ {
struct fimd_context *ctx = fimd_manager.ctx; struct fimd_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
fimd_mgr_initialize(&fimd_manager, drm_dev); fimd_mgr_initialize(&ctx->manager, drm_dev);
exynos_drm_crtc_create(&fimd_manager); exynos_drm_crtc_create(&ctx->manager);
if (ctx->display) if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display); exynos_drm_create_enc_conn(drm_dev, ctx->display);
...@@ -1055,15 +1087,14 @@ static int fimd_bind(struct device *dev, struct device *master, void *data) ...@@ -1055,15 +1087,14 @@ static int fimd_bind(struct device *dev, struct device *master, void *data)
static void fimd_unbind(struct device *dev, struct device *master, static void fimd_unbind(struct device *dev, struct device *master,
void *data) void *data)
{ {
struct exynos_drm_manager *mgr = dev_get_drvdata(dev); struct fimd_context *ctx = dev_get_drvdata(dev);
struct fimd_context *ctx = fimd_manager.ctx;
fimd_dpms(mgr, DRM_MODE_DPMS_OFF); fimd_dpms(&ctx->manager, DRM_MODE_DPMS_OFF);
if (ctx->display) if (ctx->display)
exynos_dpi_remove(dev); exynos_dpi_remove(ctx->display);
fimd_mgr_remove(mgr); fimd_mgr_remove(&ctx->manager);
} }
static const struct component_ops fimd_component_ops = { static const struct component_ops fimd_component_ops = {
...@@ -1079,21 +1110,20 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -1079,21 +1110,20 @@ static int fimd_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
int ret = -EINVAL; int ret = -EINVAL;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC, if (!dev->of_node)
fimd_manager.type); return -ENODEV;
if (ret)
return ret;
if (!dev->of_node) {
ret = -ENODEV;
goto err_del_component;
}
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) { if (!ctx)
ret = -ENOMEM; return -ENOMEM;
goto err_del_component;
} ctx->manager.type = EXYNOS_DISPLAY_TYPE_LCD;
ctx->manager.ops = &fimd_manager_ops;
ret = exynos_drm_component_add(dev, EXYNOS_DEVICE_TYPE_CRTC,
ctx->manager.type);
if (ret)
return ret;
ctx->dev = dev; ctx->dev = dev;
ctx->suspended = true; ctx->suspended = true;
...@@ -1182,27 +1212,27 @@ static int fimd_probe(struct platform_device *pdev) ...@@ -1182,27 +1212,27 @@ static int fimd_probe(struct platform_device *pdev)
init_waitqueue_head(&ctx->wait_vsync_queue); init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0); atomic_set(&ctx->wait_vsync_event, 0);
platform_set_drvdata(pdev, &fimd_manager); platform_set_drvdata(pdev, ctx);
fimd_manager.ctx = ctx;
ctx->display = exynos_dpi_probe(dev); ctx->display = exynos_dpi_probe(dev);
if (IS_ERR(ctx->display)) if (IS_ERR(ctx->display)) {
return PTR_ERR(ctx->display); ret = PTR_ERR(ctx->display);
goto err_del_component;
}
pm_runtime_enable(&pdev->dev); pm_runtime_enable(dev);
ret = component_add(&pdev->dev, &fimd_component_ops); ret = component_add(dev, &fimd_component_ops);
if (ret) if (ret)
goto err_disable_pm_runtime; goto err_disable_pm_runtime;
return ret; return ret;
err_disable_pm_runtime: err_disable_pm_runtime:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(dev);
err_del_component: err_del_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret; return ret;
} }
......
...@@ -302,9 +302,12 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d) ...@@ -302,9 +302,12 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d)
struct exynos_drm_subdrv *subdrv = &g2d->subdrv; struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
kfree(g2d->cmdlist_node); kfree(g2d->cmdlist_node);
dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
g2d->cmdlist_pool_virt, if (g2d->cmdlist_pool_virt && g2d->cmdlist_pool) {
g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs); dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
g2d->cmdlist_pool_virt,
g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
}
} }
static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d)
......
...@@ -40,7 +40,6 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev) ...@@ -40,7 +40,6 @@ static inline bool is_drm_iommu_supported(struct drm_device *drm_dev)
#else #else
struct dma_iommu_mapping;
static inline int drm_create_iommu_mapping(struct drm_device *drm_dev) static inline int drm_create_iommu_mapping(struct drm_device *drm_dev)
{ {
return 0; return 0;
......
...@@ -426,18 +426,21 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ...@@ -426,18 +426,21 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
c_node->start_work = ipp_create_cmd_work(); c_node->start_work = ipp_create_cmd_work();
if (IS_ERR(c_node->start_work)) { if (IS_ERR(c_node->start_work)) {
DRM_ERROR("failed to create start work.\n"); DRM_ERROR("failed to create start work.\n");
ret = PTR_ERR(c_node->start_work);
goto err_remove_id; goto err_remove_id;
} }
c_node->stop_work = ipp_create_cmd_work(); c_node->stop_work = ipp_create_cmd_work();
if (IS_ERR(c_node->stop_work)) { if (IS_ERR(c_node->stop_work)) {
DRM_ERROR("failed to create stop work.\n"); DRM_ERROR("failed to create stop work.\n");
ret = PTR_ERR(c_node->stop_work);
goto err_free_start; goto err_free_start;
} }
c_node->event_work = ipp_create_event_work(); c_node->event_work = ipp_create_event_work();
if (IS_ERR(c_node->event_work)) { if (IS_ERR(c_node->event_work)) {
DRM_ERROR("failed to create event work.\n"); DRM_ERROR("failed to create event work.\n");
ret = PTR_ERR(c_node->event_work);
goto err_free_stop; goto err_free_stop;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/component.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -28,7 +29,6 @@ ...@@ -28,7 +29,6 @@
/* vidi has totally three virtual windows. */ /* vidi has totally three virtual windows. */
#define WINDOWS_NR 3 #define WINDOWS_NR 3
#define get_vidi_mgr(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct vidi_context, \ #define ctx_from_connector(c) container_of(c, struct vidi_context, \
connector) connector)
...@@ -47,11 +47,13 @@ struct vidi_win_data { ...@@ -47,11 +47,13 @@ struct vidi_win_data {
}; };
struct vidi_context { struct vidi_context {
struct exynos_drm_manager manager;
struct exynos_drm_display display;
struct platform_device *pdev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector connector; struct drm_connector connector;
struct exynos_drm_subdrv subdrv;
struct vidi_win_data win_data[WINDOWS_NR]; struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid; struct edid *raw_edid;
unsigned int clkdiv; unsigned int clkdiv;
...@@ -66,6 +68,16 @@ struct vidi_context { ...@@ -66,6 +68,16 @@ struct vidi_context {
int pipe; int pipe;
}; };
static inline struct vidi_context *manager_to_vidi(struct exynos_drm_manager *m)
{
return container_of(m, struct vidi_context, manager);
}
static inline struct vidi_context *display_to_vidi(struct exynos_drm_display *d)
{
return container_of(d, struct vidi_context, display);
}
static const char fake_edid_info[] = { static const char fake_edid_info[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
...@@ -93,7 +105,7 @@ static const char fake_edid_info[] = { ...@@ -93,7 +105,7 @@ static const char fake_edid_info[] = {
static void vidi_apply(struct exynos_drm_manager *mgr) static void vidi_apply(struct exynos_drm_manager *mgr)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
struct exynos_drm_manager_ops *mgr_ops = mgr->ops; struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
struct vidi_win_data *win_data; struct vidi_win_data *win_data;
int i; int i;
...@@ -110,7 +122,7 @@ static void vidi_apply(struct exynos_drm_manager *mgr) ...@@ -110,7 +122,7 @@ static void vidi_apply(struct exynos_drm_manager *mgr)
static void vidi_commit(struct exynos_drm_manager *mgr) static void vidi_commit(struct exynos_drm_manager *mgr)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
if (ctx->suspended) if (ctx->suspended)
return; return;
...@@ -118,7 +130,7 @@ static void vidi_commit(struct exynos_drm_manager *mgr) ...@@ -118,7 +130,7 @@ static void vidi_commit(struct exynos_drm_manager *mgr)
static int vidi_enable_vblank(struct exynos_drm_manager *mgr) static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
if (ctx->suspended) if (ctx->suspended)
return -EPERM; return -EPERM;
...@@ -140,7 +152,7 @@ static int vidi_enable_vblank(struct exynos_drm_manager *mgr) ...@@ -140,7 +152,7 @@ static int vidi_enable_vblank(struct exynos_drm_manager *mgr)
static void vidi_disable_vblank(struct exynos_drm_manager *mgr) static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
if (ctx->suspended) if (ctx->suspended)
return; return;
...@@ -152,7 +164,7 @@ static void vidi_disable_vblank(struct exynos_drm_manager *mgr) ...@@ -152,7 +164,7 @@ static void vidi_disable_vblank(struct exynos_drm_manager *mgr)
static void vidi_win_mode_set(struct exynos_drm_manager *mgr, static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay) struct exynos_drm_overlay *overlay)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
struct vidi_win_data *win_data; struct vidi_win_data *win_data;
int win; int win;
unsigned long offset; unsigned long offset;
...@@ -204,7 +216,7 @@ static void vidi_win_mode_set(struct exynos_drm_manager *mgr, ...@@ -204,7 +216,7 @@ static void vidi_win_mode_set(struct exynos_drm_manager *mgr,
static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos) static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
struct vidi_win_data *win_data; struct vidi_win_data *win_data;
int win = zpos; int win = zpos;
...@@ -229,7 +241,7 @@ static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos) ...@@ -229,7 +241,7 @@ static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos)
static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos) static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
struct vidi_win_data *win_data; struct vidi_win_data *win_data;
int win = zpos; int win = zpos;
...@@ -247,7 +259,7 @@ static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos) ...@@ -247,7 +259,7 @@ static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos)
static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable) static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
...@@ -271,7 +283,7 @@ static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable) ...@@ -271,7 +283,7 @@ static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable)
static void vidi_dpms(struct exynos_drm_manager *mgr, int mode) static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
DRM_DEBUG_KMS("%d\n", mode); DRM_DEBUG_KMS("%d\n", mode);
...@@ -297,7 +309,7 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode) ...@@ -297,7 +309,7 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr, static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev) struct drm_device *drm_dev)
{ {
struct vidi_context *ctx = mgr->ctx; struct vidi_context *ctx = manager_to_vidi(mgr);
struct exynos_drm_private *priv = drm_dev->dev_private; struct exynos_drm_private *priv = drm_dev->dev_private;
mgr->drm_dev = ctx->drm_dev = drm_dev; mgr->drm_dev = ctx->drm_dev = drm_dev;
...@@ -316,11 +328,6 @@ static struct exynos_drm_manager_ops vidi_manager_ops = { ...@@ -316,11 +328,6 @@ static struct exynos_drm_manager_ops vidi_manager_ops = {
.win_disable = vidi_win_disable, .win_disable = vidi_win_disable,
}; };
static struct exynos_drm_manager vidi_manager = {
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.ops = &vidi_manager_ops,
};
static void vidi_fake_vblank_handler(struct work_struct *work) static void vidi_fake_vblank_handler(struct work_struct *work)
{ {
struct vidi_context *ctx = container_of(work, struct vidi_context, struct vidi_context *ctx = container_of(work, struct vidi_context,
...@@ -349,9 +356,8 @@ static void vidi_fake_vblank_handler(struct work_struct *work) ...@@ -349,9 +356,8 @@ static void vidi_fake_vblank_handler(struct work_struct *work)
static int vidi_show_connection(struct device *dev, static int vidi_show_connection(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct vidi_context *ctx = dev_get_drvdata(dev);
int rc; int rc;
struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
struct vidi_context *ctx = mgr->ctx;
mutex_lock(&ctx->lock); mutex_lock(&ctx->lock);
...@@ -366,8 +372,7 @@ static int vidi_store_connection(struct device *dev, ...@@ -366,8 +372,7 @@ static int vidi_store_connection(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t len) const char *buf, size_t len)
{ {
struct exynos_drm_manager *mgr = get_vidi_mgr(dev); struct vidi_context *ctx = dev_get_drvdata(dev);
struct vidi_context *ctx = mgr->ctx;
int ret; int ret;
ret = kstrtoint(buf, 0, &ctx->connected); ret = kstrtoint(buf, 0, &ctx->connected);
...@@ -420,7 +425,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, ...@@ -420,7 +425,7 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
display = exynos_drm_get_display(encoder); display = exynos_drm_get_display(encoder);
if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) { if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) {
ctx = display->ctx; ctx = display_to_vidi(display);
break; break;
} }
} }
...@@ -530,7 +535,7 @@ static struct drm_connector_helper_funcs vidi_connector_helper_funcs = { ...@@ -530,7 +535,7 @@ static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
static int vidi_create_connector(struct exynos_drm_display *display, static int vidi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct vidi_context *ctx = display->ctx; struct vidi_context *ctx = display_to_vidi(display);
struct drm_connector *connector = &ctx->connector; struct drm_connector *connector = &ctx->connector;
int ret; int ret;
...@@ -556,27 +561,22 @@ static struct exynos_drm_display_ops vidi_display_ops = { ...@@ -556,27 +561,22 @@ static struct exynos_drm_display_ops vidi_display_ops = {
.create_connector = vidi_create_connector, .create_connector = vidi_create_connector,
}; };
static struct exynos_drm_display vidi_display = { static int vidi_bind(struct device *dev, struct device *master, void *data)
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.ops = &vidi_display_ops,
};
static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{ {
struct exynos_drm_manager *mgr = get_vidi_mgr(dev); struct vidi_context *ctx = dev_get_drvdata(dev);
struct vidi_context *ctx = mgr->ctx; struct drm_device *drm_dev = data;
struct drm_crtc *crtc = ctx->crtc; struct drm_crtc *crtc = ctx->crtc;
int ret; int ret;
vidi_mgr_initialize(mgr, drm_dev); vidi_mgr_initialize(&ctx->manager, drm_dev);
ret = exynos_drm_crtc_create(&vidi_manager); ret = exynos_drm_crtc_create(&ctx->manager);
if (ret) { if (ret) {
DRM_ERROR("failed to create crtc.\n"); DRM_ERROR("failed to create crtc.\n");
return ret; return ret;
} }
ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display); ret = exynos_drm_create_enc_conn(drm_dev, &ctx->display);
if (ret) { if (ret) {
crtc->funcs->destroy(crtc); crtc->funcs->destroy(crtc);
DRM_ERROR("failed to create encoder and connector.\n"); DRM_ERROR("failed to create encoder and connector.\n");
...@@ -586,9 +586,18 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) ...@@ -586,9 +586,18 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
return 0; return 0;
} }
static void vidi_unbind(struct device *dev, struct device *master, void *data)
{
}
static const struct component_ops vidi_component_ops = {
.bind = vidi_bind,
.unbind = vidi_unbind,
};
static int vidi_probe(struct platform_device *pdev) static int vidi_probe(struct platform_device *pdev)
{ {
struct exynos_drm_subdrv *subdrv;
struct vidi_context *ctx; struct vidi_context *ctx;
int ret; int ret;
...@@ -596,40 +605,54 @@ static int vidi_probe(struct platform_device *pdev) ...@@ -596,40 +605,54 @@ static int vidi_probe(struct platform_device *pdev)
if (!ctx) if (!ctx)
return -ENOMEM; return -ENOMEM;
ctx->manager.type = EXYNOS_DISPLAY_TYPE_VIDI;
ctx->manager.ops = &vidi_manager_ops;
ctx->display.type = EXYNOS_DISPLAY_TYPE_VIDI;
ctx->display.ops = &vidi_display_ops;
ctx->default_win = 0; ctx->default_win = 0;
ctx->pdev = pdev;
INIT_WORK(&ctx->work, vidi_fake_vblank_handler); ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
ctx->manager.type);
vidi_manager.ctx = ctx; if (ret)
vidi_display.ctx = ctx; return ret;
mutex_init(&ctx->lock); ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
ctx->display.type);
if (ret)
goto err_del_crtc_component;
platform_set_drvdata(pdev, &vidi_manager); INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
subdrv = &ctx->subdrv; mutex_init(&ctx->lock);
subdrv->dev = &pdev->dev;
subdrv->probe = vidi_subdrv_probe;
ret = exynos_drm_subdrv_register(subdrv); platform_set_drvdata(pdev, ctx);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register drm vidi device\n");
return ret;
}
ret = device_create_file(&pdev->dev, &dev_attr_connection); ret = device_create_file(&pdev->dev, &dev_attr_connection);
if (ret < 0) { if (ret < 0) {
exynos_drm_subdrv_unregister(subdrv); DRM_ERROR("failed to create connection sysfs.\n");
DRM_INFO("failed to create connection sysfs.\n"); goto err_del_conn_component;
} }
return 0; ret = component_add(&pdev->dev, &vidi_component_ops);
if (ret)
goto err_remove_file;
return ret;
err_remove_file:
device_remove_file(&pdev->dev, &dev_attr_connection);
err_del_conn_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
err_del_crtc_component:
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
} }
static int vidi_remove(struct platform_device *pdev) static int vidi_remove(struct platform_device *pdev)
{ {
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev); struct vidi_context *ctx = platform_get_drvdata(pdev);
struct vidi_context *ctx = mgr->ctx;
if (ctx->raw_edid != (struct edid *)fake_edid_info) { if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid); kfree(ctx->raw_edid);
...@@ -638,6 +661,10 @@ static int vidi_remove(struct platform_device *pdev) ...@@ -638,6 +661,10 @@ static int vidi_remove(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
component_del(&pdev->dev, &vidi_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0; return 0;
} }
...@@ -668,12 +695,19 @@ int exynos_drm_probe_vidi(void) ...@@ -668,12 +695,19 @@ int exynos_drm_probe_vidi(void)
return ret; return ret;
} }
static int exynos_drm_remove_vidi_device(struct device *dev, void *data)
{
platform_device_unregister(to_platform_device(dev));
return 0;
}
void exynos_drm_remove_vidi(void) void exynos_drm_remove_vidi(void)
{ {
struct vidi_context *ctx = vidi_manager.ctx; int ret = driver_for_each_device(&vidi_driver.driver, NULL, NULL,
struct exynos_drm_subdrv *subdrv = &ctx->subdrv; exynos_drm_remove_vidi_device);
struct platform_device *pdev = to_platform_device(subdrv->dev); /* silence compiler warning */
(void)ret;
platform_driver_unregister(&vidi_driver); platform_driver_unregister(&vidi_driver);
platform_device_unregister(pdev);
} }
...@@ -49,7 +49,6 @@ ...@@ -49,7 +49,6 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <media/s5p_hdmi.h> #include <media/s5p_hdmi.h>
#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector) #define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
#define HOTPLUG_DEBOUNCE_MS 1100 #define HOTPLUG_DEBOUNCE_MS 1100
...@@ -182,6 +181,7 @@ struct hdmi_conf_regs { ...@@ -182,6 +181,7 @@ struct hdmi_conf_regs {
}; };
struct hdmi_context { struct hdmi_context {
struct exynos_drm_display display;
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
struct drm_connector connector; struct drm_connector connector;
...@@ -213,6 +213,11 @@ struct hdmi_context { ...@@ -213,6 +213,11 @@ struct hdmi_context {
enum hdmi_type type; enum hdmi_type type;
}; };
static inline struct hdmi_context *display_to_hdmi(struct exynos_drm_display *d)
{
return container_of(d, struct hdmi_context, display);
}
struct hdmiphy_config { struct hdmiphy_config {
int pixel_clock; int pixel_clock;
u8 conf[32]; u8 conf[32];
...@@ -1123,7 +1128,7 @@ static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { ...@@ -1123,7 +1128,7 @@ static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
static int hdmi_create_connector(struct exynos_drm_display *display, static int hdmi_create_connector(struct exynos_drm_display *display,
struct drm_encoder *encoder) struct drm_encoder *encoder)
{ {
struct hdmi_context *hdata = display->ctx; struct hdmi_context *hdata = display_to_hdmi(display);
struct drm_connector *connector = &hdata->connector; struct drm_connector *connector = &hdata->connector;
int ret; int ret;
...@@ -2000,7 +2005,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, ...@@ -2000,7 +2005,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
static void hdmi_mode_set(struct exynos_drm_display *display, static void hdmi_mode_set(struct exynos_drm_display *display,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct hdmi_context *hdata = display->ctx; struct hdmi_context *hdata = display_to_hdmi(display);
struct drm_display_mode *m = mode; struct drm_display_mode *m = mode;
DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n", DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n",
...@@ -2019,7 +2024,7 @@ static void hdmi_mode_set(struct exynos_drm_display *display, ...@@ -2019,7 +2024,7 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
static void hdmi_commit(struct exynos_drm_display *display) static void hdmi_commit(struct exynos_drm_display *display)
{ {
struct hdmi_context *hdata = display->ctx; struct hdmi_context *hdata = display_to_hdmi(display);
mutex_lock(&hdata->hdmi_mutex); mutex_lock(&hdata->hdmi_mutex);
if (!hdata->powered) { if (!hdata->powered) {
...@@ -2033,7 +2038,7 @@ static void hdmi_commit(struct exynos_drm_display *display) ...@@ -2033,7 +2038,7 @@ static void hdmi_commit(struct exynos_drm_display *display)
static void hdmi_poweron(struct exynos_drm_display *display) static void hdmi_poweron(struct exynos_drm_display *display)
{ {
struct hdmi_context *hdata = display->ctx; struct hdmi_context *hdata = display_to_hdmi(display);
struct hdmi_resources *res = &hdata->res; struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex); mutex_lock(&hdata->hdmi_mutex);
...@@ -2064,7 +2069,7 @@ static void hdmi_poweron(struct exynos_drm_display *display) ...@@ -2064,7 +2069,7 @@ static void hdmi_poweron(struct exynos_drm_display *display)
static void hdmi_poweroff(struct exynos_drm_display *display) static void hdmi_poweroff(struct exynos_drm_display *display)
{ {
struct hdmi_context *hdata = display->ctx; struct hdmi_context *hdata = display_to_hdmi(display);
struct hdmi_resources *res = &hdata->res; struct hdmi_resources *res = &hdata->res;
mutex_lock(&hdata->hdmi_mutex); mutex_lock(&hdata->hdmi_mutex);
...@@ -2099,7 +2104,7 @@ static void hdmi_poweroff(struct exynos_drm_display *display) ...@@ -2099,7 +2104,7 @@ static void hdmi_poweroff(struct exynos_drm_display *display)
static void hdmi_dpms(struct exynos_drm_display *display, int mode) static void hdmi_dpms(struct exynos_drm_display *display, int mode)
{ {
struct hdmi_context *hdata = display->ctx; struct hdmi_context *hdata = display_to_hdmi(display);
struct drm_encoder *encoder = hdata->encoder; struct drm_encoder *encoder = hdata->encoder;
struct drm_crtc *crtc = encoder->crtc; struct drm_crtc *crtc = encoder->crtc;
struct drm_crtc_helper_funcs *funcs = NULL; struct drm_crtc_helper_funcs *funcs = NULL;
...@@ -2143,11 +2148,6 @@ static struct exynos_drm_display_ops hdmi_display_ops = { ...@@ -2143,11 +2148,6 @@ static struct exynos_drm_display_ops hdmi_display_ops = {
.commit = hdmi_commit, .commit = hdmi_commit,
}; };
static struct exynos_drm_display hdmi_display = {
.type = EXYNOS_DISPLAY_TYPE_HDMI,
.ops = &hdmi_display_ops,
};
static void hdmi_hotplug_work_func(struct work_struct *work) static void hdmi_hotplug_work_func(struct work_struct *work)
{ {
struct hdmi_context *hdata; struct hdmi_context *hdata;
...@@ -2302,12 +2302,11 @@ MODULE_DEVICE_TABLE (of, hdmi_match_types); ...@@ -2302,12 +2302,11 @@ MODULE_DEVICE_TABLE (of, hdmi_match_types);
static int hdmi_bind(struct device *dev, struct device *master, void *data) static int hdmi_bind(struct device *dev, struct device *master, void *data)
{ {
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct hdmi_context *hdata; struct hdmi_context *hdata = dev_get_drvdata(dev);
hdata = hdmi_display.ctx;
hdata->drm_dev = drm_dev; hdata->drm_dev = drm_dev;
return exynos_drm_create_enc_conn(drm_dev, &hdmi_display); return exynos_drm_create_enc_conn(drm_dev, &hdata->display);
} }
static void hdmi_unbind(struct device *dev, struct device *master, void *data) static void hdmi_unbind(struct device *dev, struct device *master, void *data)
...@@ -2349,31 +2348,28 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2349,31 +2348,28 @@ static int hdmi_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
int ret; int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR, if (!dev->of_node)
hdmi_display.type); return -ENODEV;
if (ret)
return ret;
if (!dev->of_node) {
ret = -ENODEV;
goto err_del_component;
}
pdata = drm_hdmi_dt_parse_pdata(dev); pdata = drm_hdmi_dt_parse_pdata(dev);
if (!pdata) { if (!pdata)
ret = -EINVAL; return -EINVAL;
goto err_del_component;
}
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL); hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata) { if (!hdata)
ret = -ENOMEM; return -ENOMEM;
goto err_del_component;
} hdata->display.type = EXYNOS_DISPLAY_TYPE_HDMI;
hdata->display.ops = &hdmi_display_ops;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
hdata->display.type);
if (ret)
return ret;
mutex_init(&hdata->hdmi_mutex); mutex_init(&hdata->hdmi_mutex);
platform_set_drvdata(pdev, &hdmi_display); platform_set_drvdata(pdev, hdata);
match = of_match_node(hdmi_match_types, dev->of_node); match = of_match_node(hdmi_match_types, dev->of_node);
if (!match) { if (!match) {
...@@ -2485,7 +2481,6 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2485,7 +2481,6 @@ static int hdmi_probe(struct platform_device *pdev)
} }
pm_runtime_enable(dev); pm_runtime_enable(dev);
hdmi_display.ctx = hdata;
ret = component_add(&pdev->dev, &hdmi_component_ops); ret = component_add(&pdev->dev, &hdmi_component_ops);
if (ret) if (ret)
...@@ -2510,7 +2505,7 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -2510,7 +2505,7 @@ static int hdmi_probe(struct platform_device *pdev)
static int hdmi_remove(struct platform_device *pdev) static int hdmi_remove(struct platform_device *pdev)
{ {
struct hdmi_context *hdata = hdmi_display.ctx; struct hdmi_context *hdata = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&hdata->hotplug_work); cancel_delayed_work_sync(&hdata->hotplug_work);
......
...@@ -40,8 +40,6 @@ ...@@ -40,8 +40,6 @@
#include "exynos_drm_iommu.h" #include "exynos_drm_iommu.h"
#include "exynos_mixer.h" #include "exynos_mixer.h"
#define get_mixer_manager(dev) platform_get_drvdata(to_platform_device(dev))
#define MIXER_WIN_NR 3 #define MIXER_WIN_NR 3
#define MIXER_DEFAULT_WIN 0 #define MIXER_DEFAULT_WIN 0
...@@ -86,6 +84,7 @@ enum mixer_version_id { ...@@ -86,6 +84,7 @@ enum mixer_version_id {
}; };
struct mixer_context { struct mixer_context {
struct exynos_drm_manager manager;
struct platform_device *pdev; struct platform_device *pdev;
struct device *dev; struct device *dev;
struct drm_device *drm_dev; struct drm_device *drm_dev;
...@@ -104,6 +103,11 @@ struct mixer_context { ...@@ -104,6 +103,11 @@ struct mixer_context {
atomic_t wait_vsync_event; atomic_t wait_vsync_event;
}; };
static inline struct mixer_context *mgr_to_mixer(struct exynos_drm_manager *mgr)
{
return container_of(mgr, struct mixer_context, manager);
}
struct mixer_drv_data { struct mixer_drv_data {
enum mixer_version_id version; enum mixer_version_id version;
bool is_vp_enabled; bool is_vp_enabled;
...@@ -854,7 +858,7 @@ static int mixer_initialize(struct exynos_drm_manager *mgr, ...@@ -854,7 +858,7 @@ static int mixer_initialize(struct exynos_drm_manager *mgr,
struct drm_device *drm_dev) struct drm_device *drm_dev)
{ {
int ret; int ret;
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct exynos_drm_private *priv; struct exynos_drm_private *priv;
priv = drm_dev->dev_private; priv = drm_dev->dev_private;
...@@ -885,7 +889,7 @@ static int mixer_initialize(struct exynos_drm_manager *mgr, ...@@ -885,7 +889,7 @@ static int mixer_initialize(struct exynos_drm_manager *mgr,
static void mixer_mgr_remove(struct exynos_drm_manager *mgr) static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
if (is_drm_iommu_supported(mixer_ctx->drm_dev)) if (is_drm_iommu_supported(mixer_ctx->drm_dev))
drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev); drm_iommu_detach_device(mixer_ctx->drm_dev, mixer_ctx->dev);
...@@ -893,7 +897,7 @@ static void mixer_mgr_remove(struct exynos_drm_manager *mgr) ...@@ -893,7 +897,7 @@ static void mixer_mgr_remove(struct exynos_drm_manager *mgr)
static int mixer_enable_vblank(struct exynos_drm_manager *mgr) static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &mixer_ctx->mixer_res; struct mixer_resources *res = &mixer_ctx->mixer_res;
if (!mixer_ctx->powered) { if (!mixer_ctx->powered) {
...@@ -910,7 +914,7 @@ static int mixer_enable_vblank(struct exynos_drm_manager *mgr) ...@@ -910,7 +914,7 @@ static int mixer_enable_vblank(struct exynos_drm_manager *mgr)
static void mixer_disable_vblank(struct exynos_drm_manager *mgr) static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &mixer_ctx->mixer_res; struct mixer_resources *res = &mixer_ctx->mixer_res;
/* disable vsync interrupt */ /* disable vsync interrupt */
...@@ -920,7 +924,7 @@ static void mixer_disable_vblank(struct exynos_drm_manager *mgr) ...@@ -920,7 +924,7 @@ static void mixer_disable_vblank(struct exynos_drm_manager *mgr)
static void mixer_win_mode_set(struct exynos_drm_manager *mgr, static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay) struct exynos_drm_overlay *overlay)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct hdmi_win_data *win_data; struct hdmi_win_data *win_data;
int win; int win;
...@@ -971,7 +975,7 @@ static void mixer_win_mode_set(struct exynos_drm_manager *mgr, ...@@ -971,7 +975,7 @@ static void mixer_win_mode_set(struct exynos_drm_manager *mgr,
static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos) static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos; int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
DRM_DEBUG_KMS("win: %d\n", win); DRM_DEBUG_KMS("win: %d\n", win);
...@@ -993,7 +997,7 @@ static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos) ...@@ -993,7 +997,7 @@ static void mixer_win_commit(struct exynos_drm_manager *mgr, int zpos)
static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos) static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &mixer_ctx->mixer_res; struct mixer_resources *res = &mixer_ctx->mixer_res;
int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos; int win = zpos == DEFAULT_ZPOS ? MIXER_DEFAULT_WIN : zpos;
unsigned long flags; unsigned long flags;
...@@ -1021,7 +1025,7 @@ static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos) ...@@ -1021,7 +1025,7 @@ static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos)
static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr) static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *mixer_ctx = mgr->ctx; struct mixer_context *mixer_ctx = mgr_to_mixer(mgr);
mutex_lock(&mixer_ctx->mixer_mutex); mutex_lock(&mixer_ctx->mixer_mutex);
if (!mixer_ctx->powered) { if (!mixer_ctx->powered) {
...@@ -1048,7 +1052,7 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr) ...@@ -1048,7 +1052,7 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr)
static void mixer_window_suspend(struct exynos_drm_manager *mgr) static void mixer_window_suspend(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *ctx = mgr->ctx; struct mixer_context *ctx = mgr_to_mixer(mgr);
struct hdmi_win_data *win_data; struct hdmi_win_data *win_data;
int i; int i;
...@@ -1062,7 +1066,7 @@ static void mixer_window_suspend(struct exynos_drm_manager *mgr) ...@@ -1062,7 +1066,7 @@ static void mixer_window_suspend(struct exynos_drm_manager *mgr)
static void mixer_window_resume(struct exynos_drm_manager *mgr) static void mixer_window_resume(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *ctx = mgr->ctx; struct mixer_context *ctx = mgr_to_mixer(mgr);
struct hdmi_win_data *win_data; struct hdmi_win_data *win_data;
int i; int i;
...@@ -1077,7 +1081,7 @@ static void mixer_window_resume(struct exynos_drm_manager *mgr) ...@@ -1077,7 +1081,7 @@ static void mixer_window_resume(struct exynos_drm_manager *mgr)
static void mixer_poweron(struct exynos_drm_manager *mgr) static void mixer_poweron(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *ctx = mgr->ctx; struct mixer_context *ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex); mutex_lock(&ctx->mixer_mutex);
...@@ -1111,7 +1115,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr) ...@@ -1111,7 +1115,7 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
static void mixer_poweroff(struct exynos_drm_manager *mgr) static void mixer_poweroff(struct exynos_drm_manager *mgr)
{ {
struct mixer_context *ctx = mgr->ctx; struct mixer_context *ctx = mgr_to_mixer(mgr);
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
mutex_lock(&ctx->mixer_mutex); mutex_lock(&ctx->mixer_mutex);
...@@ -1187,11 +1191,6 @@ static struct exynos_drm_manager_ops mixer_manager_ops = { ...@@ -1187,11 +1191,6 @@ static struct exynos_drm_manager_ops mixer_manager_ops = {
.win_disable = mixer_win_disable, .win_disable = mixer_win_disable,
}; };
static struct exynos_drm_manager mixer_manager = {
.type = EXYNOS_DISPLAY_TYPE_HDMI,
.ops = &mixer_manager_ops,
};
static struct mixer_drv_data exynos5420_mxr_drv_data = { static struct mixer_drv_data exynos5420_mxr_drv_data = {
.version = MXR_VER_128_0_0_184, .version = MXR_VER_128_0_0_184,
.is_vp_enabled = 0, .is_vp_enabled = 0,
...@@ -1249,48 +1248,17 @@ MODULE_DEVICE_TABLE(of, mixer_match_types); ...@@ -1249,48 +1248,17 @@ MODULE_DEVICE_TABLE(of, mixer_match_types);
static int mixer_bind(struct device *dev, struct device *manager, void *data) static int mixer_bind(struct device *dev, struct device *manager, void *data)
{ {
struct platform_device *pdev = to_platform_device(dev); struct mixer_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data; struct drm_device *drm_dev = data;
struct mixer_context *ctx;
struct mixer_drv_data *drv;
int ret; int ret;
dev_info(dev, "probe start\n"); ret = mixer_initialize(&ctx->manager, drm_dev);
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_ERROR("failed to alloc mixer context.\n");
return -ENOMEM;
}
mutex_init(&ctx->mixer_mutex);
if (dev->of_node) {
const struct of_device_id *match;
match = of_match_node(mixer_match_types, dev->of_node);
drv = (struct mixer_drv_data *)match->data;
} else {
drv = (struct mixer_drv_data *)
platform_get_device_id(pdev)->driver_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);
mixer_manager.ctx = ctx;
ret = mixer_initialize(&mixer_manager, drm_dev);
if (ret) if (ret)
return ret; return ret;
platform_set_drvdata(pdev, &mixer_manager); ret = exynos_drm_crtc_create(&ctx->manager);
ret = exynos_drm_crtc_create(&mixer_manager);
if (ret) { if (ret) {
mixer_mgr_remove(&mixer_manager); mixer_mgr_remove(&ctx->manager);
return ret; return ret;
} }
...@@ -1301,11 +1269,9 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ...@@ -1301,11 +1269,9 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
static void mixer_unbind(struct device *dev, struct device *master, void *data) static void mixer_unbind(struct device *dev, struct device *master, void *data)
{ {
struct exynos_drm_manager *mgr = dev_get_drvdata(dev); struct mixer_context *ctx = dev_get_drvdata(dev);
dev_info(dev, "remove successful\n"); mixer_mgr_remove(&ctx->manager);
mixer_mgr_remove(mgr);
pm_runtime_disable(dev); pm_runtime_disable(dev);
} }
...@@ -1317,22 +1283,62 @@ static const struct component_ops mixer_component_ops = { ...@@ -1317,22 +1283,62 @@ static const struct component_ops mixer_component_ops = {
static int mixer_probe(struct platform_device *pdev) static int mixer_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct mixer_drv_data *drv;
struct mixer_context *ctx;
int ret; int ret;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_ERROR("failed to alloc mixer context.\n");
return -ENOMEM;
}
mutex_init(&ctx->mixer_mutex);
ctx->manager.type = EXYNOS_DISPLAY_TYPE_HDMI;
ctx->manager.ops = &mixer_manager_ops;
if (dev->of_node) {
const struct of_device_id *match;
match = of_match_node(mixer_match_types, dev->of_node);
drv = (struct mixer_drv_data *)match->data;
} else {
drv = (struct mixer_drv_data *)
platform_get_device_id(pdev)->driver_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);
platform_set_drvdata(pdev, ctx);
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC, ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
mixer_manager.type); ctx->manager.type);
if (ret) if (ret)
return ret; return ret;
ret = component_add(&pdev->dev, &mixer_component_ops); ret = component_add(&pdev->dev, &mixer_component_ops);
if (ret) if (ret) {
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return ret;
}
pm_runtime_enable(dev);
return ret; return ret;
} }
static int mixer_remove(struct platform_device *pdev) static int mixer_remove(struct platform_device *pdev)
{ {
pm_runtime_disable(&pdev->dev);
component_del(&pdev->dev, &mixer_component_ops); component_del(&pdev->dev, &mixer_component_ops);
exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册