diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_fb.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c new file mode 100644 index 000000000000..48d29cfd5240 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -0,0 +1,265 @@ +/* exynos_drm_fb.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae <inki.dae@samsung.com> + * 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. + */ + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_fb.h" +#include "exynos_drm_buf.h" +#include "exynos_drm_gem.h" + +#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) + +/* + * 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. + */ +struct exynos_drm_fb { + struct drm_framebuffer fb; + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_buf_entry *entry; +}; + +static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) +{ + struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + 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); + + kfree(exynos_fb); + exynos_fb = NULL; +} + +static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + return drm_gem_handle_create(file_priv, + &exynos_fb->exynos_gem_obj->base, handle); +} + +static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned flags, + unsigned color, struct drm_clip_rect *clips, + unsigned num_clips) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO */ + + return 0; +} + +static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { + .destroy = exynos_drm_fb_destroy, + .create_handle = exynos_drm_fb_create_handle, + .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) +{ + 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__); + + mode_cmd->pitch = max(mode_cmd->pitch, + mode_cmd->width * (mode_cmd->bpp >> 3)); + + DRM_LOG_KMS("drm fb create(%dx%d)\n", + mode_cmd->width, mode_cmd->height); + + exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); + if (!exynos_fb) { + DRM_ERROR("failed to allocate exynos drm framebuffer.\n"); + return ERR_PTR(-ENOMEM); + } + + fb = &exynos_fb->fb; + ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs); + if (ret) { + DRM_ERROR("failed to initialize framebuffer.\n"); + goto err_init; + } + + DRM_LOG_KMS("create: fb id: %d\n", fb->base.id); + + size = mode_cmd->pitch * mode_cmd->height; + + /* + * 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; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + drm_gem_object_unreference_unlocked(obj); + } + + /* + * 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. + */ + exynos_fb->entry = exynos_gem_obj->entry; + + 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); + +out: + exynos_fb->exynos_gem_obj = exynos_gem_obj; + + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + + return fb; + +err_buffer: + drm_framebuffer_cleanup(fb); + +err_init: + kfree(exynos_fb); + + return ERR_PTR(ret); +} + +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__); + + 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_fb *exynos_fb = to_exynos_fb(fb); + struct exynos_drm_buf_entry *entry; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + entry = exynos_fb->entry; + if (!entry) + return NULL; + + DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n", + (unsigned long)entry->vaddr, + (unsigned long)entry->paddr); + + return entry; +} + +static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { + .fb_create = exynos_drm_fb_create, +}; + +void exynos_drm_mode_config_init(struct drm_device *dev) +{ + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + + 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"); |