1/* 2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 * Author:Mark Yao <mark.yao@rock-chips.com> 4 * 5 * This software is licensed under the terms of the GNU General Public 6 * License version 2, as published by the Free Software Foundation, and 7 * may be copied, distributed, and modified under those terms. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15#include <drm/drm.h> 16#include <drm/drmP.h> 17#include <drm/drm_fb_helper.h> 18#include <drm/drm_crtc_helper.h> 19 20#include "rockchip_drm_drv.h" 21#include "rockchip_drm_gem.h" 22#include "rockchip_drm_fb.h" 23 24#define PREFERRED_BPP 32 25#define to_drm_private(x) \ 26 container_of(x, struct rockchip_drm_private, fbdev_helper) 27 28static int rockchip_fbdev_mmap(struct fb_info *info, 29 struct vm_area_struct *vma) 30{ 31 struct drm_fb_helper *helper = info->par; 32 struct rockchip_drm_private *private = to_drm_private(helper); 33 34 return rockchip_gem_mmap_buf(private->fbdev_bo, vma); 35} 36 37static struct fb_ops rockchip_drm_fbdev_ops = { 38 .owner = THIS_MODULE, 39 .fb_mmap = rockchip_fbdev_mmap, 40 .fb_fillrect = cfb_fillrect, 41 .fb_copyarea = cfb_copyarea, 42 .fb_imageblit = cfb_imageblit, 43 .fb_check_var = drm_fb_helper_check_var, 44 .fb_set_par = drm_fb_helper_set_par, 45 .fb_blank = drm_fb_helper_blank, 46 .fb_pan_display = drm_fb_helper_pan_display, 47 .fb_setcmap = drm_fb_helper_setcmap, 48}; 49 50static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, 51 struct drm_fb_helper_surface_size *sizes) 52{ 53 struct rockchip_drm_private *private = to_drm_private(helper); 54 struct drm_mode_fb_cmd2 mode_cmd = { 0 }; 55 struct drm_device *dev = helper->dev; 56 struct rockchip_gem_object *rk_obj; 57 struct drm_framebuffer *fb; 58 unsigned int bytes_per_pixel; 59 unsigned long offset; 60 struct fb_info *fbi; 61 size_t size; 62 int ret; 63 64 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 65 66 mode_cmd.width = sizes->surface_width; 67 mode_cmd.height = sizes->surface_height; 68 mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; 69 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 70 sizes->surface_depth); 71 72 size = mode_cmd.pitches[0] * mode_cmd.height; 73 74 rk_obj = rockchip_gem_create_object(dev, size, true); 75 if (IS_ERR(rk_obj)) 76 return -ENOMEM; 77 78 private->fbdev_bo = &rk_obj->base; 79 80 fbi = framebuffer_alloc(0, dev->dev); 81 if (!fbi) { 82 dev_err(dev->dev, "Failed to allocate framebuffer info.\n"); 83 ret = -ENOMEM; 84 goto err_rockchip_gem_free_object; 85 } 86 87 helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, 88 private->fbdev_bo); 89 if (IS_ERR(helper->fb)) { 90 dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); 91 ret = PTR_ERR(helper->fb); 92 goto err_framebuffer_release; 93 } 94 95 helper->fbdev = fbi; 96 97 fbi->par = helper; 98 fbi->flags = FBINFO_FLAG_DEFAULT; 99 fbi->fbops = &rockchip_drm_fbdev_ops; 100 101 ret = fb_alloc_cmap(&fbi->cmap, 256, 0); 102 if (ret) { 103 dev_err(dev->dev, "Failed to allocate color map.\n"); 104 goto err_drm_framebuffer_unref; 105 } 106 107 fb = helper->fb; 108 drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); 109 drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); 110 111 offset = fbi->var.xoffset * bytes_per_pixel; 112 offset += fbi->var.yoffset * fb->pitches[0]; 113 114 dev->mode_config.fb_base = 0; 115 fbi->screen_base = rk_obj->kvaddr + offset; 116 fbi->screen_size = rk_obj->base.size; 117 fbi->fix.smem_len = rk_obj->base.size; 118 119 DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n", 120 fb->width, fb->height, fb->depth, rk_obj->kvaddr, 121 offset, size); 122 123 fbi->skip_vt_switch = true; 124 125 return 0; 126 127err_drm_framebuffer_unref: 128 drm_framebuffer_unreference(helper->fb); 129err_framebuffer_release: 130 framebuffer_release(fbi); 131err_rockchip_gem_free_object: 132 rockchip_gem_free_object(&rk_obj->base); 133 return ret; 134} 135 136static const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = { 137 .fb_probe = rockchip_drm_fbdev_create, 138}; 139 140int rockchip_drm_fbdev_init(struct drm_device *dev) 141{ 142 struct rockchip_drm_private *private = dev->dev_private; 143 struct drm_fb_helper *helper; 144 unsigned int num_crtc; 145 int ret; 146 147 if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) 148 return -EINVAL; 149 150 num_crtc = dev->mode_config.num_crtc; 151 152 helper = &private->fbdev_helper; 153 154 drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs); 155 156 ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR); 157 if (ret < 0) { 158 dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n", 159 ret); 160 return ret; 161 } 162 163 ret = drm_fb_helper_single_add_all_connectors(helper); 164 if (ret < 0) { 165 dev_err(dev->dev, "Failed to add connectors - %d.\n", ret); 166 goto err_drm_fb_helper_fini; 167 } 168 169 /* disable all the possible outputs/crtcs before entering KMS mode */ 170 drm_helper_disable_unused_functions(dev); 171 172 ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); 173 if (ret < 0) { 174 dev_err(dev->dev, "Failed to set initial hw config - %d.\n", 175 ret); 176 goto err_drm_fb_helper_fini; 177 } 178 179 return 0; 180 181err_drm_fb_helper_fini: 182 drm_fb_helper_fini(helper); 183 return ret; 184} 185 186void rockchip_drm_fbdev_fini(struct drm_device *dev) 187{ 188 struct rockchip_drm_private *private = dev->dev_private; 189 struct drm_fb_helper *helper; 190 191 helper = &private->fbdev_helper; 192 193 if (helper->fbdev) { 194 struct fb_info *info; 195 int ret; 196 197 info = helper->fbdev; 198 ret = unregister_framebuffer(info); 199 if (ret < 0) 200 DRM_DEBUG_KMS("failed unregister_framebuffer() - %d\n", 201 ret); 202 203 if (info->cmap.len) 204 fb_dealloc_cmap(&info->cmap); 205 206 framebuffer_release(info); 207 } 208 209 if (helper->fb) 210 drm_framebuffer_unreference(helper->fb); 211 212 drm_fb_helper_fini(helper); 213} 214