aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/exynos
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/exynos')
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c16
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c6
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c26
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c34
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c70
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c34
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c19
10 files changed, 108 insertions, 105 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index d620b0784257..618bd4d87d28 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -28,6 +28,7 @@
#include "drmP.h"
#include "drm_crtc_helper.h"
+#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
@@ -44,8 +45,9 @@ struct exynos_drm_connector {
/* convert exynos_video_timings to drm_display_mode */
static inline void
convert_to_display_mode(struct drm_display_mode *mode,
- struct fb_videomode *timing)
+ struct exynos_drm_panel_info *panel)
{
+ struct fb_videomode *timing = &panel->timing;
DRM_DEBUG_KMS("%s\n", __FILE__);
mode->clock = timing->pixclock / 1000;
@@ -60,6 +62,8 @@ convert_to_display_mode(struct drm_display_mode *mode,
mode->vsync_start = mode->vdisplay + timing->upper_margin;
mode->vsync_end = mode->vsync_start + timing->vsync_len;
mode->vtotal = mode->vsync_end + timing->lower_margin;
+ mode->width_mm = panel->width_mm;
+ mode->height_mm = panel->height_mm;
if (timing->vmode & FB_VMODE_INTERLACED)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
@@ -148,16 +152,18 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
connector->display_info.raw_edid = edid;
} else {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
- struct fb_videomode *timing;
+ struct exynos_drm_panel_info *panel;
- if (display_ops->get_timing)
- timing = display_ops->get_timing(manager->dev);
+ if (display_ops->get_panel)
+ panel = display_ops->get_panel(manager->dev);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
}
- convert_to_display_mode(mode, timing);
+ convert_to_display_mode(mode, panel);
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 661a03571d0c..d08a55896d50 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -193,6 +193,9 @@ int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
return err;
}
+ /* setup possible_clones. */
+ exynos_drm_encoder_setup(drm_dev);
+
/*
* if any specific driver such as fimd or hdmi driver called
* exynos_drm_subdrv_register() later than drm_load(),
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index e3861ac49295..de818831a511 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -307,9 +307,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
*/
event->pipe = exynos_crtc->pipe;
- list_add_tail(&event->base.link,
- &dev_priv->pageflip_event_list);
-
ret = drm_vblank_get(dev, exynos_crtc->pipe);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter\n");
@@ -318,6 +315,9 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
goto out;
}
+ list_add_tail(&event->base.link,
+ &dev_priv->pageflip_event_list);
+
crtc->fb = fb;
ret = exynos_drm_crtc_update(crtc);
if (ret) {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 35889ca255e9..58820ebd3558 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -33,6 +33,7 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
+#include "exynos_drm_encoder.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
@@ -99,6 +100,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto err_vblank;
+ /* setup possible_clones. */
+ exynos_drm_encoder_setup(dev);
+
/*
* create and configure fb helper and also exynos specific
* fbdev object.
@@ -141,16 +145,21 @@ static int exynos_drm_unload(struct drm_device *dev)
}
static void exynos_drm_preclose(struct drm_device *dev,
- struct drm_file *file_priv)
+ struct drm_file *file)
{
- struct exynos_drm_private *dev_priv = dev->dev_private;
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
- /*
- * drm framework frees all events at release time,
- * so private event list should be cleared.
- */
- if (!list_empty(&dev_priv->pageflip_event_list))
- INIT_LIST_HEAD(&dev_priv->pageflip_event_list);
+}
+
+static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!file->driver_priv)
+ return;
+
+ kfree(file->driver_priv);
+ file->driver_priv = NULL;
}
static void exynos_drm_lastclose(struct drm_device *dev)
@@ -195,6 +204,7 @@ static struct drm_driver exynos_drm_driver = {
.unload = exynos_drm_unload,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
+ .postclose = exynos_drm_postclose,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index e685e1e33055..13540de90bfc 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -136,7 +136,7 @@ struct exynos_drm_overlay {
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @is_connected: check for that display is connected or not.
* @get_edid: get edid modes from display driver.
- * @get_timing: get timing object from display driver.
+ * @get_panel: get panel object from display driver.
* @check_timing: check if timing is valid or not.
* @power_on: display device on or off.
*/
@@ -145,7 +145,7 @@ struct exynos_drm_display_ops {
bool (*is_connected)(struct device *dev);
int (*get_edid)(struct device *dev, struct drm_connector *connector,
u8 *edid, int len);
- void *(*get_timing)(struct device *dev);
+ void *(*get_panel)(struct device *dev);
int (*check_timing)(struct device *dev, void *timing);
int (*power_on)(struct device *dev, int mode);
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 86b93dde219a..ef4754f1519b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -195,6 +195,40 @@ static struct drm_encoder_funcs exynos_encoder_funcs = {
.destroy = exynos_drm_encoder_destroy,
};
+static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
+{
+ struct drm_encoder *clone;
+ struct drm_device *dev = encoder->dev;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display_ops *display_ops =
+ exynos_encoder->manager->display_ops;
+ unsigned int clone_mask = 0;
+ int cnt = 0;
+
+ list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+ switch (display_ops->type) {
+ case EXYNOS_DISPLAY_TYPE_LCD:
+ case EXYNOS_DISPLAY_TYPE_HDMI:
+ clone_mask |= (1 << (cnt++));
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return clone_mask;
+}
+
+void exynos_drm_encoder_setup(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ encoder->possible_clones = exynos_drm_encoder_clones(encoder);
+}
+
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *manager,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index 97b087a51cb6..eb7d2316847e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -30,6 +30,7 @@
struct exynos_drm_manager;
+void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *mgr,
unsigned int possible_crtcs);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index d7ae29d2f3d6..3508700e529b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -195,66 +195,6 @@ out:
return ret;
}
-static bool
-exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
- struct drm_fb_helper_surface_size *sizes)
-{
- if (fb->width != sizes->surface_width)
- return false;
- if (fb->height != sizes->surface_height)
- return false;
- if (fb->bits_per_pixel != sizes->surface_bpp)
- return false;
- if (fb->depth != sizes->surface_depth)
- return false;
-
- return true;
-}
-
-static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
-{
- struct drm_device *dev = helper->dev;
- struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
- struct exynos_drm_gem_obj *exynos_gem_obj;
- struct drm_framebuffer *fb = helper->fb;
- struct drm_mode_fb_cmd2 mode_cmd = { 0 };
- unsigned long size;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (exynos_drm_fbdev_is_samefb(fb, sizes))
- return 0;
-
- mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
- mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
-
- if (exynos_fbdev->exynos_gem_obj)
- exynos_drm_gem_destroy(exynos_fbdev->exynos_gem_obj);
-
- if (fb->funcs->destroy)
- fb->funcs->destroy(fb);
-
- size = mode_cmd.pitches[0] * mode_cmd.height;
- exynos_gem_obj = exynos_drm_gem_create(dev, size);
- if (IS_ERR(exynos_gem_obj))
- return PTR_ERR(exynos_gem_obj);
-
- exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
-
- helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
- &exynos_gem_obj->base);
- if (IS_ERR_OR_NULL(helper->fb)) {
- DRM_ERROR("failed to create drm framebuffer.\n");
- return PTR_ERR(helper->fb);
- }
-
- return exynos_drm_fbdev_update(helper, helper->fb);
-}
-
static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -262,6 +202,10 @@ static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
DRM_DEBUG_KMS("%s\n", __FILE__);
+ /*
+ * with !helper->fb, it means that this funcion is called first time
+ * and after that, the helper->fb would be used as clone mode.
+ */
if (!helper->fb) {
ret = exynos_drm_fbdev_create(helper, sizes);
if (ret < 0) {
@@ -274,12 +218,6 @@ static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
* because register_framebuffer() should be called.
*/
ret = 1;
- } else {
- ret = exynos_drm_fbdev_recreate(helper, sizes);
- if (ret < 0) {
- DRM_ERROR("failed to reconfigure fbdev\n");
- return ret;
- }
}
return ret;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index b6a737d196ae..360adf2bba04 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -89,7 +89,7 @@ struct fimd_context {
bool suspended;
struct mutex lock;
- struct fb_videomode *timing;
+ struct exynos_drm_panel_info *panel;
};
static bool fimd_display_is_connected(struct device *dev)
@@ -101,13 +101,13 @@ static bool fimd_display_is_connected(struct device *dev)
return true;
}
-static void *fimd_get_timing(struct device *dev)
+static void *fimd_get_panel(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
- return ctx->timing;
+ return ctx->panel;
}
static int fimd_check_timing(struct device *dev, void *timing)
@@ -131,7 +131,7 @@ static int fimd_display_power_on(struct device *dev, int mode)
static struct exynos_drm_display_ops fimd_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.is_connected = fimd_display_is_connected,
- .get_timing = fimd_get_timing,
+ .get_panel = fimd_get_panel,
.check_timing = fimd_check_timing,
.power_on = fimd_display_power_on,
};
@@ -193,7 +193,8 @@ static void fimd_apply(struct device *subdrv_dev)
static void fimd_commit(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
- struct fb_videomode *timing = ctx->timing;
+ struct exynos_drm_panel_info *panel = ctx->panel;
+ struct fb_videomode *timing = &panel->timing;
u32 val;
if (ctx->suspended)
@@ -604,7 +605,12 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
}
if (is_checked) {
- drm_vblank_put(drm_dev, crtc);
+ /*
+ * call drm_vblank_put only in case that drm_vblank_get was
+ * called.
+ */
+ if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+ drm_vblank_put(drm_dev, crtc);
/*
* don't off vblank if vblank_disable_allowed is 1,
@@ -781,7 +787,7 @@ static int __devinit fimd_probe(struct platform_device *pdev)
struct fimd_context *ctx;
struct exynos_drm_subdrv *subdrv;
struct exynos_drm_fimd_pdata *pdata;
- struct fb_videomode *timing;
+ struct exynos_drm_panel_info *panel;
struct resource *res;
int win;
int ret = -EINVAL;
@@ -794,9 +800,9 @@ static int __devinit fimd_probe(struct platform_device *pdev)
return -EINVAL;
}
- timing = &pdata->timing;
- if (!timing) {
- dev_err(dev, "timing is null.\n");
+ panel = &pdata->panel;
+ if (!panel) {
+ dev_err(dev, "panel is null.\n");
return -EINVAL;
}
@@ -858,16 +864,16 @@ static int __devinit fimd_probe(struct platform_device *pdev)
goto err_req_irq;
}
- ctx->clkdiv = fimd_calc_clkdiv(ctx, timing);
+ ctx->clkdiv = fimd_calc_clkdiv(ctx, &panel->timing);
ctx->vidcon0 = pdata->vidcon0;
ctx->vidcon1 = pdata->vidcon1;
ctx->default_win = pdata->default_win;
- ctx->timing = timing;
+ ctx->panel = panel;
- timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
+ panel->timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n",
- timing->pixclock, ctx->clkdiv);
+ panel->timing.pixclock, ctx->clkdiv);
subdrv = &ctx->subdrv;
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index ac24cff39775..93846e810e38 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -712,7 +712,12 @@ static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc)
}
if (is_checked)
- drm_vblank_put(drm_dev, crtc);
+ /*
+ * call drm_vblank_put only in case that drm_vblank_get was
+ * called.
+ */
+ if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+ drm_vblank_put(drm_dev, crtc);
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
}
@@ -779,15 +784,15 @@ static void mixer_win_reset(struct mixer_context *ctx)
mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
MXR_STATUS_BURST_MASK);
- /* setting default layer priority: layer1 > video > layer0
+ /* setting default layer priority: layer1 > layer0 > video
* because typical usage scenario would be
+ * layer1 - OSD
* layer0 - framebuffer
* video - video overlay
- * layer1 - OSD
*/
- val = MXR_LAYER_CFG_GRP0_VAL(1);
- val |= MXR_LAYER_CFG_VP_VAL(2);
- val |= MXR_LAYER_CFG_GRP1_VAL(3);
+ val = MXR_LAYER_CFG_GRP1_VAL(3);
+ val |= MXR_LAYER_CFG_GRP0_VAL(2);
+ val |= MXR_LAYER_CFG_VP_VAL(1);
mixer_reg_write(res, MXR_LAYER_CFG, val);
/* setting background color */
@@ -1044,7 +1049,7 @@ static int mixer_remove(struct platform_device *pdev)
platform_get_drvdata(pdev);
struct mixer_context *ctx = (struct mixer_context *)drm_hdmi_ctx->ctx;
- dev_info(dev, "remove sucessful\n");
+ dev_info(dev, "remove successful\n");
mixer_resource_poweroff(ctx);
mixer_resources_cleanup(ctx);