Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux

* 'drm-fixes' of git://people.freedesktop.org/~airlied/linux:
  drm/exynos: fixed wrong err ptr usage and destroy call in exeception
  drm/exynos: Add disable of manager
  drm/exynos: include linux/module.h
  drm/exynos: fix vblank bug.
  drm/exynos: changed buffer structure.
  drm/exynos: removed unnecessary variable.
  drm/exynos: use gem create function generically
  drm/exynos: checked for null pointer
  drm/exynos: added crtc dpms for disable crtc
  drm/exynos: removed meaningless parameter from fbdev update
  drm/exynos: restored kernel_fb_list when reiniting fb_helper
  drm/exynos: changed exynos_drm_display to exynos_drm_display_ops
  drm/exynos: added manager object to connector
  drm/exynos: fixed converting between display mode and timing
  drm/exynos: fixed connector flag with hpd and interlace scan for hdmi
  drm/exynos: added kms poll for handling hpd event
This commit is contained in:
Linus Torvalds 2011-11-28 09:05:23 -08:00
commit 4244cb482e
15 changed files with 440 additions and 231 deletions

View File

@ -27,82 +27,84 @@
#include "drm.h" #include "drm.h"
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h" #include "exynos_drm_buf.h"
static DEFINE_MUTEX(exynos_drm_buf_lock);
static int lowlevel_buffer_allocate(struct drm_device *dev, static int lowlevel_buffer_allocate(struct drm_device *dev,
struct exynos_drm_buf_entry *entry) struct exynos_drm_gem_buf *buffer)
{ {
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size, buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size,
(dma_addr_t *)&entry->paddr, GFP_KERNEL); &buffer->dma_addr, GFP_KERNEL);
if (!entry->paddr) { if (!buffer->kvaddr) {
DRM_ERROR("failed to allocate buffer.\n"); DRM_ERROR("failed to allocate buffer.\n");
return -ENOMEM; return -ENOMEM;
} }
DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n", DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
(unsigned int)entry->vaddr, entry->paddr, entry->size); (unsigned long)buffer->kvaddr,
(unsigned long)buffer->dma_addr,
buffer->size);
return 0; return 0;
} }
static void lowlevel_buffer_deallocate(struct drm_device *dev, static void lowlevel_buffer_deallocate(struct drm_device *dev,
struct exynos_drm_buf_entry *entry) struct exynos_drm_gem_buf *buffer)
{ {
DRM_DEBUG_KMS("%s.\n", __FILE__); DRM_DEBUG_KMS("%s.\n", __FILE__);
if (entry->paddr && entry->vaddr && entry->size) if (buffer->dma_addr && buffer->size)
dma_free_writecombine(dev->dev, entry->size, entry->vaddr, dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr,
entry->paddr); (dma_addr_t)buffer->dma_addr);
else else
DRM_DEBUG_KMS("entry data is null.\n"); DRM_DEBUG_KMS("buffer data are invalid.\n");
} }
struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev, struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
unsigned int size) unsigned int size)
{ {
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
DRM_DEBUG_KMS("%s.\n", __FILE__); DRM_DEBUG_KMS("%s.\n", __FILE__);
DRM_DEBUG_KMS("desired size = 0x%x\n", size);
entry = kzalloc(sizeof(*entry), GFP_KERNEL); buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!entry) { if (!buffer) {
DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n"); DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
entry->size = size; buffer->size = size;
/* /*
* allocate memory region with size and set the memory information * allocate memory region with size and set the memory information
* to vaddr and paddr of a entry object. * to vaddr and dma_addr of a buffer object.
*/ */
if (lowlevel_buffer_allocate(dev, entry) < 0) { if (lowlevel_buffer_allocate(dev, buffer) < 0) {
kfree(entry); kfree(buffer);
entry = NULL; buffer = NULL;
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
return entry; return buffer;
} }
void exynos_drm_buf_destroy(struct drm_device *dev, void exynos_drm_buf_destroy(struct drm_device *dev,
struct exynos_drm_buf_entry *entry) struct exynos_drm_gem_buf *buffer)
{ {
DRM_DEBUG_KMS("%s.\n", __FILE__); DRM_DEBUG_KMS("%s.\n", __FILE__);
if (!entry) { if (!buffer) {
DRM_DEBUG_KMS("entry is null.\n"); DRM_DEBUG_KMS("buffer is null.\n");
return; return;
} }
lowlevel_buffer_deallocate(dev, entry); lowlevel_buffer_deallocate(dev, buffer);
kfree(entry); kfree(buffer);
entry = NULL; buffer = NULL;
} }
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");

View File

@ -26,28 +26,15 @@
#ifndef _EXYNOS_DRM_BUF_H_ #ifndef _EXYNOS_DRM_BUF_H_
#define _EXYNOS_DRM_BUF_H_ #define _EXYNOS_DRM_BUF_H_
/*
* exynos drm buffer entry structure.
*
* @paddr: physical address of allocated memory.
* @vaddr: kernel virtual address of allocated memory.
* @size: size of allocated memory.
*/
struct exynos_drm_buf_entry {
dma_addr_t paddr;
void __iomem *vaddr;
unsigned int size;
};
/* allocate physical memory. */ /* allocate physical memory. */
struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev, struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
unsigned int size); unsigned int size);
/* get physical memory information of a drm framebuffer. */ /* get memory information of a drm framebuffer. */
struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb); struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);
/* remove allocated physical memory. */ /* remove allocated physical memory. */
void exynos_drm_buf_destroy(struct drm_device *dev, void exynos_drm_buf_destroy(struct drm_device *dev,
struct exynos_drm_buf_entry *entry); struct exynos_drm_gem_buf *buffer);
#endif #endif

