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_atomic.h>
19 #include <drm/drm_fb_helper.h>
20 #include <drm/drm_crtc_helper.h>
21 
22 #include "rockchip_drm_drv.h"
23 #include "rockchip_drm_gem.h"
24 
25 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
26 
27 struct rockchip_drm_fb {
28 	struct drm_framebuffer fb;
29 	struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
30 };
31 
32 struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
33 					       unsigned int plane)
34 {
35 	struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
36 
37 	if (plane >= ROCKCHIP_MAX_FB_BUFFER)
38 		return NULL;
39 
40 	return rk_fb->obj[plane];
41 }
42 
43 static 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 
59 static 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 
69 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
70 	.destroy	= rockchip_drm_fb_destroy,
71 	.create_handle	= rockchip_drm_fb_create_handle,
72 };
73 
74 static struct rockchip_drm_fb *
75 rockchip_fb_alloc(struct drm_device *dev, const 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 
103 static struct drm_framebuffer *
104 rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
105 			const 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(file_priv, mode_cmd->handles[i]);
127 		if (!obj) {
128 			dev_err(dev->dev, "Failed to lookup GEM object\n");
129 			ret = -ENXIO;
130 			goto err_gem_object_unreference;
131 		}
132 
133 		min_size = (height - 1) * mode_cmd->pitches[i] +
134 			mode_cmd->offsets[i] +
135 			width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
136 
137 		if (obj->size < min_size) {
138 			drm_gem_object_unreference_unlocked(obj);
139 			ret = -EINVAL;
140 			goto err_gem_object_unreference;
141 		}
142 		objs[i] = obj;
143 	}
144 
145 	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
146 	if (IS_ERR(rockchip_fb)) {
147 		ret = PTR_ERR(rockchip_fb);
148 		goto err_gem_object_unreference;
149 	}
150 
151 	return &rockchip_fb->fb;
152 
153 err_gem_object_unreference:
154 	for (i--; i >= 0; i--)
155 		drm_gem_object_unreference_unlocked(objs[i]);
156 	return ERR_PTR(ret);
157 }
158 
159 static void rockchip_drm_output_poll_changed(struct drm_device *dev)
160 {
161 	struct rockchip_drm_private *private = dev->dev_private;
162 	struct drm_fb_helper *fb_helper = &private->fbdev_helper;
163 
164 	if (fb_helper)
165 		drm_fb_helper_hotplug_event(fb_helper);
166 }
167 
168 static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
169 {
170 	struct rockchip_drm_private *priv = crtc->dev->dev_private;
171 	int pipe = drm_crtc_index(crtc);
172 	const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];
173 
174 	if (crtc_funcs && crtc_funcs->wait_for_update)
175 		crtc_funcs->wait_for_update(crtc);
176 }
177 
178 /*
179  * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
180  * have hardware counters for neither vblanks nor scanlines, which results in
181  * a race where:
182  *				| <-- HW vsync irq and reg take effect
183  *	       plane_commit --> |
184  *	get_vblank and wait --> |
185  *				| <-- handle_vblank, vblank->count + 1
186  *		 cleanup_fb --> |
187  *		iommu crash --> |
188  *				| <-- HW vsync irq and reg take effect
189  *
190  * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
191  * of waiting for vblank_count to change.
192  */
193 static void
194 rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
195 {
196 	struct drm_crtc_state *old_crtc_state;
197 	struct drm_crtc *crtc;
198 	int i, ret;
199 
200 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
201 		/* No one cares about the old state, so abuse it for tracking
202 		 * and store whether we hold a vblank reference (and should do a
203 		 * vblank wait) in the ->enable boolean.
204 		 */
205 		old_crtc_state->enable = false;
206 
207 		if (!crtc->state->active)
208 			continue;
209 
210 		if (!drm_atomic_helper_framebuffer_changed(dev,
211 				old_state, crtc))
212 			continue;
213 
214 		ret = drm_crtc_vblank_get(crtc);
215 		if (ret != 0)
216 			continue;
217 
218 		old_crtc_state->enable = true;
219 	}
220 
221 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
222 		if (!old_crtc_state->enable)
223 			continue;
224 
225 		rockchip_crtc_wait_for_update(crtc);
226 		drm_crtc_vblank_put(crtc);
227 	}
228 }
229 
230 static void
231 rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
232 {
233 	struct drm_atomic_state *state = commit->state;
234 	struct drm_device *dev = commit->dev;
235 
236 	/*
237 	 * TODO: do fence wait here.
238 	 */
239 
240 	/*
241 	 * Rockchip crtc support runtime PM, can't update display planes
242 	 * when crtc is disabled.
243 	 *
244 	 * drm_atomic_helper_commit comments detail that:
245 	 *     For drivers supporting runtime PM the recommended sequence is
246 	 *
247 	 *     drm_atomic_helper_commit_modeset_disables(dev, state);
248 	 *
249 	 *     drm_atomic_helper_commit_modeset_enables(dev, state);
250 	 *
251 	 *     drm_atomic_helper_commit_planes(dev, state, true);
252 	 *
253 	 * See the kerneldoc entries for these three functions for more details.
254 	 */
255 	drm_atomic_helper_commit_modeset_disables(dev, state);
256 
257 	drm_atomic_helper_commit_modeset_enables(dev, state);
258 
259 	drm_atomic_helper_commit_planes(dev, state, true);
260 
261 	rockchip_atomic_wait_for_complete(dev, state);
262 
263 	drm_atomic_helper_cleanup_planes(dev, state);
264 
265 	drm_atomic_state_free(state);
266 }
267 
268 void rockchip_drm_atomic_work(struct work_struct *work)
269 {
270 	struct rockchip_atomic_commit *commit = container_of(work,
271 					struct rockchip_atomic_commit, work);
272 
273 	rockchip_atomic_commit_complete(commit);
274 }
275 
276 int rockchip_drm_atomic_commit(struct drm_device *dev,
277 			       struct drm_atomic_state *state,
278 			       bool nonblock)
279 {
280 	struct rockchip_drm_private *private = dev->dev_private;
281 	struct rockchip_atomic_commit *commit = &private->commit;
282 	int ret;
283 
284 	ret = drm_atomic_helper_prepare_planes(dev, state);
285 	if (ret)
286 		return ret;
287 
288 	/* serialize outstanding nonblocking commits */
289 	mutex_lock(&commit->lock);
290 	flush_work(&commit->work);
291 
292 	drm_atomic_helper_swap_state(dev, state);
293 
294 	commit->dev = dev;
295 	commit->state = state;
296 
297 	if (nonblock)
298 		schedule_work(&commit->work);
299 	else
300 		rockchip_atomic_commit_complete(commit);
301 
302 	mutex_unlock(&commit->lock);
303 
304 	return 0;
305 }
306 
307 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
308 	.fb_create = rockchip_user_fb_create,
309 	.output_poll_changed = rockchip_drm_output_poll_changed,
310 	.atomic_check = drm_atomic_helper_check,
311 	.atomic_commit = rockchip_drm_atomic_commit,
312 };
313 
314 struct drm_framebuffer *
315 rockchip_drm_framebuffer_init(struct drm_device *dev,
316 			      const struct drm_mode_fb_cmd2 *mode_cmd,
317 			      struct drm_gem_object *obj)
318 {
319 	struct rockchip_drm_fb *rockchip_fb;
320 
321 	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
322 	if (IS_ERR(rockchip_fb))
323 		return NULL;
324 
325 	return &rockchip_fb->fb;
326 }
327 
328 void rockchip_drm_mode_config_init(struct drm_device *dev)
329 {
330 	dev->mode_config.min_width = 0;
331 	dev->mode_config.min_height = 0;
332 
333 	/*
334 	 * set max width and height as default value(4096x4096).
335 	 * this value would be used to check framebuffer size limitation
336 	 * at drm_mode_addfb().
337 	 */
338 	dev->mode_config.max_width = 4096;
339 	dev->mode_config.max_height = 4096;
340 
341 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
342 }
343