Commit | Line | Data |
---|---|---|
119f5173 CH |
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_unreference_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(&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 | ||
de4ae068 | 141 | gem = drm_gem_object_lookup(file, cmd->handles[0]); |
119f5173 CH |
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_unreference_unlocked(gem); | |
164 | return ERR_PTR(ret); | |
165 | } |