diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fb.c')
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 354 |
1 files changed, 208 insertions, 146 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 48d29cfd524..65a22cad7b3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -6,33 +6,24 @@ * Joonyoung Shim <jy0922.shim@samsung.com> * Seung-Woo Kim <sw0312.kim@samsung.com> * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. */ -#include "drmP.h" -#include "drm_crtc.h" -#include "drm_crtc_helper.h" +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <uapi/drm/exynos_drm.h> +#include "exynos_drm_drv.h" #include "exynos_drm_fb.h" -#include "exynos_drm_buf.h" +#include "exynos_drm_fbdev.h" #include "exynos_drm_gem.h" +#include "exynos_drm_iommu.h" +#include "exynos_drm_crtc.h" #define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) @@ -40,31 +31,60 @@ * exynos specific framebuffer structure. * * @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. + * @buf_cnt: a buffer count to drm framebuffer. + * @exynos_gem_obj: array of exynos specific gem object containing a gem object. */ struct exynos_drm_fb { struct drm_framebuffer fb; - struct exynos_drm_gem_obj *exynos_gem_obj; - struct exynos_drm_buf_entry *entry; + unsigned int buf_cnt; + struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER]; }; +static int check_fb_gem_memory_type(struct drm_device *drm_dev, + struct exynos_drm_gem_obj *exynos_gem_obj) +{ + unsigned int flags; + + /* + * if exynos drm driver supports iommu then framebuffer can use + * all the buffer types. + */ + if (is_drm_iommu_supported(drm_dev)) + return 0; + + flags = exynos_gem_obj->flags; + + /* + * without iommu support, not support physically non-continuous memory + * for framebuffer. + */ + if (IS_NONCONTIG_BUFFER(flags)) { + DRM_ERROR("cannot use this gem memory type for fb.\n"); + return -EINVAL; + } + + return 0; +} + static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) { struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + unsigned int i; - DRM_DEBUG_KMS("%s\n", __FILE__); + /* make sure that overlay data are updated before relesing fb. */ + exynos_drm_crtc_complete_scanout(fb); drm_framebuffer_cleanup(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); + for (i = 0; i < ARRAY_SIZE(exynos_fb->exynos_gem_obj); i++) { + struct drm_gem_object *obj; + + if (exynos_fb->exynos_gem_obj[i] == NULL) + continue; + + obj = &exynos_fb->exynos_gem_obj[i]->base; + drm_gem_object_unreference_unlocked(obj); + } kfree(exynos_fb); exynos_fb = NULL; @@ -76,10 +96,12 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, { struct exynos_drm_fb *exynos_fb = to_exynos_fb(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->base, handle); + &exynos_fb->exynos_gem_obj[0]->base, handle); } static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, @@ -87,8 +109,6 @@ static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO */ return 0; @@ -100,146 +120,194 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { .dirty = exynos_drm_fb_dirty, }; -static struct drm_framebuffer * -exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, - struct drm_mode_fb_cmd *mode_cmd) +void exynos_drm_fb_set_buf_cnt(struct drm_framebuffer *fb, + unsigned int cnt) { struct exynos_drm_fb *exynos_fb; - struct drm_framebuffer *fb; - struct exynos_drm_gem_obj *exynos_gem_obj = NULL; - struct drm_gem_object *obj; - unsigned int size; - int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); + exynos_fb = to_exynos_fb(fb); - mode_cmd->pitch = max(mode_cmd->pitch, - mode_cmd->width * (mode_cmd->bpp >> 3)); + exynos_fb->buf_cnt = cnt; +} - DRM_LOG_KMS("drm fb create(%dx%d)\n", - mode_cmd->width, mode_cmd->height); +unsigned int exynos_drm_fb_get_buf_cnt(struct drm_framebuffer *fb) +{ + struct exynos_drm_fb *exynos_fb; + + exynos_fb = to_exynos_fb(fb); + + return exynos_fb->buf_cnt; +} + +struct drm_framebuffer * +exynos_drm_framebuffer_init(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + struct exynos_drm_fb *exynos_fb; + struct exynos_drm_gem_obj *exynos_gem_obj; + int ret; + + exynos_gem_obj = to_exynos_gem_obj(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"); + return ERR_PTR(-EINVAL); + } exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); - if (!exynos_fb) { - DRM_ERROR("failed to allocate exynos drm framebuffer.\n"); + if (!exynos_fb) return ERR_PTR(-ENOMEM); - } - fb = &exynos_fb->fb; - ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs); + drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd); + exynos_fb->exynos_gem_obj[0] = exynos_gem_obj; + + ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); if (ret) { - DRM_ERROR("failed to initialize framebuffer.\n"); - goto err_init; + DRM_ERROR("failed to initialize framebuffer\n"); + return ERR_PTR(ret); } - DRM_LOG_KMS("create: fb id: %d\n", fb->base.id); - - size = mode_cmd->pitch * mode_cmd->height; + return &exynos_fb->fb; +} - /* - * mode_cmd->handle could be NULL at booting time or - * with user request. if NULL, a new buffer or a gem object - * would be allocated. - */ - if (!mode_cmd->handle) { - if (!file_priv) { - struct exynos_drm_buf_entry *entry; - - /* - * 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); - goto err_buffer; - } - - exynos_fb->entry = entry; - - DRM_LOG_KMS("default fb: paddr = 0x%lx, size = 0x%x\n", - (unsigned long)entry->paddr, size); - - goto out; - } else { - exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, - size, - &mode_cmd->handle); - if (IS_ERR(exynos_gem_obj)) { - ret = PTR_ERR(exynos_gem_obj); - goto err_buffer; - } - } - } else { - obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - goto err_buffer; - } +static u32 exynos_drm_format_num_buffers(struct drm_mode_fb_cmd2 *mode_cmd) +{ + unsigned int cnt = 0; - exynos_gem_obj = to_exynos_gem_obj(obj); + if (mode_cmd->pixel_format != DRM_FORMAT_NV12) + return drm_format_num_planes(mode_cmd->pixel_format); - drm_gem_object_unreference_unlocked(obj); + while (cnt != MAX_FB_BUFFER) { + if (!mode_cmd->handles[cnt]) + break; + cnt++; } /* - * if got a exynos_gem_obj from either a handle or - * a new creation then exynos_fb->exynos_gem_obj is NULL - * so that default framebuffer has no its own gem object, - * only its own buffer object. + * check if NV12 or NV12M. + * + * NV12 + * handles[0] = base1, offsets[0] = 0 + * handles[1] = base1, offsets[1] = Y_size + * + * NV12M + * handles[0] = base1, offsets[0] = 0 + * handles[1] = base2, offsets[1] = 0 */ - exynos_fb->entry = exynos_gem_obj->entry; + if (cnt == 2) { + /* + * in case of NV12 format, offsets[1] is not 0 and + * handles[0] is same as handles[1]. + */ + if (mode_cmd->offsets[1] && + mode_cmd->handles[0] == mode_cmd->handles[1]) + cnt = 1; + } - DRM_LOG_KMS("paddr = 0x%lx, size = 0x%x, gem object = 0x%x\n", - (unsigned long)exynos_fb->entry->paddr, size, - (unsigned int)&exynos_gem_obj->base); + return cnt; +} -out: - exynos_fb->exynos_gem_obj = exynos_gem_obj; +static struct drm_framebuffer * +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_helper_mode_fill_fb_struct(fb, mode_cmd); + exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); + if (!exynos_fb) + return ERR_PTR(-ENOMEM); - return fb; + 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; + } -err_buffer: - drm_framebuffer_cleanup(fb); + 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); -err_init: - kfree(exynos_fb); + DRM_DEBUG_KMS("buf_cnt = %d\n", exynos_fb->buf_cnt); - return ERR_PTR(ret); -} + for (i = 1; i < exynos_fb->buf_cnt; i++) { + obj = drm_gem_object_lookup(dev, file_priv, + mode_cmd->handles[i]); + if (!obj) { + DRM_ERROR("failed to lookup gem object\n"); + ret = -ENOENT; + exynos_fb->buf_cnt = i; + goto err_unreference; + } -struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, - struct drm_file *file_priv, - struct drm_mode_fb_cmd *mode_cmd) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); + 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"); + goto err_unreference; + } + } + + ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); + if (ret) { + DRM_ERROR("failed to init framebuffer.\n"); + goto err_unreference; + } + + return &exynos_fb->fb; - return exynos_drm_fb_init(file_priv, dev, mode_cmd); +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_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb) +struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, + int index) { 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__); + if (index >= MAX_FB_BUFFER) + return NULL; - entry = exynos_fb->entry; - if (!entry) + buffer = exynos_fb->exynos_gem_obj[index]->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("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr); - return entry; + return buffer; } -static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { - .fb_create = exynos_drm_fb_create, +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); + else + exynos_drm_fbdev_init(dev); +} + +static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { + .fb_create = exynos_user_fb_create, + .output_poll_changed = exynos_drm_output_poll_changed, }; void exynos_drm_mode_config_init(struct drm_device *dev) @@ -257,9 +325,3 @@ void exynos_drm_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &exynos_drm_mode_config_funcs; } - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM FB Driver"); -MODULE_LICENSE("GPL"); |
