drm/rockchip: vop: remove unused device
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_fb.c
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 #include <linux/memblock.h>
22
23 #include "rockchip_drm_drv.h"
24 #include "rockchip_drm_gem.h"
25
26 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
27
28 struct rockchip_drm_fb {
29         struct drm_framebuffer fb;
30         dma_addr_t dma_addr[ROCKCHIP_MAX_FB_BUFFER];
31         struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
32         struct rockchip_logo *logo;
33 };
34
35 dma_addr_t rockchip_fb_get_dma_addr(struct drm_framebuffer *fb,
36                                     unsigned int plane)
37 {
38         struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
39
40         if (WARN_ON(plane >= ROCKCHIP_MAX_FB_BUFFER))
41                 return 0;
42
43         return rk_fb->dma_addr[plane];
44 }
45
46 static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
47 {
48         struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
49         struct drm_gem_object *obj;
50         int i;
51
52         for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
53                 obj = rockchip_fb->obj[i];
54                 if (obj)
55                         drm_gem_object_unreference_unlocked(obj);
56         }
57
58 #ifndef MODULE
59         if (rockchip_fb->logo) {
60                 struct rockchip_logo *logo = rockchip_fb->logo;
61
62                 if (!--logo->count) {
63                         void *start = phys_to_virt(logo->start);
64                         void *end = phys_to_virt(logo->size);
65
66                         dma_unmap_sg(fb->dev->dev, logo->sgt->sgl,
67                                      logo->sgt->nents, DMA_TO_DEVICE);
68                         sg_free_table(logo->sgt);
69                         memblock_free(logo->start, logo->size);
70                         free_reserved_area(start, end, -1, "drm_logo");
71                 }
72         }
73 #else
74         WARN_ON(rockchip_fb->sgt);
75 #endif
76
77         drm_framebuffer_cleanup(fb);
78         kfree(rockchip_fb);
79 }
80
81 static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
82                                          struct drm_file *file_priv,
83                                          unsigned int *handle)
84 {
85         struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
86
87         return drm_gem_handle_create(file_priv,
88                                      rockchip_fb->obj[0], handle);
89 }
90
91 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
92         .destroy        = rockchip_drm_fb_destroy,
93         .create_handle  = rockchip_drm_fb_create_handle,
94 };
95
96 struct drm_framebuffer *
97 rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
98                   struct drm_gem_object **obj, struct rockchip_logo *logo,
99                   unsigned int num_planes)
100 {
101         struct rockchip_drm_fb *rockchip_fb;
102         struct rockchip_gem_object *rk_obj;
103         int ret = 0;
104         int i;
105
106         rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
107         if (!rockchip_fb)
108                 return ERR_PTR(-ENOMEM);
109
110         drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
111
112         ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
113                                    &rockchip_drm_fb_funcs);
114         if (ret) {
115                 dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
116                         ret);
117                 goto err_free_fb;
118         }
119
120         if (obj) {
121                 for (i = 0; i < num_planes; i++)
122                         rockchip_fb->obj[i] = obj[i];
123
124                 for (i = 0; i < num_planes; i++) {
125                         rk_obj = to_rockchip_obj(obj[i]);
126                         rockchip_fb->dma_addr[i] = rk_obj->dma_addr;
127                 }
128 #ifndef MODULE
129         } else if (logo) {
130                 rockchip_fb->dma_addr[0] = logo->dma_addr;
131                 rockchip_fb->logo = logo;
132                 logo->count++;
133 #endif
134         } else {
135                 ret = -EINVAL;
136                 dev_err(dev->dev, "Failed to find available buffer\n");
137                 goto err_deinit_drm_fb;
138         }
139
140         return &rockchip_fb->fb;
141
142 err_deinit_drm_fb:
143         drm_framebuffer_cleanup(&rockchip_fb->fb);
144 err_free_fb:
145         kfree(rockchip_fb);
146         return ERR_PTR(ret);
147 }
148
149 static struct drm_framebuffer *
150 rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
151                         struct drm_mode_fb_cmd2 *mode_cmd)
152 {
153         struct drm_framebuffer *fb;
154         struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
155         struct drm_gem_object *obj;
156         unsigned int hsub;
157         unsigned int vsub;
158         int num_planes;
159         int ret;
160         int i;
161
162         hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
163         vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
164         num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
165                          ROCKCHIP_MAX_FB_BUFFER);
166
167         for (i = 0; i < num_planes; i++) {
168                 unsigned int width = mode_cmd->width / (i ? hsub : 1);
169                 unsigned int height = mode_cmd->height / (i ? vsub : 1);
170                 unsigned int min_size;
171
172                 obj = drm_gem_object_lookup(dev, file_priv,
173                                             mode_cmd->handles[i]);
174                 if (!obj) {
175                         dev_err(dev->dev, "Failed to lookup GEM object\n");
176                         ret = -ENXIO;
177                         goto err_gem_object_unreference;
178                 }
179
180                 min_size = (height - 1) * mode_cmd->pitches[i] +
181                         mode_cmd->offsets[i] +
182                         width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
183
184                 if (obj->size < min_size) {
185                         drm_gem_object_unreference_unlocked(obj);
186                         ret = -EINVAL;
187                         goto err_gem_object_unreference;
188                 }
189                 objs[i] = obj;
190         }
191
192         fb = rockchip_fb_alloc(dev, mode_cmd, objs, NULL, i);
193         if (IS_ERR(fb)) {
194                 ret = PTR_ERR(fb);
195                 goto err_gem_object_unreference;
196         }
197
198         return fb;
199
200 err_gem_object_unreference:
201         for (i--; i >= 0; i--)
202                 drm_gem_object_unreference_unlocked(objs[i]);
203         return ERR_PTR(ret);
204 }
205
206 static void rockchip_drm_output_poll_changed(struct drm_device *dev)
207 {
208         struct rockchip_drm_private *private = dev->dev_private;
209         struct drm_fb_helper *fb_helper = private->fbdev_helper;
210
211         if (fb_helper)
212                 drm_fb_helper_hotplug_event(fb_helper);
213 }
214
215 static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
216 {
217         struct rockchip_drm_private *priv = crtc->dev->dev_private;
218         int pipe = drm_crtc_index(crtc);
219         const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];
220
221         if (crtc_funcs && crtc_funcs->wait_for_update)
222                 crtc_funcs->wait_for_update(crtc);
223 }
224
225 /*
226  * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
227  * have hardware counters for neither vblanks nor scanlines, which results in
228  * a race where:
229  *                              | <-- HW vsync irq and reg take effect
230  *             plane_commit --> |
231  *      get_vblank and wait --> |
232  *                              | <-- handle_vblank, vblank->count + 1
233  *               cleanup_fb --> |
234  *              iommu crash --> |
235  *                              | <-- HW vsync irq and reg take effect
236  *
237  * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
238  * of waiting for vblank_count to change.
239  */
240 static void
241 rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
242 {
243         struct drm_crtc_state *old_crtc_state;
244         struct drm_crtc *crtc;
245         int i, ret;
246
247         for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
248                 /* No one cares about the old state, so abuse it for tracking
249                  * and store whether we hold a vblank reference (and should do a
250                  * vblank wait) in the ->enable boolean.
251                  */
252                 old_crtc_state->enable = false;
253
254                 if (!crtc->state->active)
255                         continue;
256
257                 if (!drm_atomic_helper_framebuffer_changed(dev,
258                                 old_state, crtc))
259                         continue;
260
261                 ret = drm_crtc_vblank_get(crtc);
262                 if (ret != 0)
263                         continue;
264
265                 old_crtc_state->enable = true;
266         }
267
268         for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
269                 if (!old_crtc_state->enable)
270                         continue;
271
272                 rockchip_crtc_wait_for_update(crtc);
273                 drm_crtc_vblank_put(crtc);
274         }
275 }
276
277 static void
278 rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
279 {
280         struct drm_atomic_state *state = commit->state;
281         struct drm_device *dev = commit->dev;
282
283         /*
284          * TODO: do fence wait here.
285          */
286
287         /*
288          * Rockchip crtc support runtime PM, can't update display planes
289          * when crtc is disabled.
290          *
291          * drm_atomic_helper_commit comments detail that:
292          *     For drivers supporting runtime PM the recommended sequence is
293          *
294          *     drm_atomic_helper_commit_modeset_disables(dev, state);
295          *
296          *     drm_atomic_helper_commit_modeset_enables(dev, state);
297          *
298          *     drm_atomic_helper_commit_planes(dev, state, true);
299          *
300          * See the kerneldoc entries for these three functions for more details.
301          */
302         drm_atomic_helper_commit_modeset_disables(dev, state);
303
304         drm_atomic_helper_commit_modeset_enables(dev, state);
305
306         drm_atomic_helper_commit_planes(dev, state, true);
307
308         rockchip_atomic_wait_for_complete(dev, state);
309
310         drm_atomic_helper_cleanup_planes(dev, state);
311
312         drm_atomic_state_free(state);
313 }
314
315 void rockchip_drm_atomic_work(struct work_struct *work)
316 {
317         struct rockchip_atomic_commit *commit = container_of(work,
318                                         struct rockchip_atomic_commit, work);
319
320         rockchip_atomic_commit_complete(commit);
321 }
322
323 int rockchip_drm_atomic_commit(struct drm_device *dev,
324                                struct drm_atomic_state *state,
325                                bool async)
326 {
327         struct rockchip_drm_private *private = dev->dev_private;
328         struct rockchip_atomic_commit *commit = &private->commit;
329         int ret;
330
331         ret = drm_atomic_helper_prepare_planes(dev, state);
332         if (ret)
333                 return ret;
334
335         /* serialize outstanding asynchronous commits */
336         mutex_lock(&commit->lock);
337         flush_work(&commit->work);
338
339         drm_atomic_helper_swap_state(dev, state);
340
341         commit->dev = dev;
342         commit->state = state;
343
344         if (async)
345                 schedule_work(&commit->work);
346         else
347                 rockchip_atomic_commit_complete(commit);
348
349         mutex_unlock(&commit->lock);
350
351         return 0;
352 }
353
354 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
355         .fb_create = rockchip_user_fb_create,
356         .output_poll_changed = rockchip_drm_output_poll_changed,
357         .atomic_check = drm_atomic_helper_check,
358         .atomic_commit = rockchip_drm_atomic_commit,
359 };
360
361 struct drm_framebuffer *
362 rockchip_drm_framebuffer_init(struct drm_device *dev,
363                               struct drm_mode_fb_cmd2 *mode_cmd,
364                               struct drm_gem_object *obj)
365 {
366         struct drm_framebuffer *fb;
367
368         fb = rockchip_fb_alloc(dev, mode_cmd, &obj, NULL, 1);
369         if (IS_ERR(fb))
370                 return NULL;
371
372         return fb;
373 }
374
375 void rockchip_drm_mode_config_init(struct drm_device *dev)
376 {
377         dev->mode_config.min_width = 0;
378         dev->mode_config.min_height = 0;
379
380         /*
381          * set max width and height as default value(4096x4096).
382          * this value would be used to check framebuffer size limitation
383          * at drm_mode_addfb().
384          */
385         dev->mode_config.max_width = 4096;
386         dev->mode_config.max_height = 4096;
387
388         dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
389 }