View File

@ -37,6 +37,8 @@
struct exynos_drm_connector { struct exynos_drm_connector {
struct drm_connector drm_connector; struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_manager *manager;
}; };
/* convert exynos_video_timings to drm_display_mode */ /* convert exynos_video_timings to drm_display_mode */
@ -47,6 +49,7 @@ convert_to_display_mode(struct drm_display_mode *mode,
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
mode->clock = timing->pixclock / 1000; mode->clock = timing->pixclock / 1000;
mode->vrefresh = timing->refresh;
mode->hdisplay = timing->xres; mode->hdisplay = timing->xres;
mode->hsync_start = mode->hdisplay + timing->left_margin; mode->hsync_start = mode->hdisplay + timing->left_margin;
@ -57,6 +60,12 @@ convert_to_display_mode(struct drm_display_mode *mode,
mode->vsync_start = mode->vdisplay + timing->upper_margin; mode->vsync_start = mode->vdisplay + timing->upper_margin;
mode->vsync_end = mode->vsync_start + timing->vsync_len; mode->vsync_end = mode->vsync_start + timing->vsync_len;
mode->vtotal = mode->vsync_end + timing->lower_margin; mode->vtotal = mode->vsync_end + timing->lower_margin;
if (timing->vmode & FB_VMODE_INTERLACED)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (timing->vmode & FB_VMODE_DOUBLE)
mode->flags |= DRM_MODE_FLAG_DBLSCAN;
} }
/* convert drm_display_mode to exynos_video_timings */ /* convert drm_display_mode to exynos_video_timings */
@ -69,7 +78,7 @@ convert_to_video_timing(struct fb_videomode *timing,
memset(timing, 0, sizeof(*timing)); memset(timing, 0, sizeof(*timing));
timing->pixclock = mode->clock * 1000; timing->pixclock = mode->clock * 1000;
timing->refresh = mode->vrefresh; timing->refresh = drm_mode_vrefresh(mode);
timing->xres = mode->hdisplay; timing->xres = mode->hdisplay;
timing->left_margin = mode->hsync_start - mode->hdisplay; timing->left_margin = mode->hsync_start - mode->hdisplay;
@ -92,15 +101,16 @@ convert_to_video_timing(struct fb_videomode *timing,
static int exynos_drm_connector_get_modes(struct drm_connector *connector) static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{ {
struct exynos_drm_manager *manager = struct exynos_drm_connector *exynos_connector =
exynos_drm_get_manager(connector->encoder); to_exynos_connector(connector);
struct exynos_drm_display *display = manager->display; struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
unsigned int count; unsigned int count;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (!display) { if (!display_ops) {
DRM_DEBUG_KMS("display is null.\n"); DRM_DEBUG_KMS("display_ops is null.\n");
return 0; return 0;
} }
@ -112,7 +122,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
* P.S. in case of lcd panel, count is always 1 if success * P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode. * because lcd panel has only one mode.
*/ */
if (display->get_edid) { if (display_ops->get_edid) {
int ret; int ret;
void *edid; void *edid;
@ -122,7 +132,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
return 0; return 0;
} }
ret = display->get_edid(manager->dev, connector, ret = display_ops->get_edid(manager->dev, connector,
edid, MAX_EDID); edid, MAX_EDID);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("failed to get edid data.\n"); DRM_ERROR("failed to get edid data.\n");
@ -140,8 +150,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
struct drm_display_mode *mode = drm_mode_create(connector->dev); struct drm_display_mode *mode = drm_mode_create(connector->dev);
struct fb_videomode *timing; struct fb_videomode *timing;
if (display->get_timing) if (display_ops->get_timing)
timing = display->get_timing(manager->dev); timing = display_ops->get_timing(manager->dev);
else { else {
drm_mode_destroy(connector->dev, mode); drm_mode_destroy(connector->dev, mode);
return 0; return 0;
@ -162,9 +172,10 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
static int exynos_drm_connector_mode_valid(struct drm_connector *connector, static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct exynos_drm_manager *manager = struct exynos_drm_connector *exynos_connector =
exynos_drm_get_manager(connector->encoder); to_exynos_connector(connector);
struct exynos_drm_display *display = manager->display; struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
struct fb_videomode timing; struct fb_videomode timing;
int ret = MODE_BAD; int ret = MODE_BAD;
@ -172,8 +183,8 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
convert_to_video_timing(&timing, mode); convert_to_video_timing(&timing, mode);
if (display && display->check_timing) if (display_ops && display_ops->check_timing)
if (!display->check_timing(manager->dev, (void *)&timing)) if (!display_ops->check_timing(manager->dev, (void *)&timing))
ret = MODE_OK; ret = MODE_OK;
return ret; return ret;
@ -181,9 +192,25 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
{ {
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct drm_mode_object *obj;
struct drm_encoder *encoder;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
return connector->encoder; obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
DRM_MODE_OBJECT_ENCODER);
if (!obj) {
DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
exynos_connector->encoder_id);
return NULL;
}
encoder = obj_to_encoder(obj);
return encoder;
} }
static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
@ -196,15 +223,17 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
static enum drm_connector_status static enum drm_connector_status
exynos_drm_connector_detect(struct drm_connector *connector, bool force) exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{ {
struct exynos_drm_manager *manager = struct exynos_drm_connector *exynos_connector =
exynos_drm_get_manager(connector->encoder); to_exynos_connector(connector);
struct exynos_drm_display *display = manager->display; struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops =
manager->display_ops;
enum drm_connector_status status = connector_status_disconnected; enum drm_connector_status status = connector_status_disconnected;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (display && display->is_connected) { if (display_ops && display_ops->is_connected) {
if (display->is_connected(manager->dev)) if (display_ops->is_connected(manager->dev))
status = connector_status_connected; status = connector_status_connected;
else else
status = connector_status_disconnected; status = connector_status_disconnected;
@ -251,9 +280,11 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
connector = &exynos_connector->drm_connector; connector = &exynos_connector->drm_connector;
switch (manager->display->type) { switch (manager->display_ops->type) {
case EXYNOS_DISPLAY_TYPE_HDMI: case EXYNOS_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA; type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break; break;
default: default:
type = DRM_MODE_CONNECTOR_Unknown; type = DRM_MODE_CONNECTOR_Unknown;
@ -267,7 +298,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
if (err) if (err)
goto err_connector; goto err_connector;
exynos_connector->encoder_id = encoder->base.id;
exynos_connector->manager = manager;
connector->encoder = encoder; connector->encoder = encoder;
err = drm_mode_connector_attach_encoder(connector, encoder); err = drm_mode_connector_attach_encoder(connector, encoder);
if (err) { if (err) {
DRM_ERROR("failed to attach a connector to a encoder\n"); DRM_ERROR("failed to attach a connector to a encoder\n");

View File

@ -29,35 +29,16 @@
#include "drmP.h" #include "drmP.h"
#include "drm_crtc_helper.h" #include "drm_crtc_helper.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_fb.h" #include "exynos_drm_fb.h"
#include "exynos_drm_encoder.h" #include "exynos_drm_encoder.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h" #include "exynos_drm_buf.h"
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
drm_crtc) drm_crtc)
/*
* Exynos specific crtc postion structure.
*
* @fb_x: offset x on a framebuffer to be displyed
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed
* - the unit is screen coordinates.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_w: width of hardware screen.
* @crtc_h: height of hardware screen.
*/
struct exynos_drm_crtc_pos {
unsigned int fb_x;
unsigned int fb_y;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_w;
unsigned int crtc_h;
};
/* /*
* Exynos specific crtc structure. * Exynos specific crtc structure.
* *
@ -85,30 +66,31 @@ static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
exynos_drm_fn_encoder(crtc, overlay, exynos_drm_fn_encoder(crtc, overlay,
exynos_drm_encoder_crtc_mode_set); exynos_drm_encoder_crtc_mode_set);
exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_commit); exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
exynos_drm_encoder_crtc_commit);
} }
static int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct exynos_drm_crtc_pos *pos) struct exynos_drm_crtc_pos *pos)
{ {
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
unsigned int actual_w; unsigned int actual_w;
unsigned int actual_h; unsigned int actual_h;
entry = exynos_drm_fb_get_buf(fb); buffer = exynos_drm_fb_get_buf(fb);
if (!entry) { if (!buffer) {
DRM_LOG_KMS("entry is null.\n"); DRM_LOG_KMS("buffer is null.\n");
return -EFAULT; return -EFAULT;
} }
overlay->paddr = entry->paddr; overlay->dma_addr = buffer->dma_addr;
overlay->vaddr = entry->vaddr; overlay->vaddr = buffer->kvaddr;
DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n", DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n",
(unsigned long)overlay->vaddr, (unsigned long)overlay->vaddr,
(unsigned long)overlay->paddr); (unsigned long)overlay->dma_addr);
actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w); actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h); actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
@ -171,9 +153,26 @@ static int exynos_drm_crtc_update(struct drm_crtc *crtc)
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{ {
DRM_DEBUG_KMS("%s\n", __FILE__); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
/* TODO */ DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
exynos_drm_encoder_crtc_commit);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
/* TODO */
exynos_drm_fn_encoder(crtc, NULL,
exynos_drm_encoder_crtc_disable);
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
} }
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
@ -185,9 +184,12 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
static void exynos_drm_crtc_commit(struct drm_crtc *crtc) static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */ exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
exynos_drm_encoder_crtc_commit);
} }
static bool static bool

View File

@ -35,4 +35,29 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
/*
* Exynos specific crtc postion structure.
*
* @fb_x: offset x on a framebuffer to be displyed
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed
* - the unit is screen coordinates.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_w: width of hardware screen.
* @crtc_h: height of hardware screen.
*/
struct exynos_drm_crtc_pos {
unsigned int fb_x;
unsigned int fb_y;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_w;
unsigned int crtc_h;
};
int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
struct drm_framebuffer *fb,
struct drm_display_mode *mode,
struct exynos_drm_crtc_pos *pos);
#endif #endif

View File

@ -27,6 +27,7 @@
#include "drmP.h" #include "drmP.h"
#include "drm.h" #include "drm.h"
#include "drm_crtc_helper.h"
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
@ -61,6 +62,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
drm_mode_config_init(dev); drm_mode_config_init(dev);
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev);
exynos_drm_mode_config_init(dev); exynos_drm_mode_config_init(dev);
/* /*
@ -116,6 +120,7 @@ static int exynos_drm_unload(struct drm_device *dev)
exynos_drm_fbdev_fini(dev); exynos_drm_fbdev_fini(dev);
exynos_drm_device_unregister(dev); exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev); drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
kfree(dev->dev_private); kfree(dev->dev_private);

View File

@ -29,6 +29,7 @@
#ifndef _EXYNOS_DRM_DRV_H_ #ifndef _EXYNOS_DRM_DRV_H_
#define _EXYNOS_DRM_DRV_H_ #define _EXYNOS_DRM_DRV_H_
#include <linux/module.h>
#include "drm.h" #include "drm.h"
#define MAX_CRTC 2 #define MAX_CRTC 2
@ -79,8 +80,8 @@ struct exynos_drm_overlay_ops {
* @scan_flag: interlace or progressive way. * @scan_flag: interlace or progressive way.
* (it could be DRM_MODE_FLAG_*) * (it could be DRM_MODE_FLAG_*)
* @bpp: pixel size.(in bit) * @bpp: pixel size.(in bit)
* @paddr: bus(accessed by dma) physical memory address to this overlay * @dma_addr: bus(accessed by dma) address to the memory region allocated
* and this is physically continuous. * for a overlay.
* @vaddr: virtual memory addresss to this overlay. * @vaddr: virtual memory addresss to this overlay.
* @default_win: a window to be enabled. * @default_win: a window to be enabled.
* @color_key: color key on or off. * @color_key: color key on or off.
@ -108,7 +109,7 @@ struct exynos_drm_overlay {
unsigned int scan_flag; unsigned int scan_flag;
unsigned int bpp; unsigned int bpp;
unsigned int pitch; unsigned int pitch;
dma_addr_t paddr; dma_addr_t dma_addr;
void __iomem *vaddr; void __iomem *vaddr;
bool default_win; bool default_win;
@ -130,7 +131,7 @@ struct exynos_drm_overlay {
* @check_timing: check if timing is valid or not. * @check_timing: check if timing is valid or not.
* @power_on: display device on or off. * @power_on: display device on or off.
*/ */
struct exynos_drm_display { struct exynos_drm_display_ops {
enum exynos_drm_output_type type; enum exynos_drm_output_type type;
bool (*is_connected)(struct device *dev); bool (*is_connected)(struct device *dev);
int (*get_edid)(struct device *dev, struct drm_connector *connector, int (*get_edid)(struct device *dev, struct drm_connector *connector,
@ -146,12 +147,14 @@ struct exynos_drm_display {
* @mode_set: convert drm_display_mode to hw specific display mode and * @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set(). * would be called by encoder->mode_set().
* @commit: set current hw specific display mode to hw. * @commit: set current hw specific display mode to hw.
* @disable: disable hardware specific display mode.
* @enable_vblank: specific driver callback for enabling vblank interrupt. * @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt.
*/ */
struct exynos_drm_manager_ops { struct exynos_drm_manager_ops {
void (*mode_set)(struct device *subdrv_dev, void *mode); void (*mode_set)(struct device *subdrv_dev, void *mode);
void (*commit)(struct device *subdrv_dev); void (*commit)(struct device *subdrv_dev);
void (*disable)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev); int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev); void (*disable_vblank)(struct device *subdrv_dev);
}; };
@ -178,7 +181,7 @@ struct exynos_drm_manager {
int pipe; int pipe;
struct exynos_drm_manager_ops *ops; struct exynos_drm_manager_ops *ops;
struct exynos_drm_overlay_ops *overlay_ops; struct exynos_drm_overlay_ops *overlay_ops;
struct exynos_drm_display *display; struct exynos_drm_display_ops *display_ops;
}; };
/* /*

View File

@ -53,15 +53,36 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct drm_connector *connector; struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
switch (mode) {
case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
/* TODO */
if (manager_ops && manager_ops->disable)
manager_ops->disable(manager->dev);
break;
default:
DRM_ERROR("unspecified mode %d\n", mode);
break;
}
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) { if (connector->encoder == encoder) {
struct exynos_drm_display *display = manager->display; struct exynos_drm_display_ops *display_ops =
manager->display_ops;
if (display && display->power_on) DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
display->power_on(manager->dev, mode); connector->base.id, mode);
if (display_ops && display_ops->power_on)
display_ops->power_on(manager->dev, mode);
} }
} }
} }
@ -116,15 +137,11 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{ {
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops; struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->commit) if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev); manager_ops->commit(manager->dev);
if (overlay_ops && overlay_ops->commit)
overlay_ops->commit(manager->dev);
} }
static struct drm_crtc * static struct drm_crtc *
@ -208,10 +225,23 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_manager *manager;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc) /*
continue; * if crtc is detached from encoder, check pipe,
* otherwise check crtc attached to encoder
*/
if (!encoder->crtc) {
manager = to_exynos_encoder(encoder)->manager;
if (manager->pipe < 0 ||
private->crtc[manager->pipe] != crtc)
continue;
} else {
if (encoder->crtc != crtc)
continue;
}
fn(encoder, data); fn(encoder, data);
} }
@ -250,8 +280,18 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
struct exynos_drm_manager *manager = struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager; to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int crtc = *(int *)data;
overlay_ops->commit(manager->dev); DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* when crtc is detached from encoder, this pipe is used
* to select manager operation
*/
manager->pipe = crtc;
if (overlay_ops && overlay_ops->commit)
overlay_ops->commit(manager->dev);
} }
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
@ -261,7 +301,28 @@ void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct exynos_drm_overlay *overlay = data; struct exynos_drm_overlay *overlay = data;
overlay_ops->mode_set(manager->dev, overlay); if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
}
void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
DRM_DEBUG_KMS("\n");
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev);
/*
* crtc is already detached from encoder and last
* function for detaching is properly done, so
* clear pipe from manager to prevent repeated call
*/
if (!encoder->crtc)
manager->pipe = -1;
} }
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");

