提交 c976cb37 编写于 作者: D Dave Airlie

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

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

The summary:
. Add display mode check operaion to mixer driver
  - Mixer IP also can put certain restrictions on the proposed
    display modes and these restrictions need to be considered
    during mode negotiation, which happens immediately after
    edid parsing.
. Set correct mode for range of resolutions
  - With this patch, the mixer driver could find the correct mode
    for the range of resolutions upto 1080 vertical lines.
. Support extra resolution for hdmi
  - This patch programs the core and timing generator registers
    using the timing data provided in drm_display_mode without
    hard-coded configurations. So this patch adds additional PHY
    configs to allow us to support more permissible resolutions
    and refresh rates.
. Add device tree support for g2d
  - This patch adds just the compatible string for exynos5250 SoC
    so that with device tree enabling, this driver can be probed.
. And bug fixes and code cleanups.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
  drm/exynos: Add device tree based discovery support for G2D
  drm/exynos: hdmi: support extra resolutions using drm_display_mode timings
  drm/exynos: mixer: set correct mode for range of resolutions
  drm/exynos: implement display-mode-check callback in mixer driver
  drm/exynos: add display-mode-check operation to exynos_mixer_ops struct
  drm/exynos: release resources properly when fb creation is failed.
  drm/exynos: fix wrong pointer access at vm close.
  drm/exynos: Add missing braces around sizeof
  drm/exynos: consider exception case to fb handle creation
  drm/exynos: fix iommu address allocation order
