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
This commit is contained in:
commit
c976cb37a9
@ -99,6 +99,10 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
|
||||
|
||||
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,
|
||||
&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,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct exynos_drm_fb *exynos_fb;
|
||||
int i, ret;
|
||||
|
||||
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);
|
||||
if (!exynos_fb) {
|
||||
DRM_ERROR("failed to allocate exynos drm framebuffer\n");
|
||||
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);
|
||||
exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj);
|
||||
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,
|
||||
DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt);
|
||||
|
||||
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,
|
||||
mode_cmd->handles[i]);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object\n");
|
||||
kfree(exynos_fb);
|
||||
return ERR_PTR(-ENOENT);
|
||||
ret = -ENOENT;
|
||||
exynos_fb->buf_cnt = i;
|
||||
goto err_unreference;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("cannot use this gem memory type for fb.\n");
|
||||
kfree(exynos_fb);
|
||||
return ERR_PTR(ret);
|
||||
goto err_unreference;
|
||||
}
|
||||
|
||||
exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj);
|
||||
}
|
||||
|
||||
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
|
||||
if (ret) {
|
||||
for (i = 0; i < exynos_fb->buf_cnt; i++) {
|
||||
struct exynos_drm_gem_obj *gem_obj;
|
||||
|
||||
gem_obj = exynos_fb->exynos_gem_obj[i];
|
||||
drm_gem_object_unreference_unlocked(&gem_obj->base);
|
||||
}
|
||||
|
||||
kfree(exynos_fb);
|
||||
return ERR_PTR(ret);
|
||||
DRM_ERROR("failed to init framebuffer.\n");
|
||||
goto err_unreference;
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-attrs.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/exynos_drm.h>
|
||||
@ -429,7 +430,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
|
||||
|
||||
g2d_userptr->pages = pages;
|
||||
|
||||
sgt = kzalloc(sizeof *sgt, GFP_KERNEL);
|
||||
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt) {
|
||||
DRM_ERROR("failed to allocate sg table.\n");
|
||||
ret = -ENOMEM;
|
||||
@ -1240,6 +1241,14 @@ static int g2d_resume(struct device *dev)
|
||||
|
||||
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 = {
|
||||
.probe = g2d_probe,
|
||||
.remove = g2d_remove,
|
||||
@ -1247,5 +1256,6 @@ struct platform_driver g2d_driver = {
|
||||
.name = "s5p-g2d",
|
||||
.owner = THIS_MODULE,
|
||||
.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,
|
||||
{
|
||||
struct drm_file *file_priv;
|
||||
|
||||
mutex_lock(&drm_dev->struct_mutex);
|
||||
|
||||
/* find current process's drm_file from filelist. */
|
||||
list_for_each_entry(file_priv, &drm_dev->filelist, lhead) {
|
||||
if (file_priv->filp == filp) {
|
||||
mutex_unlock(&drm_dev->struct_mutex);
|
||||
list_for_each_entry(file_priv, &drm_dev->filelist, lhead)
|
||||
if (file_priv->filp == filp)
|
||||
return file_priv;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&drm_dev->struct_mutex);
|
||||
WARN_ON(1);
|
||||
|
||||
return ERR_PTR(-EFAULT);
|
||||
@ -400,9 +394,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
|
||||
*/
|
||||
drm_gem_object_reference(obj);
|
||||
|
||||
mutex_lock(&drm_dev->struct_mutex);
|
||||
drm_vm_open_locked(drm_dev, vma);
|
||||
mutex_unlock(&drm_dev->struct_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -431,6 +423,16 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
||||
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
|
||||
* 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,
|
||||
addr = vm_mmap(file_priv->filp, 0, args->size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED, 0);
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
drm_gem_object_unreference(obj);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
args->mapped = addr;
|
||||
|
||||
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,
|
||||
static int drm_hdmi_check_timing(struct device *dev, void *timing)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(dev);
|
||||
int ret = 0;
|
||||
|
||||
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)
|
||||
return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
|
||||
|
||||
|
@ -32,7 +32,7 @@ struct exynos_hdmi_ops {
|
||||
bool (*is_connected)(void *ctx);
|
||||
struct edid *(*get_edid)(void *ctx,
|
||||
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);
|
||||
|
||||
/* manager */
|
||||
@ -58,6 +58,9 @@ struct exynos_mixer_ops {
|
||||
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
|
||||
void (*win_commit)(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);
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#define EXYNOS_DEV_ADDR_START 0x20000000
|
||||
#define EXYNOS_DEV_ADDR_SIZE 0x40000000
|
||||
#define EXYNOS_DEV_ADDR_ORDER 0x4
|
||||
#define EXYNOS_DEV_ADDR_ORDER 0x0
|
||||
|
||||
#ifdef CONFIG_DRM_EXYNOS_IOMMU
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -284,13 +284,13 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
|
||||
MXR_CFG_SCAN_PROGRASSIVE);
|
||||
|
||||
/* choosing between porper HD and SD mode */
|
||||
if (height == 480)
|
||||
if (height <= 480)
|
||||
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;
|
||||
else if (height == 720)
|
||||
else if (height <= 720)
|
||||
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;
|
||||
else
|
||||
val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD;
|
||||
@ -818,6 +818,29 @@ static void mixer_win_disable(void *ctx, int win)
|
||||
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)
|
||||
{
|
||||
struct mixer_context *mixer_ctx = ctx;
|
||||
@ -955,6 +978,9 @@ static struct exynos_mixer_ops mixer_ops = {
|
||||
.win_mode_set = mixer_win_mode_set,
|
||||
.win_commit = mixer_win_commit,
|
||||
.win_disable = mixer_win_disable,
|
||||
|
||||
/* display */
|
||||
.check_timing = mixer_check_timing,
|
||||
};
|
||||
|
||||
static irqreturn_t mixer_irq_handler(int irq, void *arg)
|
||||
|
Loading…
Reference in New Issue
Block a user