View File

@ -41,5 +41,6 @@ void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data);
#endif #endif

View File

@ -29,7 +29,9 @@
#include "drmP.h" #include "drmP.h"
#include "drm_crtc.h" #include "drm_crtc.h"
#include "drm_crtc_helper.h" #include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h" #include "exynos_drm_fb.h"
#include "exynos_drm_buf.h" #include "exynos_drm_buf.h"
#include "exynos_drm_gem.h" #include "exynos_drm_gem.h"
@ -41,14 +43,14 @@
* *
* @fb: drm framebuffer obejct. * @fb: drm framebuffer obejct.
* @exynos_gem_obj: exynos specific gem object containing a gem object. * @exynos_gem_obj: exynos specific gem object containing a gem object.
* @entry: pointer to exynos drm buffer entry object. * @buffer: pointer to exynos_drm_gem_buffer object.
* - containing only the information to physically continuous memory * - contain the memory information to memory region allocated
* region allocated at default framebuffer creation. * at default framebuffer creation.
*/ */
struct exynos_drm_fb { struct exynos_drm_fb {
struct drm_framebuffer fb; struct drm_framebuffer fb;
struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
}; };
static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
@ -63,8 +65,8 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
* default framebuffer has no gem object so * default framebuffer has no gem object so
* a buffer of the default framebuffer should be released at here. * a buffer of the default framebuffer should be released at here.
*/ */
if (!exynos_fb->exynos_gem_obj && exynos_fb->entry) if (!exynos_fb->exynos_gem_obj && exynos_fb->buffer)
exynos_drm_buf_destroy(fb->dev, exynos_fb->entry); exynos_drm_buf_destroy(fb->dev, exynos_fb->buffer);
kfree(exynos_fb); kfree(exynos_fb);
exynos_fb = NULL; exynos_fb = NULL;
@ -143,29 +145,29 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
*/ */
if (!mode_cmd->handle) { if (!mode_cmd->handle) {
if (!file_priv) { if (!file_priv) {
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
/* /*
* in case that file_priv is NULL, it allocates * in case that file_priv is NULL, it allocates
* only buffer and this buffer would be used * only buffer and this buffer would be used
* for default framebuffer. * for default framebuffer.
*/ */
entry = exynos_drm_buf_create(dev, size); buffer = exynos_drm_buf_create(dev, size);
if (IS_ERR(entry)) { if (IS_ERR(buffer)) {
ret = PTR_ERR(entry); ret = PTR_ERR(buffer);
goto err_buffer; goto err_buffer;
} }
exynos_fb->entry = entry; exynos_fb->buffer = buffer;
DRM_LOG_KMS("default fb: paddr = 0x%lx, size = 0x%x\n", DRM_LOG_KMS("default: dma_addr = 0x%lx, size = 0x%x\n",
(unsigned long)entry->paddr, size); (unsigned long)buffer->dma_addr, size);
goto out; goto out;
} else { } else {
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
size, &mode_cmd->handle,
&mode_cmd->handle); size);
if (IS_ERR(exynos_gem_obj)) { if (IS_ERR(exynos_gem_obj)) {
ret = PTR_ERR(exynos_gem_obj); ret = PTR_ERR(exynos_gem_obj);
goto err_buffer; goto err_buffer;
@ -189,10 +191,10 @@ exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
* so that default framebuffer has no its own gem object, * so that default framebuffer has no its own gem object,
* only its own buffer object. * only its own buffer object.
*/ */
exynos_fb->entry = exynos_gem_obj->entry; exynos_fb->buffer = exynos_gem_obj->buffer;
DRM_LOG_KMS("paddr = 0x%lx, size = 0x%x, gem object = 0x%x\n", DRM_LOG_KMS("dma_addr = 0x%lx, size = 0x%x, gem object = 0x%x\n",
(unsigned long)exynos_fb->entry->paddr, size, (unsigned long)exynos_fb->buffer->dma_addr, size,
(unsigned int)&exynos_gem_obj->base); (unsigned int)&exynos_gem_obj->base);
out: out:
@ -220,26 +222,36 @@ struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
return exynos_drm_fb_init(file_priv, dev, mode_cmd); return exynos_drm_fb_init(file_priv, dev, mode_cmd);
} }
struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb) struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb)
{ {
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
entry = exynos_fb->entry; buffer = exynos_fb->buffer;
if (!entry) if (!buffer)
return NULL; return NULL;
DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n", DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n",
(unsigned long)entry->vaddr, (unsigned long)buffer->kvaddr,
(unsigned long)entry->paddr); (unsigned long)buffer->dma_addr);
return entry; return buffer;
}
static void exynos_drm_output_poll_changed(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
struct drm_fb_helper *fb_helper = private->fb_helper;
if (fb_helper)
drm_fb_helper_hotplug_event(fb_helper);
} }
static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_drm_fb_create, .fb_create = exynos_drm_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
}; };
void exynos_drm_mode_config_init(struct drm_device *dev) void exynos_drm_mode_config_init(struct drm_device *dev)

