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 <linux/kernel.h> 16#include <drm/drm.h> 17#include <drm/drmP.h> 18#include <drm/drm_fb_helper.h> 19#include <drm/drm_crtc_helper.h> 20 21#include "rockchip_drm_drv.h" 22#include "rockchip_drm_gem.h" 23 24#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) 25 26struct rockchip_drm_fb { 27 struct drm_framebuffer fb; 28 struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; 29}; 30 31struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb, 32 unsigned int plane) 33{ 34 struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb); 35 36 if (plane >= ROCKCHIP_MAX_FB_BUFFER) 37 return NULL; 38 39 return rk_fb->obj[plane]; 40} 41EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj); 42 43static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb) 44{ 45 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 46 struct drm_gem_object *obj; 47 int i; 48 49 for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) { 50 obj = rockchip_fb->obj[i]; 51 if (obj) 52 drm_gem_object_unreference_unlocked(obj); 53 } 54 55 drm_framebuffer_cleanup(fb); 56 kfree(rockchip_fb); 57} 58 59static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb, 60 struct drm_file *file_priv, 61 unsigned int *handle) 62{ 63 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 64 65 return drm_gem_handle_create(file_priv, 66 rockchip_fb->obj[0], handle); 67} 68 69static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { 70 .destroy = rockchip_drm_fb_destroy, 71 .create_handle = rockchip_drm_fb_create_handle, 72}; 73 74static struct rockchip_drm_fb * 75rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, 76 struct drm_gem_object **obj, unsigned int num_planes) 77{ 78 struct rockchip_drm_fb *rockchip_fb; 79 int ret; 80 int i; 81 82 rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL); 83 if (!rockchip_fb) 84 return ERR_PTR(-ENOMEM); 85 86 drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd); 87 88 for (i = 0; i < num_planes; i++) 89 rockchip_fb->obj[i] = obj[i]; 90 91 ret = drm_framebuffer_init(dev, &rockchip_fb->fb, 92 &rockchip_drm_fb_funcs); 93 if (ret) { 94 dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", 95 ret); 96 kfree(rockchip_fb); 97 return ERR_PTR(ret); 98 } 99 100 return rockchip_fb; 101} 102 103static struct drm_framebuffer * 104rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, 105 struct drm_mode_fb_cmd2 *mode_cmd) 106{ 107 struct rockchip_drm_fb *rockchip_fb; 108 struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; 109 struct drm_gem_object *obj; 110 unsigned int hsub; 111 unsigned int vsub; 112 int num_planes; 113 int ret; 114 int i; 115 116 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); 117 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); 118 num_planes = min(drm_format_num_planes(mode_cmd->pixel_format), 119 ROCKCHIP_MAX_FB_BUFFER); 120 121 for (i = 0; i < num_planes; i++) { 122 unsigned int width = mode_cmd->width / (i ? hsub : 1); 123 unsigned int height = mode_cmd->height / (i ? vsub : 1); 124 unsigned int min_size; 125 126 obj = drm_gem_object_lookup(dev, file_priv, 127 mode_cmd->handles[i]); 128 if (!obj) { 129 dev_err(dev->dev, "Failed to lookup GEM object\n"); 130 ret = -ENXIO; 131 goto err_gem_object_unreference; 132 } 133 134 min_size = (height - 1) * mode_cmd->pitches[i] + 135 mode_cmd->offsets[i] + 136 width * drm_format_plane_cpp(mode_cmd->pixel_format, i); 137 138 if (obj->size < min_size) { 139 drm_gem_object_unreference_unlocked(obj); 140 ret = -EINVAL; 141 goto err_gem_object_unreference; 142 } 143 objs[i] = obj; 144 } 145 146 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i); 147 if (IS_ERR(rockchip_fb)) { 148 ret = PTR_ERR(rockchip_fb); 149 goto err_gem_object_unreference; 150 } 151 152 return &rockchip_fb->fb; 153 154err_gem_object_unreference: 155 for (i--; i >= 0; i--) 156 drm_gem_object_unreference_unlocked(objs[i]); 157 return ERR_PTR(ret); 158} 159 160static void rockchip_drm_output_poll_changed(struct drm_device *dev) 161{ 162 struct rockchip_drm_private *private = dev->dev_private; 163 struct drm_fb_helper *fb_helper = &private->fbdev_helper; 164 165 drm_fb_helper_hotplug_event(fb_helper); 166} 167 168static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { 169 .fb_create = rockchip_user_fb_create, 170 .output_poll_changed = rockchip_drm_output_poll_changed, 171}; 172 173struct drm_framebuffer * 174rockchip_drm_framebuffer_init(struct drm_device *dev, 175 struct drm_mode_fb_cmd2 *mode_cmd, 176 struct drm_gem_object *obj) 177{ 178 struct rockchip_drm_fb *rockchip_fb; 179 180 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); 181 if (IS_ERR(rockchip_fb)) 182 return NULL; 183 184 return &rockchip_fb->fb; 185} 186 187void rockchip_drm_mode_config_init(struct drm_device *dev) 188{ 189 dev->mode_config.min_width = 0; 190 dev->mode_config.min_height = 0; 191 192 /* 193 * set max width and height as default value(4096x4096). 194 * this value would be used to check framebuffer size limitation 195 * at drm_mode_addfb(). 196 */ 197 dev->mode_config.max_width = 4096; 198 dev->mode_config.max_height = 4096; 199 200 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; 201} 202