diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 2bf6b589b957..bc18ae5501f6 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -6,6 +6,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchip_drm_gem.o rockchip_drm_logo.o rockchip_drm_clk.o\ +rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_DRM_DEBUG) += rockchip_drm_debugfs.o rockchipdrm-$(CONFIG_ROCKCHIP_DRM_DIRECT_SHOW) += rockchip_drm_direct_show.o rockchipdrm-$(CONFIG_ROCKCHIP_DRM_SELF_TEST) += rockchip_drm_display_pattern.o \ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 6f7bd9e2d933..b8d06d221c4a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -41,6 +41,7 @@ #include "rockchip_drm_drv.h" #include "rockchip_drm_fb.h" +#include "rockchip_drm_fbdev.h" #include "rockchip_drm_gem.h" #include "rockchip_drm_logo.h" @@ -1722,11 +1723,19 @@ static int rockchip_drm_bind(struct device *dev) if (ret) goto err_kms_helper_poll_fini; - drm_fbdev_generic_setup(drm_dev, 0); + ret = rockchip_drm_fbdev_init(drm_dev); + if (ret) + goto err_drm_dev_unregister; - rockchip_drm_sysfs_init(drm_dev); + ret = rockchip_drm_sysfs_init(drm_dev); + if (ret) + goto err_drm_fbdev_fini; return 0; +err_drm_fbdev_fini: + rockchip_drm_fbdev_fini(drm_dev); +err_drm_dev_unregister: + drm_dev_unregister(drm_dev); err_kms_helper_poll_fini: rockchip_gem_pool_destroy(drm_dev); drm_kms_helper_poll_fini(drm_dev); @@ -1747,6 +1756,7 @@ static void rockchip_drm_unbind(struct device *dev) struct drm_device *drm_dev = dev_get_drvdata(dev); rockchip_drm_sysfs_fini(drm_dev); + rockchip_drm_fbdev_fini(drm_dev); drm_dev_unregister(drm_dev); rockchip_gem_pool_destroy(drm_dev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index c513ad2c6f6f..97e756101440 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -389,6 +389,20 @@ static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; +struct drm_framebuffer * +rockchip_drm_framebuffer_init(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + struct drm_framebuffer *fb; + + fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); + if (IS_ERR(fb)) + return ERR_CAST(fb); + + return fb; +} + void rockchip_drm_mode_config_init(struct drm_device *dev) { dev->mode_config.min_width = 0; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h index 6c4294d78a83..ccb26cbd6031 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h @@ -11,6 +11,10 @@ #include "rockchip_drm_gem.h" #define ROCKCHIP_DRM_MODE_LOGO_FB (1<<31) /* used for kernel logo, follow the define: DRM_MODE_FB_MODIFIERS at drm_mode.h */ +struct drm_framebuffer * +rockchip_drm_framebuffer_init(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj); void rockchip_drm_mode_config_init(struct drm_device *dev); struct drm_framebuffer * diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c new file mode 100644 index 000000000000..bd449be5b9c0 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author:Mark Yao + */ + +#include +#include +#include +#include + +#include "rockchip_drm_drv.h" +#include "rockchip_drm_gem.h" +#include "rockchip_drm_fb.h" +#include "rockchip_drm_fbdev.h" + +#define PREFERRED_BPP 32 + +static int rockchip_fbdev_mmap(struct fb_info *info, + struct vm_area_struct *vma) +{ + struct drm_fb_helper *helper = info->par; + struct rockchip_drm_private *private = helper->dev->dev_private; + + return rockchip_gem_mmap_buf(private->fbdev_bo, vma); +} + +static const struct fb_ops rockchip_drm_fbdev_ops = { + .owner = THIS_MODULE, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_mmap = rockchip_fbdev_mmap, + .fb_fillrect = drm_fb_helper_cfb_fillrect, + .fb_copyarea = drm_fb_helper_cfb_copyarea, + .fb_imageblit = drm_fb_helper_cfb_imageblit, +}; + +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct rockchip_drm_private *private = helper->dev->dev_private; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + struct drm_device *dev = helper->dev; + struct rockchip_gem_object *rk_obj; + struct drm_framebuffer *fb; + unsigned int bytes_per_pixel; + unsigned long offset; + struct fb_info *fbi; + size_t size; + int ret; + + 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; + + rk_obj = rockchip_gem_create_object(dev, size, true, 0); + if (IS_ERR(rk_obj)) + return -ENOMEM; + + private->fbdev_bo = &rk_obj->base; + + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { + DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info.\n"); + ret = PTR_ERR(fbi); + goto out; + } + + helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, + private->fbdev_bo); + if (IS_ERR(helper->fb)) { + DRM_DEV_ERROR(dev->dev, + "Failed to allocate DRM framebuffer.\n"); + ret = PTR_ERR(helper->fb); + goto out; + } + + fbi->fbops = &rockchip_drm_fbdev_ops; + + fb = helper->fb; + drm_fb_helper_fill_info(fbi, helper, sizes); + + offset = fbi->var.xoffset * bytes_per_pixel; + offset += fbi->var.yoffset * fb->pitches[0]; + + dev->mode_config.fb_base = 0; + fbi->screen_base = rk_obj->kvaddr + offset; + fbi->screen_size = rk_obj->base.size; + fbi->fix.smem_len = rk_obj->base.size; + + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%zu\n", + fb->width, fb->height, fb->format->depth, + rk_obj->kvaddr, + offset, size); + + return 0; + +out: + drm_gem_object_put(&rk_obj->base); + return ret; +} + +static const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = { + .fb_probe = rockchip_drm_fbdev_create, +}; + +int rockchip_drm_fbdev_init(struct drm_device *dev) +{ + struct rockchip_drm_private *private = dev->dev_private; + struct drm_fb_helper *helper; + int ret; + + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) + return -EINVAL; + + helper = devm_kzalloc(dev->dev, sizeof(*helper), GFP_KERNEL); + if (!helper) + return -ENOMEM; + private->fbdev_helper = helper; + + drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs); + + ret = drm_fb_helper_init(dev, helper); + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, + "Failed to initialize drm fb helper - %d.\n", + ret); + return ret; + } + + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); + if (ret < 0) { + DRM_DEV_ERROR(dev->dev, + "Failed to set initial hw config - %d.\n", + ret); + goto err_drm_fb_helper_fini; + } + + return 0; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(helper); + return ret; +} + +void rockchip_drm_fbdev_fini(struct drm_device *dev) +{ + struct rockchip_drm_private *private = dev->dev_private; + struct drm_fb_helper *helper = private->fbdev_helper; + + if (!helper) + return; + + drm_fb_helper_unregister_fbi(helper); + + if (helper->fb) + drm_framebuffer_put(helper->fb); + + drm_fb_helper_fini(helper); +} diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h new file mode 100644 index 000000000000..d0a588e17678 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author:Mark Yao + */ + +#ifndef _ROCKCHIP_DRM_FBDEV_H +#define _ROCKCHIP_DRM_FBDEV_H + +#if defined(CONFIG_DRM_FBDEV_EMULATION) +int rockchip_drm_fbdev_init(struct drm_device *dev); +void rockchip_drm_fbdev_fini(struct drm_device *dev); +#else +static inline int rockchip_drm_fbdev_init(struct drm_device *dev) +{ + return 0; +} + +static inline void rockchip_drm_fbdev_fini(struct drm_device *dev) +{ +} +#endif + +#endif /* _ROCKCHIP_DRM_FBDEV_H */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 25e5f4818e08..a75dbfed016f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -594,6 +594,18 @@ const struct drm_gem_object_funcs rockchip_gem_object_funcs = { .vm_ops = &drm_gem_dma_vm_ops, }; +int rockchip_gem_mmap_buf(struct drm_gem_object *obj, + struct vm_area_struct *vma) +{ + int ret; + + ret = drm_gem_mmap_obj(obj, obj->size, vma); + if (ret) + return ret; + + return rockchip_drm_gem_object_mmap(obj, vma); +} + static struct rockchip_gem_object * rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size, unsigned int flags) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index 5b434cb1e494..af588adb31f9 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -54,6 +54,10 @@ void rockchip_gem_free_object(struct drm_gem_object *obj); int rockchip_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); +/* mmap a gem object to userspace. */ +int rockchip_gem_mmap_buf(struct drm_gem_object *obj, + struct vm_area_struct *vma); + /* * request gem object creation and buffer allocation as the size * that it is calculated with framebuffer information such as width,