mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-26 16:30:55 +07:00
Merge branch 'exynos-drm' of git://git.infradead.org/users/kmpark/linux-samsung into drm-fixes
* 'exynos-drm' of git://git.infradead.org/users/kmpark/linux-samsung: 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:
commit
b7b996da27
@ -27,82 +27,84 @@
|
||||
#include "drm.h"
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
|
||||
static DEFINE_MUTEX(exynos_drm_buf_lock);
|
||||
|
||||
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__);
|
||||
|
||||
entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size,
|
||||
(dma_addr_t *)&entry->paddr, GFP_KERNEL);
|
||||
if (!entry->paddr) {
|
||||
buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size,
|
||||
&buffer->dma_addr, GFP_KERNEL);
|
||||
if (!buffer->kvaddr) {
|
||||
DRM_ERROR("failed to allocate buffer.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
|
||||
(unsigned int)entry->vaddr, entry->paddr, entry->size);
|
||||
DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
|
||||
(unsigned long)buffer->kvaddr,
|
||||
(unsigned long)buffer->dma_addr,
|
||||
buffer->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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__);
|
||||
|
||||
if (entry->paddr && entry->vaddr && entry->size)
|
||||
dma_free_writecombine(dev->dev, entry->size, entry->vaddr,
|
||||
entry->paddr);
|
||||
if (buffer->dma_addr && buffer->size)
|
||||
dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr,
|
||||
(dma_addr_t)buffer->dma_addr);
|
||||
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)
|
||||
{
|
||||
struct exynos_drm_buf_entry *entry;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
|
||||
DRM_DEBUG_KMS("%s.\n", __FILE__);
|
||||
DRM_DEBUG_KMS("desired size = 0x%x\n", size);
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n");
|
||||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
entry->size = size;
|
||||
buffer->size = size;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
kfree(entry);
|
||||
entry = NULL;
|
||||
if (lowlevel_buffer_allocate(dev, buffer) < 0) {
|
||||
kfree(buffer);
|
||||
buffer = NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return entry;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
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__);
|
||||
|
||||
if (!entry) {
|
||||
DRM_DEBUG_KMS("entry is null.\n");
|
||||
if (!buffer) {
|
||||
DRM_DEBUG_KMS("buffer is null.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
lowlevel_buffer_deallocate(dev, entry);
|
||||
lowlevel_buffer_deallocate(dev, buffer);
|
||||
|
||||
kfree(entry);
|
||||
entry = NULL;
|
||||
kfree(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
|
||||
|
@ -26,28 +26,15 @@
|
||||
#ifndef _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. */
|
||||
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);
|
||||
|
||||
/* get physical memory information of a drm framebuffer. */
|
||||
struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);
|
||||
/* get memory information of a drm framebuffer. */
|
||||
struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);
|
||||
|
||||
/* remove allocated physical memory. */
|
||||
void exynos_drm_buf_destroy(struct drm_device *dev,
|
||||
struct exynos_drm_buf_entry *entry);
|
||||
struct exynos_drm_gem_buf *buffer);
|
||||
|
||||
#endif
|
||||
|
@ -37,6 +37,8 @@
|
||||
|
||||
struct exynos_drm_connector {
|
||||
struct drm_connector drm_connector;
|
||||
uint32_t encoder_id;
|
||||
struct exynos_drm_manager *manager;
|
||||
};
|
||||
|
||||
/* 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__);
|
||||
|
||||
mode->clock = timing->pixclock / 1000;
|
||||
mode->vrefresh = timing->refresh;
|
||||
|
||||
mode->hdisplay = timing->xres;
|
||||
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_end = mode->vsync_start + timing->vsync_len;
|
||||
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 */
|
||||
@ -69,7 +78,7 @@ convert_to_video_timing(struct fb_videomode *timing,
|
||||
memset(timing, 0, sizeof(*timing));
|
||||
|
||||
timing->pixclock = mode->clock * 1000;
|
||||
timing->refresh = mode->vrefresh;
|
||||
timing->refresh = drm_mode_vrefresh(mode);
|
||||
|
||||
timing->xres = 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)
|
||||
{
|
||||
struct exynos_drm_manager *manager =
|
||||
exynos_drm_get_manager(connector->encoder);
|
||||
struct exynos_drm_display *display = manager->display;
|
||||
struct exynos_drm_connector *exynos_connector =
|
||||
to_exynos_connector(connector);
|
||||
struct exynos_drm_manager *manager = exynos_connector->manager;
|
||||
struct exynos_drm_display_ops *display_ops = manager->display_ops;
|
||||
unsigned int count;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (!display) {
|
||||
DRM_DEBUG_KMS("display is null.\n");
|
||||
if (!display_ops) {
|
||||
DRM_DEBUG_KMS("display_ops is null.\n");
|
||||
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
|
||||
* because lcd panel has only one mode.
|
||||
*/
|
||||
if (display->get_edid) {
|
||||
if (display_ops->get_edid) {
|
||||
int ret;
|
||||
void *edid;
|
||||
|
||||
@ -122,7 +132,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = display->get_edid(manager->dev, connector,
|
||||
ret = display_ops->get_edid(manager->dev, connector,
|
||||
edid, MAX_EDID);
|
||||
if (ret < 0) {
|
||||
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 fb_videomode *timing;
|
||||
|
||||
if (display->get_timing)
|
||||
timing = display->get_timing(manager->dev);
|
||||
if (display_ops->get_timing)
|
||||
timing = display_ops->get_timing(manager->dev);
|
||||
else {
|
||||
drm_mode_destroy(connector->dev, mode);
|
||||
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,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct exynos_drm_manager *manager =
|
||||
exynos_drm_get_manager(connector->encoder);
|
||||
struct exynos_drm_display *display = manager->display;
|
||||
struct exynos_drm_connector *exynos_connector =
|
||||
to_exynos_connector(connector);
|
||||
struct exynos_drm_manager *manager = exynos_connector->manager;
|
||||
struct exynos_drm_display_ops *display_ops = manager->display_ops;
|
||||
struct fb_videomode timing;
|
||||
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);
|
||||
|
||||
if (display && display->check_timing)
|
||||
if (!display->check_timing(manager->dev, (void *)&timing))
|
||||
if (display_ops && display_ops->check_timing)
|
||||
if (!display_ops->check_timing(manager->dev, (void *)&timing))
|
||||
ret = MODE_OK;
|
||||
|
||||
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_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__);
|
||||
|
||||
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 = {
|
||||
@ -196,15 +223,17 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
|
||||
static enum drm_connector_status
|
||||
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct exynos_drm_manager *manager =
|
||||
exynos_drm_get_manager(connector->encoder);
|
||||
struct exynos_drm_display *display = manager->display;
|
||||
struct exynos_drm_connector *exynos_connector =
|
||||
to_exynos_connector(connector);
|
||||
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;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (display && display->is_connected) {
|
||||
if (display->is_connected(manager->dev))
|
||||
if (display_ops && display_ops->is_connected) {
|
||||
if (display_ops->is_connected(manager->dev))
|
||||
status = connector_status_connected;
|
||||
else
|
||||
status = connector_status_disconnected;
|
||||
@ -251,9 +280,11 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
|
||||
|
||||
connector = &exynos_connector->drm_connector;
|
||||
|
||||
switch (manager->display->type) {
|
||||
switch (manager->display_ops->type) {
|
||||
case EXYNOS_DISPLAY_TYPE_HDMI:
|
||||
type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
connector->interlace_allowed = true;
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
break;
|
||||
default:
|
||||
type = DRM_MODE_CONNECTOR_Unknown;
|
||||
@ -267,7 +298,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
|
||||
if (err)
|
||||
goto err_connector;
|
||||
|
||||
exynos_connector->encoder_id = encoder->base.id;
|
||||
exynos_connector->manager = manager;
|
||||
connector->encoder = encoder;
|
||||
|
||||
err = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (err) {
|
||||
DRM_ERROR("failed to attach a connector to a encoder\n");
|
||||
|
@ -29,35 +29,16 @@
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_encoder.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
|
||||
#define to_exynos_crtc(x) container_of(x, struct exynos_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.
|
||||
*
|
||||
@ -85,30 +66,31 @@ static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
|
||||
|
||||
exynos_drm_fn_encoder(crtc, overlay,
|
||||
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,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_display_mode *mode,
|
||||
struct exynos_drm_crtc_pos *pos)
|
||||
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)
|
||||
{
|
||||
struct exynos_drm_buf_entry *entry;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
unsigned int actual_w;
|
||||
unsigned int actual_h;
|
||||
|
||||
entry = exynos_drm_fb_get_buf(fb);
|
||||
if (!entry) {
|
||||
DRM_LOG_KMS("entry is null.\n");
|
||||
buffer = exynos_drm_fb_get_buf(fb);
|
||||
if (!buffer) {
|
||||
DRM_LOG_KMS("buffer is null.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
overlay->paddr = entry->paddr;
|
||||
overlay->vaddr = entry->vaddr;
|
||||
overlay->dma_addr = buffer->dma_addr;
|
||||
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->paddr);
|
||||
(unsigned long)overlay->dma_addr);
|
||||
|
||||
actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -185,9 +184,12 @@ static void exynos_drm_crtc_prepare(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 framework doesn't check NULL. */
|
||||
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
|
||||
exynos_drm_encoder_crtc_commit);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -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);
|
||||
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
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm.h"
|
||||
#include "drm_crtc_helper.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);
|
||||
|
||||
/* init kms poll for handling hpd */
|
||||
drm_kms_helper_poll_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_device_unregister(dev);
|
||||
drm_vblank_cleanup(dev);
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
drm_mode_config_cleanup(dev);
|
||||
kfree(dev->dev_private);
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#ifndef _EXYNOS_DRM_DRV_H_
|
||||
#define _EXYNOS_DRM_DRV_H_
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "drm.h"
|
||||
|
||||
#define MAX_CRTC 2
|
||||
@ -79,8 +80,8 @@ struct exynos_drm_overlay_ops {
|
||||
* @scan_flag: interlace or progressive way.
|
||||
* (it could be DRM_MODE_FLAG_*)
|
||||
* @bpp: pixel size.(in bit)
|
||||
* @paddr: bus(accessed by dma) physical memory address to this overlay
|
||||
* and this is physically continuous.
|
||||
* @dma_addr: bus(accessed by dma) address to the memory region allocated
|
||||
* for a overlay.
|
||||
* @vaddr: virtual memory addresss to this overlay.
|
||||
* @default_win: a window to be enabled.
|
||||
* @color_key: color key on or off.
|
||||
@ -108,7 +109,7 @@ struct exynos_drm_overlay {
|
||||
unsigned int scan_flag;
|
||||
unsigned int bpp;
|
||||
unsigned int pitch;
|
||||
dma_addr_t paddr;
|
||||
dma_addr_t dma_addr;
|
||||
void __iomem *vaddr;
|
||||
|
||||
bool default_win;
|
||||
@ -130,7 +131,7 @@ struct exynos_drm_overlay {
|
||||
* @check_timing: check if timing is valid or not.
|
||||
* @power_on: display device on or off.
|
||||
*/
|
||||
struct exynos_drm_display {
|
||||
struct exynos_drm_display_ops {
|
||||
enum exynos_drm_output_type type;
|
||||
bool (*is_connected)(struct device *dev);
|
||||
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
|
||||
* would be called by encoder->mode_set().
|
||||
* @commit: set current hw specific display mode to hw.
|
||||
* @disable: disable hardware specific display mode.
|
||||
* @enable_vblank: specific driver callback for enabling vblank interrupt.
|
||||
* @disable_vblank: specific driver callback for disabling vblank interrupt.
|
||||
*/
|
||||
struct exynos_drm_manager_ops {
|
||||
void (*mode_set)(struct device *subdrv_dev, void *mode);
|
||||
void (*commit)(struct device *subdrv_dev);
|
||||
void (*disable)(struct device *subdrv_dev);
|
||||
int (*enable_vblank)(struct device *subdrv_dev);
|
||||
void (*disable_vblank)(struct device *subdrv_dev);
|
||||
};
|
||||
@ -178,7 +181,7 @@ struct exynos_drm_manager {
|
||||
int pipe;
|
||||
struct exynos_drm_manager_ops *ops;
|
||||
struct exynos_drm_overlay_ops *overlay_ops;
|
||||
struct exynos_drm_display *display;
|
||||
struct exynos_drm_display_ops *display_ops;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -53,15 +53,36 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_connector *connector;
|
||||
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);
|
||||
|
||||
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) {
|
||||
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)
|
||||
display->power_on(manager->dev, mode);
|
||||
DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
|
||||
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_ops *manager_ops = manager->ops;
|
||||
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (manager_ops && manager_ops->commit)
|
||||
manager_ops->commit(manager->dev);
|
||||
|
||||
if (overlay_ops && overlay_ops->commit)
|
||||
overlay_ops->commit(manager->dev);
|
||||
}
|
||||
|
||||
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_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) {
|
||||
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);
|
||||
}
|
||||
@ -250,8 +280,18 @@ void exynos_drm_encoder_crtc_commit(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;
|
||||
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)
|
||||
@ -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 *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>");
|
||||
|
@ -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_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_disable(struct drm_encoder *encoder, void *data);
|
||||
|
||||
#endif
|
||||
|
@ -29,7 +29,9 @@
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "drm_fb_helper.h"
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
@ -41,14 +43,14 @@
|
||||
*
|
||||
* @fb: drm framebuffer obejct.
|
||||
* @exynos_gem_obj: exynos specific gem object containing a gem object.
|
||||
* @entry: pointer to exynos drm buffer entry object.
|
||||
* - containing only the information to physically continuous memory
|
||||
* region allocated at default framebuffer creation.
|
||||
* @buffer: pointer to exynos_drm_gem_buffer object.
|
||||
* - contain the memory information to memory region allocated
|
||||
* at default framebuffer creation.
|
||||
*/
|
||||
struct exynos_drm_fb {
|
||||
struct drm_framebuffer fb;
|
||||
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)
|
||||
@ -63,8 +65,8 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
|
||||
* default framebuffer has no gem object so
|
||||
* a buffer of the default framebuffer should be released at here.
|
||||
*/
|
||||
if (!exynos_fb->exynos_gem_obj && exynos_fb->entry)
|
||||
exynos_drm_buf_destroy(fb->dev, exynos_fb->entry);
|
||||
if (!exynos_fb->exynos_gem_obj && exynos_fb->buffer)
|
||||
exynos_drm_buf_destroy(fb->dev, exynos_fb->buffer);
|
||||
|
||||
kfree(exynos_fb);
|
||||
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 (!file_priv) {
|
||||
struct exynos_drm_buf_entry *entry;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
|
||||
/*
|
||||
* in case that file_priv is NULL, it allocates
|
||||
* only buffer and this buffer would be used
|
||||
* for default framebuffer.
|
||||
*/
|
||||
entry = exynos_drm_buf_create(dev, size);
|
||||
if (IS_ERR(entry)) {
|
||||
ret = PTR_ERR(entry);
|
||||
buffer = exynos_drm_buf_create(dev, size);
|
||||
if (IS_ERR(buffer)) {
|
||||
ret = PTR_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",
|
||||
(unsigned long)entry->paddr, size);
|
||||
DRM_LOG_KMS("default: dma_addr = 0x%lx, size = 0x%x\n",
|
||||
(unsigned long)buffer->dma_addr, size);
|
||||
|
||||
goto out;
|
||||
} else {
|
||||
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev,
|
||||
size,
|
||||
&mode_cmd->handle);
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
|
||||
&mode_cmd->handle,
|
||||
size);
|
||||
if (IS_ERR(exynos_gem_obj)) {
|
||||
ret = PTR_ERR(exynos_gem_obj);
|
||||
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,
|
||||
* 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",
|
||||
(unsigned long)exynos_fb->entry->paddr, size,
|
||||
DRM_LOG_KMS("dma_addr = 0x%lx, size = 0x%x, gem object = 0x%x\n",
|
||||
(unsigned long)exynos_fb->buffer->dma_addr, size,
|
||||
(unsigned int)&exynos_gem_obj->base);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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_buf_entry *entry;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
entry = exynos_fb->entry;
|
||||
if (!entry)
|
||||
buffer = exynos_fb->buffer;
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n",
|
||||
(unsigned long)entry->vaddr,
|
||||
(unsigned long)entry->paddr);
|
||||
DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n",
|
||||
(unsigned long)buffer->kvaddr,
|
||||
(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 = {
|
||||
.fb_create = exynos_drm_fb_create,
|
||||
.output_poll_changed = exynos_drm_output_poll_changed,
|
||||
};
|
||||
|
||||
void exynos_drm_mode_config_init(struct drm_device *dev)
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
|
||||
#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,
|
||||
struct drm_framebuffer *fb,
|
||||
unsigned int fb_width,
|
||||
unsigned int fb_height)
|
||||
struct drm_framebuffer *fb)
|
||||
{
|
||||
struct fb_info *fbi = helper->fbdev;
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper);
|
||||
struct exynos_drm_buf_entry *entry;
|
||||
unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
|
||||
unsigned long offset;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
if (!entry) {
|
||||
DRM_LOG_KMS("entry is null.\n");
|
||||
buffer = exynos_drm_fb_get_buf(fb);
|
||||
if (!buffer) {
|
||||
DRM_LOG_KMS("buffer is null.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
|
||||
offset += fbi->var.yoffset * fb->pitch;
|
||||
|
||||
dev->mode_config.fb_base = entry->paddr;
|
||||
fbi->screen_base = entry->vaddr + offset;
|
||||
fbi->fix.smem_start = entry->paddr + offset;
|
||||
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
|
||||
fbi->screen_base = buffer->kvaddr + offset;
|
||||
fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset);
|
||||
fbi->screen_size = size;
|
||||
fbi->fix.smem_len = size;
|
||||
|
||||
@ -171,8 +170,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
|
||||
sizes->fb_height);
|
||||
ret = exynos_drm_fbdev_update(helper, helper->fb);
|
||||
if (ret < 0)
|
||||
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;
|
||||
return exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
|
||||
sizes->fb_height);
|
||||
return exynos_drm_fbdev_update(helper, helper->fb);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
ret = drm_fb_helper_init(dev, fb_helper,
|
||||
@ -414,6 +423,9 @@ int exynos_drm_fbdev_reinit(struct drm_device *dev)
|
||||
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);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to add fb helper to connectors\n");
|
||||
|
@ -64,7 +64,7 @@ struct fimd_win_data {
|
||||
unsigned int fb_width;
|
||||
unsigned int fb_height;
|
||||
unsigned int bpp;
|
||||
dma_addr_t paddr;
|
||||
dma_addr_t dma_addr;
|
||||
void __iomem *vaddr;
|
||||
unsigned int buf_offsize;
|
||||
unsigned int line_size; /* bytes */
|
||||
@ -124,7 +124,7 @@ static int fimd_display_power_on(struct device *dev, int mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct exynos_drm_display fimd_display = {
|
||||
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,
|
||||
@ -177,6 +177,40 @@ static void fimd_commit(struct device *dev)
|
||||
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)
|
||||
{
|
||||
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 = {
|
||||
.commit = fimd_commit,
|
||||
.disable = fimd_disable,
|
||||
.enable_vblank = fimd_enable_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->fb_width = overlay->fb_width;
|
||||
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->bpp = overlay->bpp;
|
||||
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",
|
||||
win_data->ovl_width, win_data->ovl_height);
|
||||
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);
|
||||
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
|
||||
overlay->fb_width, overlay->crtc_width);
|
||||
@ -376,16 +411,16 @@ static void fimd_win_commit(struct device *dev)
|
||||
writel(val, ctx->regs + SHADOWCON);
|
||||
|
||||
/* buffer start address */
|
||||
val = win_data->paddr;
|
||||
val = (unsigned long)win_data->dma_addr;
|
||||
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
|
||||
|
||||
/* buffer end address */
|
||||
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));
|
||||
|
||||
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",
|
||||
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)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
struct fimd_win_data *win_data;
|
||||
int win = ctx->default_win;
|
||||
u32 val;
|
||||
|
||||
@ -456,8 +490,6 @@ static void fimd_win_disable(struct device *dev)
|
||||
if (win < 0 || win > WINDOWS_NR)
|
||||
return;
|
||||
|
||||
win_data = &ctx->win_data[win];
|
||||
|
||||
/* protect windows */
|
||||
val = readl(ctx->regs + SHADOWCON);
|
||||
val |= SHADOWCON_WINx_PROTECT(win);
|
||||
@ -528,6 +560,16 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
|
||||
/* VSYNC interrupt */
|
||||
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);
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@ -731,7 +766,7 @@ static int __devinit fimd_probe(struct platform_device *pdev)
|
||||
subdrv->manager.pipe = -1;
|
||||
subdrv->manager.ops = &fimd_manager_ops;
|
||||
subdrv->manager.overlay_ops = &fimd_overlay_ops;
|
||||
subdrv->manager.display = &fimd_display;
|
||||
subdrv->manager.display_ops = &fimd_display_ops;
|
||||
subdrv->manager.dev = dev;
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev, unsigned int size,
|
||||
unsigned int *handle)
|
||||
static struct exynos_drm_gem_obj
|
||||
*exynos_drm_gem_init(struct drm_device *drm_dev,
|
||||
struct drm_file *file_priv, unsigned int *handle,
|
||||
unsigned int size)
|
||||
{
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct exynos_drm_buf_entry *entry;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
size = roundup(size, PAGE_SIZE);
|
||||
|
||||
exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
|
||||
if (!exynos_gem_obj) {
|
||||
DRM_ERROR("failed to allocate exynos gem object.\n");
|
||||
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;
|
||||
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
ret = drm_gem_object_init(drm_dev, obj, size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initailize gem object.\n");
|
||||
goto err_obj_init;
|
||||
DRM_ERROR("failed to initialize gem object.\n");
|
||||
ret = -EINVAL;
|
||||
goto err_object_init;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
|
||||
@ -127,24 +115,50 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
|
||||
err_create_mmap_offset:
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
err_obj_init:
|
||||
exynos_drm_buf_destroy(dev, exynos_gem_obj->entry);
|
||||
|
||||
err_object_init:
|
||||
kfree(exynos_gem_obj);
|
||||
|
||||
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,
|
||||
struct drm_file *file_priv)
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
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,
|
||||
&args->handle);
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
|
||||
&args->handle, args->size);
|
||||
if (IS_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 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;
|
||||
|
||||
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;
|
||||
/*
|
||||
* a entry contains information to physically continuous memory
|
||||
* a buffer contains information to physically continuous memory
|
||||
* 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. */
|
||||
if (vm_size > entry->size)
|
||||
if (vm_size > buffer->size)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* get page frame number to physical memory to be mapped
|
||||
* 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);
|
||||
|
||||
@ -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_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);
|
||||
}
|
||||
@ -302,8 +316,8 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
args->pitch = args->width * args->bpp >> 3;
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size,
|
||||
&args->handle);
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, &args->handle,
|
||||
args->size);
|
||||
if (IS_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);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -29,14 +29,30 @@
|
||||
#define to_exynos_gem_obj(x) container_of(x,\
|
||||
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.
|
||||
*
|
||||
* @base: a gem object.
|
||||
* - a new handle to this gem object would be created
|
||||
* by drm_gem_handle_create().
|
||||
* @entry: pointer to exynos drm buffer entry object.
|
||||
* - containing the information to physically
|
||||
* @buffer: a pointer to exynos_drm_gem_buffer object.
|
||||
* - contain the information to memory region allocated
|
||||
* by user request or at framebuffer creation.
|
||||
* continuous memory region allocated by user request
|
||||
* or at framebuffer creation.
|
||||
*
|
||||
@ -45,13 +61,13 @@
|
||||
*/
|
||||
struct exynos_drm_gem_obj {
|
||||
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. */
|
||||
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev, unsigned int size,
|
||||
unsigned int *handle);
|
||||
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle, unsigned long size);
|
||||
|
||||
/*
|
||||
* request gem object creation and buffer allocation as the size
|
||||
|
@ -32,17 +32,16 @@
|
||||
/**
|
||||
* 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.
|
||||
* @flags: user request for setting memory type or cache attributes.
|
||||
* @handle: returned handle for the object.
|
||||
* @pad: just padding to be 64-bit aligned.
|
||||
* @handle: returned a handle to created gem object.
|
||||
* - this handle will be set by gem module of kernel side.
|
||||
*/
|
||||
struct drm_exynos_gem_create {
|
||||
unsigned int size;
|
||||
uint64_t size;
|
||||
unsigned int flags;
|
||||
unsigned int handle;
|
||||
unsigned int pad;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user