blob: f575c9e5f7c1dfc9715c1736113fe077508cf92c [file] [log] [blame]
/*
*
* (C) COPYRIGHT 2012-2014 ARM Limited. All rights reserved.
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* pl111_drm_fb.c
* Implementation of the framebuffer functions for PL111 DRM
*/
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
#include <linux/version.h>
#include <linux/shmem_fs.h>
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_crtc.h>
#include "pl111_drm.h"
static void pl111_fb_destroy(struct drm_framebuffer *framebuffer)
{
struct pl111_drm_framebuffer *pl111_fb;
#ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS
struct drm_crtc *crtc;
unsigned long flags;
#endif
DRM_DEBUG_KMS("Destroying framebuffer 0x%p...\n", framebuffer);
pl111_fb = PL111_FB_FROM_FRAMEBUFFER(framebuffer);
/*
* Because flips are deferred, wait for all previous flips to complete
*/
wait_event(priv.wait_for_flips,
atomic_read(&priv.nr_flips_in_flight) == 0);
#ifdef CONFIG_DMA_SHARED_BUFFER_USES_KDS
/*
* Release KDS resources if it's currently being displayed. Only occurs
* when the last framebuffer is destroyed.
*/
list_for_each_entry(crtc, &framebuffer->dev->mode_config.crtc_list,
head) {
struct pl111_drm_crtc *pl111_crtc = to_pl111_crtc(crtc);
spin_lock_irqsave(&pl111_crtc->current_displaying_lock, flags);
if (pl111_crtc->displaying_fb == framebuffer) {
/* Release the current buffers */
if (pl111_crtc->old_kds_res_set != NULL) {
DRM_DEBUG_KMS("Releasing KDS resources for ");
DRM_DEBUG_KMS("displayed 0x%p\n", framebuffer);
kds_resource_set_release(
&pl111_crtc->old_kds_res_set);
}
pl111_crtc->old_kds_res_set = NULL;
}
spin_unlock_irqrestore(&pl111_crtc->current_displaying_lock,
flags);
}
#endif
drm_framebuffer_cleanup(framebuffer);
if ((pl111_fb->bo != NULL) && (&pl111_fb->bo->gem_object != NULL))
drm_gem_object_unreference_unlocked(&pl111_fb->bo->gem_object);
kfree(pl111_fb);
DRM_DEBUG_KMS("Destroyed framebuffer 0x%p\n", framebuffer);
}
static int pl111_fb_create_handle(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int *handle)
{
struct pl111_gem_bo *bo = PL111_BO_FROM_FRAMEBUFFER(fb);
DRM_DEBUG_KMS("DRM %s on fb=%p\n", __func__, fb);
if (bo == NULL)
return -EINVAL;
return drm_gem_handle_create(file_priv, &bo->gem_object, handle);
}
const struct drm_framebuffer_funcs fb_funcs = {
.destroy = pl111_fb_destroy,
.create_handle = pl111_fb_create_handle,
};
struct drm_framebuffer *pl111_fb_create(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct pl111_drm_framebuffer *pl111_fb = NULL;
struct drm_framebuffer *fb = NULL;
struct drm_gem_object *gem_obj;
struct pl111_gem_bo *bo;
int err = 0;
size_t min_size;
int bpp;
int depth;
pr_info("DRM %s\n", __func__);
gem_obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
if (gem_obj == NULL) {
DRM_ERROR("Could not get gem obj from handle to create fb\n");
err = -ENOENT;
goto error;
}
bo = PL111_BO_FROM_GEM(gem_obj);
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
if (mode_cmd->pitches[0] < mode_cmd->width * (bpp >> 3)) {
DRM_ERROR("bad pitch %u for plane 0\n", mode_cmd->pitches[0]);
err = -EINVAL;
goto error;
}
min_size = (mode_cmd->height - 1) * mode_cmd->pitches[0]
+ mode_cmd->width * (bpp >> 3);
if (bo->gem_object.size < min_size) {
DRM_ERROR("gem obj size < min size\n");
err = -EINVAL;
goto error;
}
/* We can't scan out SHM so we can't create an fb for it */
if (!(bo->type & PL111_BOT_DMA)) {
DRM_ERROR("Can't create FB for non-scanout buffer\n");
err = -EINVAL;
goto error;
}
switch ((char)(mode_cmd->pixel_format & 0xFF)) {
case 'Y':
case 'U':
case 'V':
case 'N':
case 'T':
DRM_ERROR("YUV formats not supported\n");
err = -EINVAL;
goto error;
}
pl111_fb = kzalloc(sizeof(struct pl111_drm_framebuffer), GFP_KERNEL);
if (pl111_fb == NULL) {
DRM_ERROR("Could not allocate pl111_drm_framebuffer\n");
err = -ENOMEM;
goto error;
}
fb = &pl111_fb->fb;
err = drm_framebuffer_init(dev, fb, &fb_funcs);
if (err) {
DRM_ERROR("drm_framebuffer_init failed\n");
kfree(fb);
fb = NULL;
goto error;
}
drm_helper_mode_fill_fb_struct(fb, mode_cmd);
/* The only framebuffer formats supported by pl111
* are 16 bpp or 32 bpp with 24 bit depth.
* See clcd_enable()
*/
if (!((fb->bits_per_pixel == 16) ||
(fb->bits_per_pixel == 32 && fb->depth == 24))) {
DRM_DEBUG_KMS("unsupported pixel format bpp=%d, depth=%d\n", fb->bits_per_pixel, fb->depth);
drm_framebuffer_cleanup(fb);
kfree(fb);
fb = NULL;
err = -EINVAL;
goto error;
}
pl111_fb->bo = bo;
DRM_DEBUG_KMS("Created fb 0x%p with gem_obj 0x%p physaddr=0x%.8x\n",
fb, gem_obj, bo->backing_data.dma.fb_dev_addr);
return fb;
error:
if (gem_obj != NULL)
drm_gem_object_unreference_unlocked(gem_obj);
return ERR_PTR(err);
}