View File

@ -33,6 +33,7 @@
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_fb.h" #include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h" #include "exynos_drm_buf.h"
#define MAX_CONNECTOR 4 #define MAX_CONNECTOR 4
@ -85,15 +86,13 @@ static struct fb_ops exynos_drm_fb_ops = {
}; };
static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
struct drm_framebuffer *fb, struct drm_framebuffer *fb)
unsigned int fb_width,
unsigned int fb_height)
{ {
struct fb_info *fbi = helper->fbdev; struct fb_info *fbi = helper->fbdev;
struct drm_device *dev = helper->dev; struct drm_device *dev = helper->dev;
struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper); struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper);
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3); unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
unsigned long offset; unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
@ -101,20 +100,20 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
exynos_fb->fb = fb; exynos_fb->fb = fb;
drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height); drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
entry = exynos_drm_fb_get_buf(fb); buffer = exynos_drm_fb_get_buf(fb);
if (!entry) { if (!buffer) {
DRM_LOG_KMS("entry is null.\n"); DRM_LOG_KMS("buffer is null.\n");
return -EFAULT; return -EFAULT;
} }
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3); offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitch; offset += fbi->var.yoffset * fb->pitch;
dev->mode_config.fb_base = entry->paddr; dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
fbi->screen_base = entry->vaddr + offset; fbi->screen_base = buffer->kvaddr + offset;
fbi->fix.smem_start = entry->paddr + offset; fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset);
fbi->screen_size = size; fbi->screen_size = size;
fbi->fix.smem_len = size; fbi->fix.smem_len = size;
@ -171,8 +170,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
goto out; goto out;
} }
ret = exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width, ret = exynos_drm_fbdev_update(helper, helper->fb);
sizes->fb_height);
if (ret < 0) if (ret < 0)
fb_dealloc_cmap(&fbi->cmap); fb_dealloc_cmap(&fbi->cmap);
@ -235,8 +233,7 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
} }
helper->fb = exynos_fbdev->fb; helper->fb = exynos_fbdev->fb;
return exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width, return exynos_drm_fbdev_update(helper, helper->fb);
sizes->fb_height);
} }
static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
@ -405,6 +402,18 @@ int exynos_drm_fbdev_reinit(struct drm_device *dev)
fb_helper = private->fb_helper; fb_helper = private->fb_helper;
if (fb_helper) { if (fb_helper) {
struct list_head temp_list;
INIT_LIST_HEAD(&temp_list);
/*
* fb_helper is reintialized but kernel fb is reused
* so kernel_fb_list need to be backuped and restored
*/
if (!list_empty(&fb_helper->kernel_fb_list))
list_replace_init(&fb_helper->kernel_fb_list,
&temp_list);
drm_fb_helper_fini(fb_helper); drm_fb_helper_fini(fb_helper);
ret = drm_fb_helper_init(dev, fb_helper, ret = drm_fb_helper_init(dev, fb_helper,
@ -414,6 +423,9 @@ int exynos_drm_fbdev_reinit(struct drm_device *dev)
return ret; return ret;
} }
if (!list_empty(&temp_list))
list_replace(&temp_list, &fb_helper->kernel_fb_list);
ret = drm_fb_helper_single_add_all_connectors(fb_helper); ret = drm_fb_helper_single_add_all_connectors(fb_helper);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("failed to add fb helper to connectors\n"); DRM_ERROR("failed to add fb helper to connectors\n");

View File

@ -64,7 +64,7 @@ struct fimd_win_data {
unsigned int fb_width; unsigned int fb_width;
unsigned int fb_height; unsigned int fb_height;
unsigned int bpp; unsigned int bpp;
dma_addr_t paddr; dma_addr_t dma_addr;
void __iomem *vaddr; void __iomem *vaddr;
unsigned int buf_offsize; unsigned int buf_offsize;
unsigned int line_size; /* bytes */ unsigned int line_size; /* bytes */
@ -124,7 +124,7 @@ static int fimd_display_power_on(struct device *dev, int mode)
return 0; return 0;
} }
static struct exynos_drm_display fimd_display = { static struct exynos_drm_display_ops fimd_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_LCD, .type = EXYNOS_DISPLAY_TYPE_LCD,
.is_connected = fimd_display_is_connected, .is_connected = fimd_display_is_connected,
.get_timing = fimd_get_timing, .get_timing = fimd_get_timing,
@ -177,6 +177,40 @@ static void fimd_commit(struct device *dev)
writel(val, ctx->regs + VIDCON0); writel(val, ctx->regs + VIDCON0);
} }
static void fimd_disable(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct drm_device *drm_dev = subdrv->drm_dev;
struct exynos_drm_manager *manager = &subdrv->manager;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* fimd dma off */
val = readl(ctx->regs + VIDCON0);
val &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F);
writel(val, ctx->regs + VIDCON0);
/*
* if vblank is enabled status with dma off then
* it disables vsync interrupt.
*/
if (drm_dev->vblank_enabled[manager->pipe] &&
atomic_read(&drm_dev->vblank_refcount[manager->pipe])) {
drm_vblank_put(drm_dev, manager->pipe);
/*
* if vblank_disable_allowed is 0 then disable
* vsync interrupt right now else the vsync interrupt
* would be disabled by drm timer once a current process
* gives up ownershop of vblank event.
*/
if (!drm_dev->vblank_disable_allowed)
drm_vblank_off(drm_dev, manager->pipe);
}
}
static int fimd_enable_vblank(struct device *dev) static int fimd_enable_vblank(struct device *dev)
{ {
struct fimd_context *ctx = get_fimd_context(dev); struct fimd_context *ctx = get_fimd_context(dev);
@ -220,6 +254,7 @@ static void fimd_disable_vblank(struct device *dev)
static struct exynos_drm_manager_ops fimd_manager_ops = { static struct exynos_drm_manager_ops fimd_manager_ops = {
.commit = fimd_commit, .commit = fimd_commit,
.disable = fimd_disable,
.enable_vblank = fimd_enable_vblank, .enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank, .disable_vblank = fimd_disable_vblank,
}; };
@ -251,7 +286,7 @@ static void fimd_win_mode_set(struct device *dev,
win_data->ovl_height = overlay->crtc_height; win_data->ovl_height = overlay->crtc_height;
win_data->fb_width = overlay->fb_width; win_data->fb_width = overlay->fb_width;
win_data->fb_height = overlay->fb_height; win_data->fb_height = overlay->fb_height;
win_data->paddr = overlay->paddr + offset; win_data->dma_addr = overlay->dma_addr + offset;
win_data->vaddr = overlay->vaddr + offset; win_data->vaddr = overlay->vaddr + offset;
win_data->bpp = overlay->bpp; win_data->bpp = overlay->bpp;
win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
@ -263,7 +298,7 @@ static void fimd_win_mode_set(struct device *dev,
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
win_data->ovl_width, win_data->ovl_height); win_data->ovl_width, win_data->ovl_height);
DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
(unsigned long)win_data->paddr, (unsigned long)win_data->dma_addr,
(unsigned long)win_data->vaddr); (unsigned long)win_data->vaddr);
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
overlay->fb_width, overlay->crtc_width); overlay->fb_width, overlay->crtc_width);
@ -376,16 +411,16 @@ static void fimd_win_commit(struct device *dev)
writel(val, ctx->regs + SHADOWCON); writel(val, ctx->regs + SHADOWCON);
/* buffer start address */ /* buffer start address */
val = win_data->paddr; val = (unsigned long)win_data->dma_addr;
writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */ /* buffer end address */
size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
val = win_data->paddr + size; val = (unsigned long)(win_data->dma_addr + size);
writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
(unsigned long)win_data->paddr, val, size); (unsigned long)win_data->dma_addr, val, size);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
win_data->ovl_width, win_data->ovl_height); win_data->ovl_width, win_data->ovl_height);
@ -447,7 +482,6 @@ static void fimd_win_commit(struct device *dev)
static void fimd_win_disable(struct device *dev) static void fimd_win_disable(struct device *dev)
{ {
struct fimd_context *ctx = get_fimd_context(dev); struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data;
int win = ctx->default_win; int win = ctx->default_win;
u32 val; u32 val;
@ -456,8 +490,6 @@ static void fimd_win_disable(struct device *dev)
if (win < 0 || win > WINDOWS_NR) if (win < 0 || win > WINDOWS_NR)
return; return;
win_data = &ctx->win_data[win];
/* protect windows */ /* protect windows */
val = readl(ctx->regs + SHADOWCON); val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_WINx_PROTECT(win); val |= SHADOWCON_WINx_PROTECT(win);
@ -528,6 +560,16 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
/* VSYNC interrupt */ /* VSYNC interrupt */
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
/*
* in case that vblank_disable_allowed is 1, it could induce
* the problem that manager->pipe could be -1 because with
* disable callback, vsync interrupt isn't disabled and at this moment,
* vsync interrupt could occur. the vsync interrupt would be disabled
* by timer handler later.
*/
if (manager->pipe == -1)
return IRQ_HANDLED;
drm_handle_vblank(drm_dev, manager->pipe); drm_handle_vblank(drm_dev, manager->pipe);
fimd_finish_pageflip(drm_dev, manager->pipe); fimd_finish_pageflip(drm_dev, manager->pipe);
@ -548,13 +590,6 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
*/ */
drm_dev->irq_enabled = 1; drm_dev->irq_enabled = 1;
/*
* with vblank_disable_allowed = 1, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(drm_vblank_put function was called)
*/
drm_dev->vblank_disable_allowed = 1;
return 0; return 0;
} }
@ -731,7 +766,7 @@ static int __devinit fimd_probe(struct platform_device *pdev)
subdrv->manager.pipe = -1; subdrv->manager.pipe = -1;
subdrv->manager.ops = &fimd_manager_ops; subdrv->manager.ops = &fimd_manager_ops;
subdrv->manager.overlay_ops = &fimd_overlay_ops; subdrv->manager.overlay_ops = &fimd_overlay_ops;
subdrv->manager.display = &fimd_display; subdrv->manager.display_ops = &fimd_display_ops;
subdrv->manager.dev = dev; subdrv->manager.dev = dev;
platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx);

