diff options
author | Jon Medhurst <tixy@linaro.org> | 2017-03-15 11:50:12 +0000 |
---|---|---|
committer | Jon Medhurst <tixy@linaro.org> | 2017-04-03 11:59:00 +0100 |
commit | 4aa6c1511e22e868407474996db6178a5bd8c758 (patch) | |
tree | a8690dfbe9a37ce54abef8fc85551be07f65633e | |
parent | ed0a1bd8019af005526c275d57f2c5bee578d73f (diff) |
drm: hdlcd: Fork drm_fb_cma_helper -> hdlcd_fb_helper
Because we need to modify it to get Android and Mali working.
Files files generated with:
cp drivers/gpu/drm/drm_fb_cma_helper.c drivers/gpu/drm/arm/hdlcd_fb_helper.c
sed -e s/drm_fb_cma/hdlcd_fb/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.c
sed -e s/drm_fbdev_cma/hdlcd_drm_fbdev/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.c
sed -e s/fbdev_cma/hdlcd_fbdev/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.c
sed -e s/fb_cma/hdlcd_fb/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.c
sed -e 's/<drm\/hdlcd_fb_helper\.h>/\"hdlcd_fb_helper\.h\"/' -i drivers/gpu/drm/arm/hdlcd_fb_helper.c
cp include/drm/drm_fb_cma_helper.h drivers/gpu/drm/arm/hdlcd_fb_helper.h
sed -e s/drm_fb_cma/hdlcd_fb/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.h
sed -e s/drm_fbdev_cma/hdlcd_drm_fbdev/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.h
sed -e s/fbdev_cma/hdlcd_fbdev/g -i drivers/gpu/drm/arm/hdlcd_fb_helper.h
sed -e 's/<drm\/drm_fb_cma_helper\.h>/\"hdlcd_fb_helper\.h\"/' -i drivers/gpu/drm/arm/hdlcd_drv.c
sed -e s/drm_fb_cma/hdlcd_fb/g -i drivers/gpu/drm/arm/hdlcd_drv.c
sed -e s/drm_fbdev_cma/hdlcd_drm_fbdev/g -i drivers/gpu/drm/arm/hdlcd_drv.c
sed -e s/drm_fbdev_cma/hdlcd_drm_fbdev/g -i drivers/gpu/drm/arm/hdlcd_drv.h
sed -e 's/<drm\/drm_fb_cma_helper\.h>/\"hdlcd_fb_helper\.h\"/' -i drivers/gpu/drm/arm/hdlcd_crtc.c
sed -e s/drm_fb_cma/hdlcd_fb/g -i drivers/gpu/drm/arm/hdlcd_crtc.c
sed -e 's/hdlcd_crtc\.o/hdlcd_crtc\.o hdlcd_fb_helper\.o/g' -i drivers/gpu/drm/arm/Makefile
Signed-off-by: Jon Medhurst <tixy@linaro.org>
# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# Date: Wed Mar 15 11:50:12 2017 +0000
#
# HEAD detached from 0c05917f761c
# Changes to be committed:
# modified: drivers/gpu/drm/arm/Makefile
# modified: drivers/gpu/drm/arm/hdlcd_crtc.c
# modified: drivers/gpu/drm/arm/hdlcd_drv.c
# modified: drivers/gpu/drm/arm/hdlcd_drv.h
# new file: drivers/gpu/drm/arm/hdlcd_fb_helper.c
# new file: drivers/gpu/drm/arm/hdlcd_fb_helper.h
#
-rw-r--r-- | drivers/gpu/drm/arm/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/hdlcd_crtc.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/hdlcd_drv.c | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/hdlcd_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/hdlcd_fb_helper.c | 641 | ||||
-rw-r--r-- | drivers/gpu/drm/arm/hdlcd_fb_helper.h | 54 |
6 files changed, 707 insertions, 12 deletions
diff --git a/drivers/gpu/drm/arm/Makefile b/drivers/gpu/drm/arm/Makefile index bb8b158ff90d..6e09ee75681e 100644 --- a/drivers/gpu/drm/arm/Makefile +++ b/drivers/gpu/drm/arm/Makefile @@ -1,4 +1,4 @@ -hdlcd-y := hdlcd_drv.o hdlcd_crtc.o +hdlcd-y := hdlcd_drv.o hdlcd_crtc.o hdlcd_fb_helper.o hdlcd_fb_helper.o obj-$(CONFIG_DRM_HDLCD) += hdlcd.o mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index 34ce0dd7cabe..b6010098ba87 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -14,7 +14,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> -#include <drm/drm_fb_cma_helper.h> +#include "hdlcd_fb_helper.h" #include <drm/drm_gem_cma_helper.h> #include <drm/drm_of.h> #include <drm/drm_plane_helper.h> @@ -250,7 +250,7 @@ static void hdlcd_plane_atomic_update(struct drm_plane *plane, src_h = plane->state->src_h >> 16; dest_w = plane->state->crtc_w; dest_h = plane->state->crtc_h; - gem = drm_fb_cma_get_gem_obj(fb, 0); + gem = hdlcd_fb_get_gem_obj(fb, 0); scanout_start = gem->paddr + fb->offsets[0] + plane->state->crtc_y * fb->pitches[0] + plane->state->crtc_x * diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 96908d6cc9f6..869508c22a60 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -23,7 +23,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> -#include <drm/drm_fb_cma_helper.h> +#include "hdlcd_fb_helper.h" #include <drm/drm_gem_cma_helper.h> #include <drm/drm_of.h> @@ -108,11 +108,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) { struct hdlcd_drm_private *hdlcd = drm->dev_private; - drm_fbdev_cma_hotplug_event(hdlcd->fbdev); + hdlcd_drm_fbdev_hotplug_event(hdlcd->fbdev); } static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { - .fb_create = drm_fb_cma_create, + .fb_create = hdlcd_fb_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, @@ -132,7 +132,7 @@ static void hdlcd_lastclose(struct drm_device *drm) { struct hdlcd_drm_private *hdlcd = drm->dev_private; - drm_fbdev_cma_restore_mode(hdlcd->fbdev); + hdlcd_drm_fbdev_restore_mode(hdlcd->fbdev); } static irqreturn_t hdlcd_irq(int irq, void *arg) @@ -253,7 +253,7 @@ static int hdlcd_show_pxlclock(struct seq_file *m, void *arg) static struct drm_info_list hdlcd_debugfs_list[] = { { "interrupt_count", hdlcd_show_underrun_count, 0 }, { "clocks", hdlcd_show_pxlclock, 0 }, - { "fb", drm_fb_cma_debugfs_show, 0 }, + { "fb", hdlcd_fb_debugfs_show, 0 }, }; static int hdlcd_debugfs_init(struct drm_minor *minor) @@ -365,7 +365,7 @@ static int hdlcd_drm_bind(struct device *dev) preferred_bpp = 32; /* If Mali present, assume 32bpp */ } - hdlcd->fbdev = drm_fbdev_cma_init(drm, preferred_bpp, + hdlcd->fbdev = hdlcd_drm_fbdev_init(drm, preferred_bpp, drm->mode_config.num_connector); if (IS_ERR(hdlcd->fbdev)) { @@ -382,7 +382,7 @@ static int hdlcd_drm_bind(struct device *dev) err_register: if (hdlcd->fbdev) { - drm_fbdev_cma_fini(hdlcd->fbdev); + hdlcd_drm_fbdev_fini(hdlcd->fbdev); hdlcd->fbdev = NULL; } err_fbdev: @@ -410,7 +410,7 @@ static void hdlcd_drm_unbind(struct device *dev) drm_dev_unregister(drm); if (hdlcd->fbdev) { - drm_fbdev_cma_fini(hdlcd->fbdev); + hdlcd_drm_fbdev_fini(hdlcd->fbdev); hdlcd->fbdev = NULL; } drm_kms_helper_poll_fini(drm); diff --git a/drivers/gpu/drm/arm/hdlcd_drv.h b/drivers/gpu/drm/arm/hdlcd_drv.h index e3950a071152..4d2fa6459031 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.h +++ b/drivers/gpu/drm/arm/hdlcd_drv.h @@ -8,7 +8,7 @@ struct hdlcd_drm_private { void __iomem *mmio; struct clk *clk; - struct drm_fbdev_cma *fbdev; + struct hdlcd_drm_fbdev *fbdev; struct drm_crtc crtc; struct drm_plane *plane; struct drm_atomic_state *state; diff --git a/drivers/gpu/drm/arm/hdlcd_fb_helper.c b/drivers/gpu/drm/arm/hdlcd_fb_helper.c new file mode 100644 index 000000000000..9e660e3cd855 --- /dev/null +++ b/drivers/gpu/drm/arm/hdlcd_fb_helper.c @@ -0,0 +1,641 @@ +/* + * drm kms/fb cma (contiguous memory allocator) helper functions + * + * Copyright (C) 2012 Analog Device Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Based on udl_fbdev.c + * Copyright (C) 2012 Red Hat + * + * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include "hdlcd_fb_helper.h" +#include <linux/dma-buf.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/reservation.h> + +#define DEFAULT_FBDEFIO_DELAY_MS 50 + +struct hdlcd_fb { + struct drm_framebuffer fb; + struct drm_gem_cma_object *obj[4]; +}; + +struct hdlcd_drm_fbdev { + struct drm_fb_helper fb_helper; + struct hdlcd_fb *fb; + const struct drm_framebuffer_funcs *fb_funcs; +}; + +/** + * DOC: framebuffer cma helper functions + * + * Provides helper functions for creating a cma (contiguous memory allocator) + * backed framebuffer. + * + * hdlcd_fb_create() is used in the &drm_mode_config_funcs.fb_create + * callback function to create a cma backed framebuffer. + * + * An fbdev framebuffer backed by cma is also available by calling + * hdlcd_drm_fbdev_init(). hdlcd_drm_fbdev_fini() tears it down. + * If the &drm_framebuffer_funcs.dirty callback is set, fb_deferred_io will be + * set up automatically. &drm_framebuffer_funcs.dirty is called by + * drm_fb_helper_deferred_io() in process context (&struct delayed_work). + * + * Example fbdev deferred io code:: + * + * static int driver_fb_dirty(struct drm_framebuffer *fb, + * struct drm_file *file_priv, + * unsigned flags, unsigned color, + * struct drm_clip_rect *clips, + * unsigned num_clips) + * { + * struct drm_gem_cma_object *cma = hdlcd_fb_get_gem_obj(fb, 0); + * ... push changes ... + * return 0; + * } + * + * static struct drm_framebuffer_funcs driver_fb_funcs = { + * .destroy = hdlcd_fb_destroy, + * .create_handle = hdlcd_fb_create_handle, + * .dirty = driver_fb_dirty, + * }; + * + * Initialize:: + * + * fbdev = hdlcd_drm_fbdev_init_with_funcs(dev, 16, + * dev->mode_config.num_crtc, + * dev->mode_config.num_connector, + * &driver_fb_funcs); + * + */ + +static inline struct hdlcd_drm_fbdev *to_hdlcd_fbdev(struct drm_fb_helper *helper) +{ + return container_of(helper, struct hdlcd_drm_fbdev, fb_helper); +} + +static inline struct hdlcd_fb *to_hdlcd_fb(struct drm_framebuffer *fb) +{ + return container_of(fb, struct hdlcd_fb, fb); +} + +void hdlcd_fb_destroy(struct drm_framebuffer *fb) +{ + struct hdlcd_fb *hdlcd_fb = to_hdlcd_fb(fb); + int i; + + for (i = 0; i < 4; i++) { + if (hdlcd_fb->obj[i]) + drm_gem_object_unreference_unlocked(&hdlcd_fb->obj[i]->base); + } + + drm_framebuffer_cleanup(fb); + kfree(hdlcd_fb); +} +EXPORT_SYMBOL(hdlcd_fb_destroy); + +int hdlcd_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned int *handle) +{ + struct hdlcd_fb *hdlcd_fb = to_hdlcd_fb(fb); + + return drm_gem_handle_create(file_priv, + &hdlcd_fb->obj[0]->base, handle); +} +EXPORT_SYMBOL(hdlcd_fb_create_handle); + +static struct drm_framebuffer_funcs hdlcd_fb_funcs = { + .destroy = hdlcd_fb_destroy, + .create_handle = hdlcd_fb_create_handle, +}; + +static struct hdlcd_fb *hdlcd_fb_alloc(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_cma_object **obj, + unsigned int num_planes, const struct drm_framebuffer_funcs *funcs) +{ + struct hdlcd_fb *hdlcd_fb; + int ret; + int i; + + hdlcd_fb = kzalloc(sizeof(*hdlcd_fb), GFP_KERNEL); + if (!hdlcd_fb) + return ERR_PTR(-ENOMEM); + + drm_helper_mode_fill_fb_struct(dev, &hdlcd_fb->fb, mode_cmd); + + for (i = 0; i < num_planes; i++) + hdlcd_fb->obj[i] = obj[i]; + + ret = drm_framebuffer_init(dev, &hdlcd_fb->fb, funcs); + if (ret) { + dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", ret); + kfree(hdlcd_fb); + return ERR_PTR(ret); + } + + return hdlcd_fb; +} + +/** + * hdlcd_fb_create_with_funcs() - helper function for the + * &drm_mode_config_funcs.fb_create + * callback + * @dev: DRM device + * @file_priv: drm file for the ioctl call + * @mode_cmd: metadata from the userspace fb creation request + * @funcs: vtable to be used for the new framebuffer object + * + * This can be used to set &drm_framebuffer_funcs for drivers that need the + * &drm_framebuffer_funcs.dirty callback. Use hdlcd_fb_create() if you don't + * need to change &drm_framebuffer_funcs. + */ +struct drm_framebuffer *hdlcd_fb_create_with_funcs(struct drm_device *dev, + struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd, + const struct drm_framebuffer_funcs *funcs) +{ + const struct drm_format_info *info; + struct hdlcd_fb *hdlcd_fb; + struct drm_gem_cma_object *objs[4]; + struct drm_gem_object *obj; + int ret; + int i; + + info = drm_format_info(mode_cmd->pixel_format); + if (!info) + return ERR_PTR(-EINVAL); + + for (i = 0; i < info->num_planes; i++) { + unsigned int width = mode_cmd->width / (i ? info->hsub : 1); + unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int min_size; + + obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]); + if (!obj) { + dev_err(dev->dev, "Failed to lookup GEM object\n"); + ret = -ENXIO; + goto err_gem_object_unreference; + } + + min_size = (height - 1) * mode_cmd->pitches[i] + + width * info->cpp[i] + + mode_cmd->offsets[i]; + + if (obj->size < min_size) { + drm_gem_object_unreference_unlocked(obj); + ret = -EINVAL; + goto err_gem_object_unreference; + } + objs[i] = to_drm_gem_cma_obj(obj); + } + + hdlcd_fb = hdlcd_fb_alloc(dev, mode_cmd, objs, i, funcs); + if (IS_ERR(hdlcd_fb)) { + ret = PTR_ERR(hdlcd_fb); + goto err_gem_object_unreference; + } + + return &hdlcd_fb->fb; + +err_gem_object_unreference: + for (i--; i >= 0; i--) + drm_gem_object_unreference_unlocked(&objs[i]->base); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(hdlcd_fb_create_with_funcs); + +/** + * hdlcd_fb_create() - &drm_mode_config_funcs.fb_create callback function + * @dev: DRM device + * @file_priv: drm file for the ioctl call + * @mode_cmd: metadata from the userspace fb creation request + * + * If your hardware has special alignment or pitch requirements these should be + * checked before calling this function. Use hdlcd_fb_create_with_funcs() if + * you need to set &drm_framebuffer_funcs.dirty. + */ +struct drm_framebuffer *hdlcd_fb_create(struct drm_device *dev, + struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) +{ + return hdlcd_fb_create_with_funcs(dev, file_priv, mode_cmd, + &hdlcd_fb_funcs); +} +EXPORT_SYMBOL_GPL(hdlcd_fb_create); + +/** + * hdlcd_fb_get_gem_obj() - Get CMA GEM object for framebuffer + * @fb: The framebuffer + * @plane: Which plane + * + * Return the CMA GEM object for given framebuffer. + * + * This function will usually be called from the CRTC callback functions. + */ +struct drm_gem_cma_object *hdlcd_fb_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane) +{ + struct hdlcd_fb *hdlcd_fb = to_hdlcd_fb(fb); + + if (plane >= 4) + return NULL; + + return hdlcd_fb->obj[plane]; +} +EXPORT_SYMBOL_GPL(hdlcd_fb_get_gem_obj); + +/** + * hdlcd_fb_prepare_fb() - Prepare CMA framebuffer + * @plane: Which plane + * @state: Plane state attach fence to + * + * This should be set as the &struct drm_plane_helper_funcs.prepare_fb hook. + * + * This function checks if the plane FB has an dma-buf attached, extracts + * the exclusive fence and attaches it to plane state for the atomic helper + * to wait on. + * + * There is no need for cleanup_fb for CMA based framebuffer drivers. + */ +int hdlcd_fb_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct dma_buf *dma_buf; + struct dma_fence *fence; + + if ((plane->state->fb == state->fb) || !state->fb) + return 0; + + dma_buf = hdlcd_fb_get_gem_obj(state->fb, 0)->base.dma_buf; + if (dma_buf) { + fence = reservation_object_get_excl_rcu(dma_buf->resv); + drm_atomic_set_fence_for_plane(state, fence); + } + + return 0; +} +EXPORT_SYMBOL_GPL(hdlcd_fb_prepare_fb); + +#ifdef CONFIG_DEBUG_FS +static void hdlcd_fb_describe(struct drm_framebuffer *fb, struct seq_file *m) +{ + struct hdlcd_fb *hdlcd_fb = to_hdlcd_fb(fb); + int i; + + seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, + (char *)&fb->format->format); + + for (i = 0; i < fb->format->num_planes; i++) { + seq_printf(m, " %d: offset=%d pitch=%d, obj: ", + i, fb->offsets[i], fb->pitches[i]); + drm_gem_cma_describe(hdlcd_fb->obj[i], m); + } +} + +/** + * hdlcd_fb_debugfs_show() - Helper to list CMA framebuffer objects + * in debugfs. + * @m: output file + * @arg: private data for the callback + */ +int hdlcd_fb_debugfs_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.fb_lock); + drm_for_each_fb(fb, dev) + hdlcd_fb_describe(fb, m); + mutex_unlock(&dev->mode_config.fb_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(hdlcd_fb_debugfs_show); +#endif + +static int hdlcd_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + return dma_mmap_writecombine(info->device, vma, info->screen_base, + info->fix.smem_start, info->fix.smem_len); +} + +static struct fb_ops hdlcd_drm_fbdev_ops = { + .owner = THIS_MODULE, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, + .fb_mmap = hdlcd_fb_mmap, +}; + +static int hdlcd_drm_fbdev_deferred_io_mmap(struct fb_info *info, + struct vm_area_struct *vma) +{ + fb_deferred_io_mmap(info, vma); + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + return 0; +} + +static int hdlcd_drm_fbdev_defio_init(struct fb_info *fbi, + struct drm_gem_cma_object *cma_obj) +{ + struct fb_deferred_io *fbdefio; + struct fb_ops *fbops; + + /* + * Per device structures are needed because: + * fbops: fb_deferred_io_cleanup() clears fbops.fb_mmap + * fbdefio: individual delays + */ + fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL); + fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); + if (!fbdefio || !fbops) { + kfree(fbdefio); + kfree(fbops); + return -ENOMEM; + } + + /* can't be offset from vaddr since dirty() uses cma_obj */ + fbi->screen_buffer = cma_obj->vaddr; + /* fb_deferred_io_fault() needs a physical address */ + fbi->fix.smem_start = page_to_phys(virt_to_page(fbi->screen_buffer)); + + *fbops = *fbi->fbops; + fbi->fbops = fbops; + + fbdefio->delay = msecs_to_jiffies(DEFAULT_FBDEFIO_DELAY_MS); + fbdefio->deferred_io = drm_fb_helper_deferred_io; + fbi->fbdefio = fbdefio; + fb_deferred_io_init(fbi); + fbi->fbops->fb_mmap = hdlcd_drm_fbdev_deferred_io_mmap; + + return 0; +} + +static void hdlcd_drm_fbdev_defio_fini(struct fb_info *fbi) +{ + if (!fbi->fbdefio) + return; + + fb_deferred_io_cleanup(fbi); + kfree(fbi->fbdefio); + kfree(fbi->fbops); +} + +static int +hdlcd_drm_fbdev_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct hdlcd_drm_fbdev *hdlcd_fbdev = to_hdlcd_fbdev(helper); + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + struct drm_device *dev = helper->dev; + struct drm_gem_cma_object *obj; + struct drm_framebuffer *fb; + unsigned int bytes_per_pixel; + unsigned long offset; + struct fb_info *fbi; + size_t size; + int ret; + + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + size = mode_cmd.pitches[0] * mode_cmd.height; + obj = drm_gem_cma_create(dev, size); + if (IS_ERR(obj)) + return -ENOMEM; + + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { + ret = PTR_ERR(fbi); + goto err_gem_free_object; + } + + hdlcd_fbdev->fb = hdlcd_fb_alloc(dev, &mode_cmd, &obj, 1, + hdlcd_fbdev->fb_funcs); + if (IS_ERR(hdlcd_fbdev->fb)) { + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); + ret = PTR_ERR(hdlcd_fbdev->fb); + goto err_fb_info_destroy; + } + + fb = &hdlcd_fbdev->fb->fb; + helper->fb = fb; + + fbi->par = helper; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->fbops = &hdlcd_drm_fbdev_ops; + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); + + offset = fbi->var.xoffset * bytes_per_pixel; + offset += fbi->var.yoffset * fb->pitches[0]; + + dev->mode_config.fb_base = (resource_size_t)obj->paddr; + fbi->screen_base = obj->vaddr + offset; + fbi->fix.smem_start = (unsigned long)(obj->paddr + offset); + fbi->screen_size = size; + fbi->fix.smem_len = size; + + if (hdlcd_fbdev->fb_funcs->dirty) { + ret = hdlcd_drm_fbdev_defio_init(fbi, obj); + if (ret) + goto err_cma_destroy; + } + + return 0; + +err_cma_destroy: + drm_framebuffer_remove(&hdlcd_fbdev->fb->fb); +err_fb_info_destroy: + drm_fb_helper_release_fbi(helper); +err_gem_free_object: + drm_gem_object_unreference_unlocked(&obj->base); + return ret; +} + +static const struct drm_fb_helper_funcs hdlcd_fb_helper_funcs = { + .fb_probe = hdlcd_drm_fbdev_create, +}; + +/** + * hdlcd_drm_fbdev_init_with_funcs() - Allocate and initializes a hdlcd_drm_fbdev struct + * @dev: DRM device + * @preferred_bpp: Preferred bits per pixel for the device + * @max_conn_count: Maximum number of connectors + * @funcs: fb helper functions, in particular a custom dirty() callback + * + * Returns a newly allocated hdlcd_drm_fbdev struct or a ERR_PTR. + */ +struct hdlcd_drm_fbdev *hdlcd_drm_fbdev_init_with_funcs(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int max_conn_count, + const struct drm_framebuffer_funcs *funcs) +{ + struct hdlcd_drm_fbdev *hdlcd_fbdev; + struct drm_fb_helper *helper; + int ret; + + hdlcd_fbdev = kzalloc(sizeof(*hdlcd_fbdev), GFP_KERNEL); + if (!hdlcd_fbdev) { + dev_err(dev->dev, "Failed to allocate drm fbdev.\n"); + return ERR_PTR(-ENOMEM); + } + hdlcd_fbdev->fb_funcs = funcs; + + helper = &hdlcd_fbdev->fb_helper; + + drm_fb_helper_prepare(dev, helper, &hdlcd_fb_helper_funcs); + + ret = drm_fb_helper_init(dev, helper, max_conn_count); + if (ret < 0) { + dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); + goto err_free; + } + + ret = drm_fb_helper_single_add_all_connectors(helper); + if (ret < 0) { + dev_err(dev->dev, "Failed to add connectors.\n"); + goto err_drm_fb_helper_fini; + + } + + ret = drm_fb_helper_initial_config(helper, preferred_bpp); + if (ret < 0) { + dev_err(dev->dev, "Failed to set initial hw configuration.\n"); + goto err_drm_fb_helper_fini; + } + + return hdlcd_fbdev; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(helper); +err_free: + kfree(hdlcd_fbdev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(hdlcd_drm_fbdev_init_with_funcs); + +/** + * hdlcd_drm_fbdev_init() - Allocate and initializes a hdlcd_drm_fbdev struct + * @dev: DRM device + * @preferred_bpp: Preferred bits per pixel for the device + * @num_crtc: Number of CRTCs + * @max_conn_count: Maximum number of connectors + * + * Returns a newly allocated hdlcd_drm_fbdev struct or a ERR_PTR. + */ +struct hdlcd_drm_fbdev *hdlcd_drm_fbdev_init(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int max_conn_count) +{ + return hdlcd_drm_fbdev_init_with_funcs(dev, preferred_bpp, + max_conn_count, + &hdlcd_fb_funcs); +} +EXPORT_SYMBOL_GPL(hdlcd_drm_fbdev_init); + +/** + * hdlcd_drm_fbdev_fini() - Free hdlcd_drm_fbdev struct + * @hdlcd_fbdev: The hdlcd_drm_fbdev struct + */ +void hdlcd_drm_fbdev_fini(struct hdlcd_drm_fbdev *hdlcd_fbdev) +{ + drm_fb_helper_unregister_fbi(&hdlcd_fbdev->fb_helper); + if (hdlcd_fbdev->fb_helper.fbdev) + hdlcd_drm_fbdev_defio_fini(hdlcd_fbdev->fb_helper.fbdev); + drm_fb_helper_release_fbi(&hdlcd_fbdev->fb_helper); + + if (hdlcd_fbdev->fb) + drm_framebuffer_remove(&hdlcd_fbdev->fb->fb); + + drm_fb_helper_fini(&hdlcd_fbdev->fb_helper); + kfree(hdlcd_fbdev); +} +EXPORT_SYMBOL_GPL(hdlcd_drm_fbdev_fini); + +/** + * hdlcd_drm_fbdev_restore_mode() - Restores initial framebuffer mode + * @hdlcd_fbdev: The hdlcd_drm_fbdev struct, may be NULL + * + * This function is usually called from the &drm_driver.lastclose callback. + */ +void hdlcd_drm_fbdev_restore_mode(struct hdlcd_drm_fbdev *hdlcd_fbdev) +{ + if (hdlcd_fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(&hdlcd_fbdev->fb_helper); +} +EXPORT_SYMBOL_GPL(hdlcd_drm_fbdev_restore_mode); + +/** + * hdlcd_drm_fbdev_hotplug_event() - Poll for hotpulug events + * @hdlcd_fbdev: The hdlcd_drm_fbdev struct, may be NULL + * + * This function is usually called from the &drm_mode_config.output_poll_changed + * callback. + */ +void hdlcd_drm_fbdev_hotplug_event(struct hdlcd_drm_fbdev *hdlcd_fbdev) +{ + if (hdlcd_fbdev) + drm_fb_helper_hotplug_event(&hdlcd_fbdev->fb_helper); +} +EXPORT_SYMBOL_GPL(hdlcd_drm_fbdev_hotplug_event); + +/** + * hdlcd_drm_fbdev_set_suspend - wrapper around drm_fb_helper_set_suspend + * @hdlcd_fbdev: The hdlcd_drm_fbdev struct, may be NULL + * @state: desired state, zero to resume, non-zero to suspend + * + * Calls drm_fb_helper_set_suspend, which is a wrapper around + * fb_set_suspend implemented by fbdev core. + */ +void hdlcd_drm_fbdev_set_suspend(struct hdlcd_drm_fbdev *hdlcd_fbdev, int state) +{ + if (hdlcd_fbdev) + drm_fb_helper_set_suspend(&hdlcd_fbdev->fb_helper, state); +} +EXPORT_SYMBOL(hdlcd_drm_fbdev_set_suspend); + +/** + * hdlcd_drm_fbdev_set_suspend_unlocked - wrapper around + * drm_fb_helper_set_suspend_unlocked + * @hdlcd_fbdev: The hdlcd_drm_fbdev struct, may be NULL + * @state: desired state, zero to resume, non-zero to suspend + * + * Calls drm_fb_helper_set_suspend, which is a wrapper around + * fb_set_suspend implemented by fbdev core. + */ +void hdlcd_drm_fbdev_set_suspend_unlocked(struct hdlcd_drm_fbdev *hdlcd_fbdev, + int state) +{ + if (hdlcd_fbdev) + drm_fb_helper_set_suspend_unlocked(&hdlcd_fbdev->fb_helper, + state); +} +EXPORT_SYMBOL(hdlcd_drm_fbdev_set_suspend_unlocked); diff --git a/drivers/gpu/drm/arm/hdlcd_fb_helper.h b/drivers/gpu/drm/arm/hdlcd_fb_helper.h new file mode 100644 index 000000000000..aee0d9037561 --- /dev/null +++ b/drivers/gpu/drm/arm/hdlcd_fb_helper.h @@ -0,0 +1,54 @@ +#ifndef __DRM_FB_CMA_HELPER_H__ +#define __DRM_FB_CMA_HELPER_H__ + +struct hdlcd_drm_fbdev; +struct drm_gem_cma_object; + +struct drm_fb_helper_surface_size; +struct drm_framebuffer_funcs; +struct drm_fb_helper_funcs; +struct drm_framebuffer; +struct drm_fb_helper; +struct drm_device; +struct drm_file; +struct drm_mode_fb_cmd2; +struct drm_plane; +struct drm_plane_state; + +struct hdlcd_drm_fbdev *hdlcd_drm_fbdev_init_with_funcs(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int max_conn_count, + const struct drm_framebuffer_funcs *funcs); +struct hdlcd_drm_fbdev *hdlcd_drm_fbdev_init(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int max_conn_count); +void hdlcd_drm_fbdev_fini(struct hdlcd_drm_fbdev *hdlcd_fbdev); + +void hdlcd_drm_fbdev_restore_mode(struct hdlcd_drm_fbdev *hdlcd_fbdev); +void hdlcd_drm_fbdev_hotplug_event(struct hdlcd_drm_fbdev *hdlcd_fbdev); +void hdlcd_drm_fbdev_set_suspend(struct hdlcd_drm_fbdev *hdlcd_fbdev, int state); +void hdlcd_drm_fbdev_set_suspend_unlocked(struct hdlcd_drm_fbdev *hdlcd_fbdev, + int state); + +void hdlcd_fb_destroy(struct drm_framebuffer *fb); +int hdlcd_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned int *handle); + +struct drm_framebuffer *hdlcd_fb_create_with_funcs(struct drm_device *dev, + struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd, + const struct drm_framebuffer_funcs *funcs); +struct drm_framebuffer *hdlcd_fb_create(struct drm_device *dev, + struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd); + +struct drm_gem_cma_object *hdlcd_fb_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane); + +int hdlcd_fb_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *state); + +#ifdef CONFIG_DEBUG_FS +struct seq_file; + +int hdlcd_fb_debugfs_show(struct seq_file *m, void *arg); +#endif + +#endif + |