GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / gpu / drm / mediatek / mtk_drm_fb.c
1 /*
2  * Copyright (c) 2015 MediaTek Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #include <drm/drmP.h>
15 #include <drm/drm_crtc_helper.h>
16 #include <drm/drm_fb_helper.h>
17 #include <drm/drm_gem.h>
18 #include <linux/dma-buf.h>
19 #include <linux/reservation.h>
20
21 #include "mtk_drm_drv.h"
22 #include "mtk_drm_fb.h"
23 #include "mtk_drm_gem.h"
24
25 /*
26  * mtk specific framebuffer structure.
27  *
28  * @fb: drm framebuffer object.
29  * @gem_obj: array of gem objects.
30  */
31 struct mtk_drm_fb {
32         struct drm_framebuffer  base;
33         /* For now we only support a single plane */
34         struct drm_gem_object   *gem_obj;
35 };
36
37 #define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base)
38
39 struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb)
40 {
41         struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
42
43         return mtk_fb->gem_obj;
44 }
45
46 static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb,
47                                     struct drm_file *file_priv,
48                                     unsigned int *handle)
49 {
50         struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
51
52         return drm_gem_handle_create(file_priv, mtk_fb->gem_obj, handle);
53 }
54
55 static void mtk_drm_fb_destroy(struct drm_framebuffer *fb)
56 {
57         struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb);
58
59         drm_framebuffer_cleanup(fb);
60
61         drm_gem_object_put_unlocked(mtk_fb->gem_obj);
62
63         kfree(mtk_fb);
64 }
65
66 static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = {
67         .create_handle = mtk_drm_fb_create_handle,
68         .destroy = mtk_drm_fb_destroy,
69 };
70
71 static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev,
72                                         const struct drm_mode_fb_cmd2 *mode,
73                                         struct drm_gem_object *obj)
74 {
75         struct mtk_drm_fb *mtk_fb;
76         int ret;
77
78         if (drm_format_num_planes(mode->pixel_format) != 1)
79                 return ERR_PTR(-EINVAL);
80
81         mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL);
82         if (!mtk_fb)
83                 return ERR_PTR(-ENOMEM);
84
85         drm_helper_mode_fill_fb_struct(dev, &mtk_fb->base, mode);
86
87         mtk_fb->gem_obj = obj;
88
89         ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs);
90         if (ret) {
91                 DRM_ERROR("failed to initialize framebuffer\n");
92                 kfree(mtk_fb);
93                 return ERR_PTR(ret);
94         }
95
96         return mtk_fb;
97 }
98
99 /*
100  * Wait for any exclusive fence in fb's gem object's reservation object.
101  *
102  * Returns -ERESTARTSYS if interrupted, else 0.
103  */
104 int mtk_fb_wait(struct drm_framebuffer *fb)
105 {
106         struct drm_gem_object *gem;
107         struct reservation_object *resv;
108         long ret;
109
110         if (!fb)
111                 return 0;
112
113         gem = mtk_fb_get_gem_obj(fb);
114         if (!gem || !gem->dma_buf || !gem->dma_buf->resv)
115                 return 0;
116
117         resv = gem->dma_buf->resv;
118         ret = reservation_object_wait_timeout_rcu(resv, false, true,
119                                                   MAX_SCHEDULE_TIMEOUT);
120         /* MAX_SCHEDULE_TIMEOUT on success, -ERESTARTSYS if interrupted */
121         if (WARN_ON(ret < 0))
122                 return ret;
123
124         return 0;
125 }
126
127 struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev,
128                                                struct drm_file *file,
129                                                const struct drm_mode_fb_cmd2 *cmd)
130 {
131         struct mtk_drm_fb *mtk_fb;
132         struct drm_gem_object *gem;
133         unsigned int width = cmd->width;
134         unsigned int height = cmd->height;
135         unsigned int size, bpp;
136         int ret;
137
138         if (drm_format_num_planes(cmd->pixel_format) != 1)
139                 return ERR_PTR(-EINVAL);
140
141         gem = drm_gem_object_lookup(file, cmd->handles[0]);
142         if (!gem)
143                 return ERR_PTR(-ENOENT);
144
145         bpp = drm_format_plane_cpp(cmd->pixel_format, 0);
146         size = (height - 1) * cmd->pitches[0] + width * bpp;
147         size += cmd->offsets[0];
148
149         if (gem->size < size) {
150                 ret = -EINVAL;
151                 goto unreference;
152         }
153
154         mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem);
155         if (IS_ERR(mtk_fb)) {
156                 ret = PTR_ERR(mtk_fb);
157                 goto unreference;
158         }
159
160         return &mtk_fb->base;
161
162 unreference:
163         drm_gem_object_put_unlocked(gem);
164         return ERR_PTR(ret);
165 }