View File

@ -62,40 +62,28 @@ static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
} }
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv, static struct exynos_drm_gem_obj
struct drm_device *dev, unsigned int size, *exynos_drm_gem_init(struct drm_device *drm_dev,
unsigned int *handle) struct drm_file *file_priv, unsigned int *handle,
unsigned int size)
{ {
struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_buf_entry *entry;
struct drm_gem_object *obj; struct drm_gem_object *obj;
int ret; int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
size = roundup(size, PAGE_SIZE);
exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
if (!exynos_gem_obj) { if (!exynos_gem_obj) {
DRM_ERROR("failed to allocate exynos gem object.\n"); DRM_ERROR("failed to allocate exynos gem object.\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
/* allocate the new buffer object and memory region. */
entry = exynos_drm_buf_create(dev, size);
if (!entry) {
kfree(exynos_gem_obj);
return ERR_PTR(-ENOMEM);
}
exynos_gem_obj->entry = entry;
obj = &exynos_gem_obj->base; obj = &exynos_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size); ret = drm_gem_object_init(drm_dev, obj, size);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("failed to initailize gem object.\n"); DRM_ERROR("failed to initialize gem object.\n");
goto err_obj_init; ret = -EINVAL;
goto err_object_init;
} }
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
@ -127,24 +115,50 @@ err_handle_create:
err_create_mmap_offset: err_create_mmap_offset:
drm_gem_object_release(obj); drm_gem_object_release(obj);
err_obj_init: err_object_init:
exynos_drm_buf_destroy(dev, exynos_gem_obj->entry);
kfree(exynos_gem_obj); kfree(exynos_gem_obj);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
struct drm_file *file_priv,
unsigned int *handle, unsigned long size)
{
struct exynos_drm_gem_obj *exynos_gem_obj = NULL;
struct exynos_drm_gem_buf *buffer;
size = roundup(size, PAGE_SIZE);
DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
buffer = exynos_drm_buf_create(dev, size);
if (IS_ERR(buffer)) {
return ERR_CAST(buffer);
}
exynos_gem_obj = exynos_drm_gem_init(dev, file_priv, handle, size);
if (IS_ERR(exynos_gem_obj)) {
exynos_drm_buf_destroy(dev, buffer);
return exynos_gem_obj;
}
exynos_gem_obj->buffer = buffer;
return exynos_gem_obj;
}
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
struct drm_exynos_gem_create *args = data; struct drm_exynos_gem_create *args = data;
struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_obj *exynos_gem_obj = NULL;
DRM_DEBUG_KMS("%s : size = 0x%x\n", __FILE__, args->size); DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size, exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
&args->handle); &args->handle, args->size);
if (IS_ERR(exynos_gem_obj)) if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj); return PTR_ERR(exynos_gem_obj);
@ -175,7 +189,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
{ {
struct drm_gem_object *obj = filp->private_data; struct drm_gem_object *obj = filp->private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
unsigned long pfn, vm_size; unsigned long pfn, vm_size;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
@ -187,20 +201,20 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
vm_size = vma->vm_end - vma->vm_start; vm_size = vma->vm_end - vma->vm_start;
/* /*
* a entry contains information to physically continuous memory * a buffer contains information to physically continuous memory
* allocated by user request or at framebuffer creation. * allocated by user request or at framebuffer creation.
*/ */
entry = exynos_gem_obj->entry; buffer = exynos_gem_obj->buffer;
/* check if user-requested size is valid. */ /* check if user-requested size is valid. */
if (vm_size > entry->size) if (vm_size > buffer->size)
return -EINVAL; return -EINVAL;
/* /*
* get page frame number to physical memory to be mapped * get page frame number to physical memory to be mapped
* to user space. * to user space.
*/ */
pfn = exynos_gem_obj->entry->paddr >> PAGE_SHIFT; pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT;
DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
@ -281,7 +295,7 @@ void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj)
exynos_gem_obj = to_exynos_gem_obj(gem_obj); exynos_gem_obj = to_exynos_gem_obj(gem_obj);
exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->entry); exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->buffer);
kfree(exynos_gem_obj); kfree(exynos_gem_obj);
} }
@ -302,8 +316,8 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
args->pitch = args->width * args->bpp >> 3; args->pitch = args->width * args->bpp >> 3;
args->size = args->pitch * args->height; args->size = args->pitch * args->height;
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size, exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, &args->handle,
&args->handle); args->size);
if (IS_ERR(exynos_gem_obj)) if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj); return PTR_ERR(exynos_gem_obj);
@ -360,7 +374,8 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
pfn = (exynos_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset; pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
PAGE_SHIFT) + page_offset;
ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);

