18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 48c2ecf20Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <drm/drm.h> 88c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "rockchip_drm_drv.h" 138c2ecf20Sopenharmony_ci#include "rockchip_drm_gem.h" 148c2ecf20Sopenharmony_ci#include "rockchip_drm_fb.h" 158c2ecf20Sopenharmony_ci#include "rockchip_drm_fbdev.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define PREFERRED_BPP 32 188c2ecf20Sopenharmony_ci#define to_drm_private(x) \ 198c2ecf20Sopenharmony_ci container_of(x, struct rockchip_drm_private, fbdev_helper) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int rockchip_fbdev_mmap(struct fb_info *info, 228c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct drm_fb_helper *helper = info->par; 258c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = to_drm_private(helper); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci return rockchip_gem_mmap_buf(private->fbdev_bo, vma); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic const struct fb_ops rockchip_drm_fbdev_ops = { 318c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 328c2ecf20Sopenharmony_ci DRM_FB_HELPER_DEFAULT_OPS, 338c2ecf20Sopenharmony_ci .fb_mmap = rockchip_fbdev_mmap, 348c2ecf20Sopenharmony_ci .fb_fillrect = drm_fb_helper_cfb_fillrect, 358c2ecf20Sopenharmony_ci .fb_copyarea = drm_fb_helper_cfb_copyarea, 368c2ecf20Sopenharmony_ci .fb_imageblit = drm_fb_helper_cfb_imageblit, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, 408c2ecf20Sopenharmony_ci struct drm_fb_helper_surface_size *sizes) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = to_drm_private(helper); 438c2ecf20Sopenharmony_ci struct drm_mode_fb_cmd2 mode_cmd = { 0 }; 448c2ecf20Sopenharmony_ci struct drm_device *dev = helper->dev; 458c2ecf20Sopenharmony_ci struct rockchip_gem_object *rk_obj; 468c2ecf20Sopenharmony_ci struct drm_framebuffer *fb; 478c2ecf20Sopenharmony_ci unsigned int bytes_per_pixel; 488c2ecf20Sopenharmony_ci unsigned long offset; 498c2ecf20Sopenharmony_ci struct fb_info *fbi; 508c2ecf20Sopenharmony_ci size_t size; 518c2ecf20Sopenharmony_ci int ret; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci mode_cmd.width = sizes->surface_width; 568c2ecf20Sopenharmony_ci mode_cmd.height = sizes->surface_height; 578c2ecf20Sopenharmony_ci mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; 588c2ecf20Sopenharmony_ci mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 598c2ecf20Sopenharmony_ci sizes->surface_depth); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci size = mode_cmd.pitches[0] * mode_cmd.height; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci rk_obj = rockchip_gem_create_object(dev, size, true); 648c2ecf20Sopenharmony_ci if (IS_ERR(rk_obj)) 658c2ecf20Sopenharmony_ci return -ENOMEM; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci private->fbdev_bo = &rk_obj->base; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci fbi = drm_fb_helper_alloc_fbi(helper); 708c2ecf20Sopenharmony_ci if (IS_ERR(fbi)) { 718c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info.\n"); 728c2ecf20Sopenharmony_ci ret = PTR_ERR(fbi); 738c2ecf20Sopenharmony_ci goto out; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, 778c2ecf20Sopenharmony_ci private->fbdev_bo); 788c2ecf20Sopenharmony_ci if (IS_ERR(helper->fb)) { 798c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, 808c2ecf20Sopenharmony_ci "Failed to allocate DRM framebuffer.\n"); 818c2ecf20Sopenharmony_ci ret = PTR_ERR(helper->fb); 828c2ecf20Sopenharmony_ci goto out; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci fbi->fbops = &rockchip_drm_fbdev_ops; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci fb = helper->fb; 888c2ecf20Sopenharmony_ci drm_fb_helper_fill_info(fbi, helper, sizes); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci offset = fbi->var.xoffset * bytes_per_pixel; 918c2ecf20Sopenharmony_ci offset += fbi->var.yoffset * fb->pitches[0]; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci dev->mode_config.fb_base = 0; 948c2ecf20Sopenharmony_ci fbi->screen_base = rk_obj->kvaddr + offset; 958c2ecf20Sopenharmony_ci fbi->screen_size = rk_obj->base.size; 968c2ecf20Sopenharmony_ci fbi->fix.smem_len = rk_obj->base.size; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%zu\n", 998c2ecf20Sopenharmony_ci fb->width, fb->height, fb->format->depth, 1008c2ecf20Sopenharmony_ci rk_obj->kvaddr, 1018c2ecf20Sopenharmony_ci offset, size); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ciout: 1068c2ecf20Sopenharmony_ci rockchip_gem_free_object(&rk_obj->base); 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = { 1118c2ecf20Sopenharmony_ci .fb_probe = rockchip_drm_fbdev_create, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciint rockchip_drm_fbdev_init(struct drm_device *dev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = dev->dev_private; 1178c2ecf20Sopenharmony_ci struct drm_fb_helper *helper; 1188c2ecf20Sopenharmony_ci int ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci helper = &private->fbdev_helper; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = drm_fb_helper_init(dev, helper); 1288c2ecf20Sopenharmony_ci if (ret < 0) { 1298c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, 1308c2ecf20Sopenharmony_ci "Failed to initialize drm fb helper - %d.\n", 1318c2ecf20Sopenharmony_ci ret); 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); 1368c2ecf20Sopenharmony_ci if (ret < 0) { 1378c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, 1388c2ecf20Sopenharmony_ci "Failed to set initial hw config - %d.\n", 1398c2ecf20Sopenharmony_ci ret); 1408c2ecf20Sopenharmony_ci goto err_drm_fb_helper_fini; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cierr_drm_fb_helper_fini: 1468c2ecf20Sopenharmony_ci drm_fb_helper_fini(helper); 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_civoid rockchip_drm_fbdev_fini(struct drm_device *dev) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct rockchip_drm_private *private = dev->dev_private; 1538c2ecf20Sopenharmony_ci struct drm_fb_helper *helper; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci helper = &private->fbdev_helper; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci drm_fb_helper_unregister_fbi(helper); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (helper->fb) 1608c2ecf20Sopenharmony_ci drm_framebuffer_put(helper->fb); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci drm_fb_helper_fini(helper); 1638c2ecf20Sopenharmony_ci} 164