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(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 
154 err_gem_object_unreference:
155 	for (i--; i >= 0; i--)
156 		drm_gem_object_unreference_unlocked(objs[i]);
157 	return ERR_PTR(ret);
158 }
159 
160 static 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 	if (fb_helper)
166 		drm_fb_helper_hotplug_event(fb_helper);
167 }
168 
169 static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
170 {
171 	struct rockchip_drm_private *priv = crtc->dev->dev_private;
172 	int pipe = drm_crtc_index(crtc);
173 	const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];
174 
175 	if (crtc_funcs && crtc_funcs->wait_for_update)
176 		crtc_funcs->wait_for_update(crtc);
177 }
178 
179 /*
180  * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
181  * have hardware counters for neither vblanks nor scanlines, which results in
182  * a race where:
183  *				| <-- HW vsync irq and reg take effect
184  *	       plane_commit --> |
185  *	get_vblank and wait --> |
186  *				| <-- handle_vblank, vblank->count + 1
187  *		 cleanup_fb --> |
188  *		iommu crash --> |
189  *				| <-- HW vsync irq and reg take effect
190  *
191  * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
192  * of waiting for vblank_count to change.
193  */
194 static void
195 rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
196 {
197 	struct drm_crtc_state *old_crtc_state;
198 	struct drm_crtc *crtc;
199 	int i, ret;
200 
201 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
202 		/* No one cares about the old state, so abuse it for tracking
203 		 * and store whether we hold a vblank reference (and should do a
204 		 * vblank wait) in the ->enable boolean.
205 		 */
206 		old_crtc_state->enable = false;
207 
208 		if (!crtc->state->active)
209 			continue;
210 
211 		if (!drm_atomic_helper_framebuffer_changed(dev,
212 				old_state, crtc))
213 			continue;
214 
215 		ret = drm_crtc_vblank_get(crtc);
216 		if (ret != 0)
217 			continue;
218 
219 		old_crtc_state->enable = true;
220 	}
221 
222 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
223 		if (!old_crtc_state->enable)
224 			continue;
225 
226 		rockchip_crtc_wait_for_update(crtc);
227 		drm_crtc_vblank_put(crtc);
228 	}
229 }
230 
231 static void
232 rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
233 {
234 	struct drm_atomic_state *state = commit->state;
235 	struct drm_device *dev = commit->dev;
236 
237 	/*
238 	 * TODO: do fence wait here.
239 	 */
240 
241 	/*
242 	 * Rockchip crtc support runtime PM, can't update display planes
243 	 * when crtc is disabled.
244 	 *
245 	 * drm_atomic_helper_commit comments detail that:
246 	 *     For drivers supporting runtime PM the recommended sequence is
247 	 *
248 	 *     drm_atomic_helper_commit_modeset_disables(dev, state);
249 	 *
250 	 *     drm_atomic_helper_commit_modeset_enables(dev, state);
251 	 *
252 	 *     drm_atomic_helper_commit_planes(dev, state, true);
253 	 *
254 	 * See the kerneldoc entries for these three functions for more details.
255 	 */
256 	drm_atomic_helper_commit_modeset_disables(dev, state);
257 
258 	drm_atomic_helper_commit_modeset_enables(dev, state);
259 
260 	drm_atomic_helper_commit_planes(dev, state, true);
261 
262 	rockchip_atomic_wait_for_complete(dev, state);
263 
264 	drm_atomic_helper_cleanup_planes(dev, state);
265 
266 	drm_atomic_state_free(state);
267 }
268 
269 void rockchip_drm_atomic_work(struct work_struct *work)
270 {
271 	struct rockchip_atomic_commit *commit = container_of(work,
272 					struct rockchip_atomic_commit, work);
273 
274 	rockchip_atomic_commit_complete(commit);
275 }
276 
277 int rockchip_drm_atomic_commit(struct drm_device *dev,
278 			       struct drm_atomic_state *state,
279 			       bool async)
280 {
281 	struct rockchip_drm_private *private = dev->dev_private;
282 	struct rockchip_atomic_commit *commit = &private->commit;
283 	int ret;
284 
285 	ret = drm_atomic_helper_prepare_planes(dev, state);
286 	if (ret)
287 		return ret;
288 
289 	/* serialize outstanding asynchronous commits */
290 	mutex_lock(&commit->lock);
291 	flush_work(&commit->work);
292 
293 	drm_atomic_helper_swap_state(dev, state);
294 
295 	commit->dev = dev;
296 	commit->state = state;
297 
298 	if (async)
299 		schedule_work(&commit->work);
300 	else
301 		rockchip_atomic_commit_complete(commit);
302 
303 	mutex_unlock(&commit->lock);
304 
305 	return 0;
306 }
307 
308 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
309 	.fb_create = rockchip_user_fb_create,
310 	.output_poll_changed = rockchip_drm_output_poll_changed,
311 	.atomic_check = drm_atomic_helper_check,
312 	.atomic_commit = rockchip_drm_atomic_commit,
313 };
314 
315 struct drm_framebuffer *
316 rockchip_drm_framebuffer_init(struct drm_device *dev,
317 			      const struct drm_mode_fb_cmd2 *mode_cmd,
318 			      struct drm_gem_object *obj)
319 {
320 	struct rockchip_drm_fb *rockchip_fb;
321 
322 	rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
323 	if (IS_ERR(rockchip_fb))
324 		return NULL;
325 
326 	return &rockchip_fb->fb;
327 }
328 
329 void rockchip_drm_mode_config_init(struct drm_device *dev)
330 {
331 	dev->mode_config.min_width = 0;
332 	dev->mode_config.min_height = 0;
333 
334 	/*
335 	 * set max width and height as default value(4096x4096).
336 	 * this value would be used to check framebuffer size limitation
337 	 * at drm_mode_addfb().
338 	 */
339 	dev->mode_config.max_width = 4096;
340 	dev->mode_config.max_height = 4096;
341 
342 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
343 }
344