View File

@ -29,14 +29,30 @@
#define to_exynos_gem_obj(x) container_of(x,\ #define to_exynos_gem_obj(x) container_of(x,\
struct exynos_drm_gem_obj, base) struct exynos_drm_gem_obj, base)
/*
* exynos drm gem buffer structure.
*
* @kvaddr: kernel virtual address to allocated memory region.
* @dma_addr: bus address(accessed by dma) to allocated memory region.
* - this address could be physical address without IOMMU and
* device address with IOMMU.
* @size: size of allocated memory region.
*/
struct exynos_drm_gem_buf {
void __iomem *kvaddr;
dma_addr_t dma_addr;
unsigned long size;
};
/* /*
* exynos drm buffer structure. * exynos drm buffer structure.
* *
* @base: a gem object. * @base: a gem object.
* - a new handle to this gem object would be created * - a new handle to this gem object would be created
* by drm_gem_handle_create(). * by drm_gem_handle_create().
* @entry: pointer to exynos drm buffer entry object. * @buffer: a pointer to exynos_drm_gem_buffer object.
* - containing the information to physically * - contain the information to memory region allocated
* by user request or at framebuffer creation.
* continuous memory region allocated by user request * continuous memory region allocated by user request
* or at framebuffer creation. * or at framebuffer creation.
* *
@ -45,13 +61,13 @@
*/ */
struct exynos_drm_gem_obj { struct exynos_drm_gem_obj {
struct drm_gem_object base; struct drm_gem_object base;
struct exynos_drm_buf_entry *entry; struct exynos_drm_gem_buf *buffer;
}; };
/* create a new buffer and get a new gem handle. */ /* create a new buffer and get a new gem handle. */
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv, struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
struct drm_device *dev, unsigned int size, struct drm_file *file_priv,
unsigned int *handle); unsigned int *handle, unsigned long size);
/* /*
* request gem object creation and buffer allocation as the size * request gem object creation and buffer allocation as the size

View File

@ -32,17 +32,16 @@
/** /**
* User-desired buffer creation information structure. * User-desired buffer creation information structure.
* *
* @size: requested size for the object. * @size: user-desired memory allocation size.
* - this size value would be page-aligned internally. * - this size value would be page-aligned internally.
* @flags: user request for setting memory type or cache attributes. * @flags: user request for setting memory type or cache attributes.
* @handle: returned handle for the object. * @handle: returned a handle to created gem object.
* @pad: just padding to be 64-bit aligned. * - this handle will be set by gem module of kernel side.
*/ */
struct drm_exynos_gem_create { struct drm_exynos_gem_create {
unsigned int size; uint64_t size;
unsigned int flags; unsigned int flags;
unsigned int handle; unsigned int handle;
unsigned int pad;
}; };
/** /**