diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fb.c')
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 338 |
1 files changed, 194 insertions, 144 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 5bf4a1ac7f8..65a22cad7b3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -6,35 +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_fb_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) @@ -42,31 +31,60 @@ * exynos specific framebuffer structure. * * @fb: drm framebuffer obejct. - * @exynos_gem_obj: exynos specific gem object containing a gem object. - * @buffer: pointer to exynos_drm_gem_buffer object. - * - contain the memory information to 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_gem_buf *buffer; + 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->buffer) - exynos_drm_buf_destroy(fb->dev, exynos_fb->buffer); + 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; @@ -78,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, @@ -89,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; @@ -102,140 +120,176 @@ 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); + + exynos_fb->buf_cnt = cnt; +} + +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; - mode_cmd->pitch = max(mode_cmd->pitch, - mode_cmd->width * (mode_cmd->bpp >> 3)); + exynos_gem_obj = to_exynos_gem_obj(obj); - DRM_LOG_KMS("drm fb create(%dx%d)\n", - mode_cmd->width, mode_cmd->height); + 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_gem_buf *buffer; - - /* - * in case that file_priv is NULL, it allocates - * only buffer and this buffer would be used - * for default framebuffer. - */ - buffer = exynos_drm_buf_create(dev, size); - if (IS_ERR(buffer)) { - ret = PTR_ERR(buffer); - goto err_buffer; - } - - exynos_fb->buffer = buffer; - - 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(dev, file_priv, - &mode_cmd->handle, - size); - 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->buffer = exynos_gem_obj->buffer; + 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("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); + 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; + } + } - return exynos_drm_fb_init(file_priv, dev, mode_cmd); + 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; + +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_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_gem_buf *buffer; - DRM_DEBUG_KMS("%s\n", __FILE__); + if (index >= MAX_FB_BUFFER) + return NULL; - buffer = exynos_fb->buffer; + buffer = exynos_fb->exynos_gem_obj[index]->buffer; if (!buffer) return NULL; - DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n", - (unsigned long)buffer->kvaddr, - (unsigned long)buffer->dma_addr); + DRM_DEBUG_KMS("dma_addr = 0x%lx\n", (unsigned long)buffer->dma_addr); return buffer; } @@ -247,10 +301,12 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev) if (fb_helper) drm_fb_helper_hotplug_event(fb_helper); + else + exynos_drm_fbdev_init(dev); } -static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { - .fb_create = exynos_drm_fb_create, +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, }; @@ -269,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"); |