...@@ -99,6 +99,10 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, ...@@ -99,6 +99,10 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
/* This fb should have only one gem object. */
if (WARN_ON(exynos_fb->buf_cnt != 1))
return -EINVAL;
return drm_gem_handle_create(file_priv, return drm_gem_handle_create(file_priv,
&exynos_fb->exynos_gem_obj[0]->base, handle); &exynos_fb->exynos_gem_obj[0]->base, handle);
} }
...@@ -217,23 +221,25 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, ...@@ -217,23 +221,25 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd) struct drm_mode_fb_cmd2 *mode_cmd)
{ {
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_fb *exynos_fb; struct exynos_drm_fb *exynos_fb;
int i, ret; int i, ret;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
return ERR_PTR(-ENOENT);
}
exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
if (!exynos_fb) { if (!exynos_fb) {
DRM_ERROR("failed to allocate exynos drm framebuffer\n"); DRM_ERROR("failed to allocate exynos drm framebuffer\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
ret = -ENOENT;
goto err_free;
}
drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj); exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj);
exynos_fb->buf_cnt = exynos_drm_format_num_buffers(mode_cmd); exynos_fb->buf_cnt = exynos_drm_format_num_buffers(mode_cmd);
...@@ -241,43 +247,44 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, ...@@ -241,43 +247,44 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt); DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt);
for (i = 1; i < exynos_fb->buf_cnt; i++) { for (i = 1; i < exynos_fb->buf_cnt; i++) {
struct exynos_drm_gem_obj *exynos_gem_obj;
int ret;
obj = drm_gem_object_lookup(dev, file_priv, obj = drm_gem_object_lookup(dev, file_priv,
mode_cmd->handles[i]); mode_cmd->handles[i]);
if (!obj) { if (!obj) {
DRM_ERROR("failed to lookup gem object\n"); DRM_ERROR("failed to lookup gem object\n");
kfree(exynos_fb); ret = -ENOENT;
return ERR_PTR(-ENOENT); exynos_fb->buf_cnt = i;
goto err_unreference;
} }
exynos_gem_obj = to_exynos_gem_obj(obj); exynos_gem_obj = to_exynos_gem_obj(obj);
exynos_fb->exynos_gem_obj[i] = exynos_gem_obj;
ret = check_fb_gem_memory_type(dev, exynos_gem_obj); ret = check_fb_gem_memory_type(dev, exynos_gem_obj);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("cannot use this gem memory type for fb.\n"); DRM_ERROR("cannot use this gem memory type for fb.\n");
kfree(exynos_fb); goto err_unreference;
return ERR_PTR(ret);
} }
exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj);
} }
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
if (ret) { if (ret) {
for (i = 0; i < exynos_fb->buf_cnt; i++) { DRM_ERROR("failed to init framebuffer.\n");
struct exynos_drm_gem_obj *gem_obj; goto err_unreference;
gem_obj = exynos_fb->exynos_gem_obj[i];
drm_gem_object_unreference_unlocked(&gem_obj->base);
}
kfree(exynos_fb);
return ERR_PTR(ret);
} }
return &exynos_fb->fb; return &exynos_fb->fb;
err_unreference:
for (i = 0; i < exynos_fb->buf_cnt; i++) {
struct drm_gem_object *obj;
obj = &exynos_fb->exynos_gem_obj[i]->base;
if (obj)
drm_gem_object_unreference_unlocked(obj);
}
err_free:
kfree(exynos_fb);
return ERR_PTR(ret);
} }
struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dma-attrs.h> #include <linux/dma-attrs.h>
#include <linux/of.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -429,7 +430,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, ...@@ -429,7 +430,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
g2d_userptr->pages = pages; g2d_userptr->pages = pages;
sgt = kzalloc(sizeof *sgt, GFP_KERNEL); sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt) { if (!sgt) {
DRM_ERROR("failed to allocate sg table.\n"); DRM_ERROR("failed to allocate sg table.\n");
ret = -ENOMEM; ret = -ENOMEM;
...@@ -1240,6 +1241,14 @@ static int g2d_resume(struct device *dev) ...@@ -1240,6 +1241,14 @@ static int g2d_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume); static SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume);
#ifdef CONFIG_OF
static const struct of_device_id exynos_g2d_match[] = {
{ .compatible = "samsung,exynos5250-g2d" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_g2d_match);
#endif
struct platform_driver g2d_driver = { struct platform_driver g2d_driver = {
.probe = g2d_probe, .probe = g2d_probe,
.remove = g2d_remove, .remove = g2d_remove,
...@@ -1247,5 +1256,6 @@ struct platform_driver g2d_driver = { ...@@ -1247,5 +1256,6 @@ struct platform_driver g2d_driver = {
.name = "s5p-g2d", .name = "s5p-g2d",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &g2d_pm_ops, .pm = &g2d_pm_ops,
.of_match_table = of_match_ptr(exynos_g2d_match),
}, },
}; };
...@@ -329,17 +329,11 @@ static struct drm_file *exynos_drm_find_drm_file(struct drm_device *drm_dev, ...@@ -329,17 +329,11 @@ static struct drm_file *exynos_drm_find_drm_file(struct drm_device *drm_dev,
{ {
struct drm_file *file_priv; struct drm_file *file_priv;
mutex_lock(&drm_dev->struct_mutex);
/* find current process's drm_file from filelist. */ /* find current process's drm_file from filelist. */
list_for_each_entry(file_priv, &drm_dev->filelist, lhead) { list_for_each_entry(file_priv, &drm_dev->filelist, lhead)
if (file_priv->filp == filp) { if (file_priv->filp == filp)
mutex_unlock(&drm_dev->struct_mutex);
return file_priv; return file_priv;
}
}
mutex_unlock(&drm_dev->struct_mutex);
WARN_ON(1); WARN_ON(1);
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
...@@ -400,9 +394,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, ...@@ -400,9 +394,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
*/ */
drm_gem_object_reference(obj); drm_gem_object_reference(obj);
mutex_lock(&drm_dev->struct_mutex);
drm_vm_open_locked(drm_dev, vma); drm_vm_open_locked(drm_dev, vma);
mutex_unlock(&drm_dev->struct_mutex);
return 0; return 0;
} }
...@@ -431,6 +423,16 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, ...@@ -431,6 +423,16 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
return -EINVAL; return -EINVAL;
} }
/*
* We have to use gem object and its fops for specific mmaper,
* but vm_mmap() can deliver only filp. So we have to change
* filp->f_op and filp->private_data temporarily, then restore
* again. So it is important to keep lock until restoration the
* settings to prevent others from misuse of filp->f_op or
* filp->private_data.
*/
mutex_lock(&dev->struct_mutex);
/* /*
* Set specific mmper's fops. And it will be restored by * Set specific mmper's fops. And it will be restored by
* exynos_drm_gem_mmap_buffer to dev->driver->fops. * exynos_drm_gem_mmap_buffer to dev->driver->fops.
...@@ -448,13 +450,20 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, ...@@ -448,13 +450,20 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
addr = vm_mmap(file_priv->filp, 0, args->size, addr = vm_mmap(file_priv->filp, 0, args->size,
PROT_READ | PROT_WRITE, MAP_SHARED, 0); PROT_READ | PROT_WRITE, MAP_SHARED, 0);
drm_gem_object_unreference_unlocked(obj); drm_gem_object_unreference(obj);
if (IS_ERR((void *)addr)) { if (IS_ERR((void *)addr)) {
file_priv->filp->private_data = file_priv; /* check filp->f_op, filp->private_data are restored */
if (file_priv->filp->f_op == &exynos_drm_gem_fops) {
file_priv->filp->f_op = fops_get(dev->driver->fops);
file_priv->filp->private_data = file_priv;
}
mutex_unlock(&dev->struct_mutex);
return PTR_ERR((void *)addr); return PTR_ERR((void *)addr);
} }
mutex_unlock(&dev->struct_mutex);
args->mapped = addr; args->mapped = addr;
DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
......
...@@ -124,9 +124,21 @@ static struct edid *drm_hdmi_get_edid(struct device *dev, ...@@ -124,9 +124,21 @@ static struct edid *drm_hdmi_get_edid(struct device *dev,
static int drm_hdmi_check_timing(struct device *dev, void *timing) static int drm_hdmi_check_timing(struct device *dev, void *timing)
{ {
struct drm_hdmi_context *ctx = to_context(dev); struct drm_hdmi_context *ctx = to_context(dev);
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__); DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* Both, mixer and hdmi should be able to handle the requested mode.
* If any of the two fails, return mode as BAD.
*/
if (mixer_ops && mixer_ops->check_timing)
ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing);
if (ret)
return ret;
if (hdmi_ops && hdmi_ops->check_timing) if (hdmi_ops && hdmi_ops->check_timing)
return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing); return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
......
...@@ -32,7 +32,7 @@ struct exynos_hdmi_ops { ...@@ -32,7 +32,7 @@ struct exynos_hdmi_ops {
bool (*is_connected)(void *ctx); bool (*is_connected)(void *ctx);
struct edid *(*get_edid)(void *ctx, struct edid *(*get_edid)(void *ctx,
struct drm_connector *connector); struct drm_connector *connector);
int (*check_timing)(void *ctx, void *timing); int (*check_timing)(void *ctx, struct fb_videomode *timing);
int (*power_on)(void *ctx, int mode); int (*power_on)(void *ctx, int mode);
/* manager */ /* manager */
...@@ -58,6 +58,9 @@ struct exynos_mixer_ops { ...@@ -58,6 +58,9 @@ struct exynos_mixer_ops {
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay); void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
void (*win_commit)(void *ctx, int zpos); void (*win_commit)(void *ctx, int zpos);
void (*win_disable)(void *ctx, int zpos); void (*win_disable)(void *ctx, int zpos);
/* display */
int (*check_timing)(void *ctx, struct fb_videomode *timing);
}; };
void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx); void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx);
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#define EXYNOS_DEV_ADDR_START 0x20000000 #define EXYNOS_DEV_ADDR_START 0x20000000
#define EXYNOS_DEV_ADDR_SIZE 0x40000000 #define EXYNOS_DEV_ADDR_SIZE 0x40000000
#define EXYNOS_DEV_ADDR_ORDER 0x4 #define EXYNOS_DEV_ADDR_ORDER 0x0
#ifdef CONFIG_DRM_EXYNOS_IOMMU #ifdef CONFIG_DRM_EXYNOS_IOMMU
......
...@@ -284,13 +284,13 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height) ...@@ -284,13 +284,13 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
MXR_CFG_SCAN_PROGRASSIVE); MXR_CFG_SCAN_PROGRASSIVE);
/* choosing between porper HD and SD mode */ /* choosing between porper HD and SD mode */
if (height == 480) if (height <= 480)
val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD;
else if (height == 576) else if (height <= 576)
val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD;
else if (height == 720) else if (height <= 720)
val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
else if (height == 1080) else if (height <= 1080)
val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD;
else else
val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
...@@ -818,6 +818,29 @@ static void mixer_win_disable(void *ctx, int win) ...@@ -818,6 +818,29 @@ static void mixer_win_disable(void *ctx, int win)
mixer_ctx->win_data[win].enabled = false; mixer_ctx->win_data[win].enabled = false;
} }
int mixer_check_timing(void *ctx, struct fb_videomode *timing)
{
struct mixer_context *mixer_ctx = ctx;
u32 w, h;
w = timing->xres;
h = timing->yres;
DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d\n",
__func__, timing->xres, timing->yres,
timing->refresh, (timing->vmode &
FB_VMODE_INTERLACED) ? true : false);
if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16)
return 0;
if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) ||
(w >= 1024 && w <= 1280 && h >= 576 && h <= 720) ||
(w >= 1664 && w <= 1920 && h >= 936 && h <= 1080))
return 0;
return -EINVAL;
}
static void mixer_wait_for_vblank(void *ctx) static void mixer_wait_for_vblank(void *ctx)
{ {
struct mixer_context *mixer_ctx = ctx; struct mixer_context *mixer_ctx = ctx;
...@@ -955,6 +978,9 @@ static struct exynos_mixer_ops mixer_ops = { ...@@ -955,6 +978,9 @@ static struct exynos_mixer_ops mixer_ops = {
.win_mode_set = mixer_win_mode_set, .win_mode_set = mixer_win_mode_set,
.win_commit = mixer_win_commit, .win_commit = mixer_win_commit,
.win_disable = mixer_win_disable, .win_disable = mixer_win_disable,
/* display */
.check_timing = mixer_check_timing,
}; };
static irqreturn_t mixer_irq_handler(int irq, void *arg) static irqreturn_t mixer_irq_handler(int irq, void *arg)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册