Commit | Line | Data |
---|---|---|
c8afe684 RC |
1 | /* |
2 | * Copyright (C) 2013 Red Hat | |
3 | * Author: Rob Clark <robdclark@gmail.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published by | |
7 | * the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
78f27b1c MY |
18 | #include <drm/drm_crtc.h> |
19 | #include <drm/drm_crtc_helper.h> | |
20 | ||
c8afe684 | 21 | #include "msm_drv.h" |
dd2da6e3 | 22 | #include "msm_kms.h" |
466e5606 | 23 | #include "msm_gem.h" |
c8afe684 | 24 | |
c8afe684 RC |
25 | struct msm_framebuffer { |
26 | struct drm_framebuffer base; | |
27 | const struct msm_format *format; | |
7ca12718 | 28 | struct drm_gem_object *planes[MAX_PLANE]; |
c8afe684 RC |
29 | }; |
30 | #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base) | |
31 | ||
6c0693b1 RC |
32 | static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, |
33 | const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); | |
c8afe684 RC |
34 | |
35 | static int msm_framebuffer_create_handle(struct drm_framebuffer *fb, | |
36 | struct drm_file *file_priv, | |
37 | unsigned int *handle) | |
38 | { | |
39 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
40 | return drm_gem_handle_create(file_priv, | |
41 | msm_fb->planes[0], handle); | |
42 | } | |
43 | ||
44 | static void msm_framebuffer_destroy(struct drm_framebuffer *fb) | |
45 | { | |
46 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
bcb0b461 | 47 | int i, n = fb->format->num_planes; |
c8afe684 RC |
48 | |
49 | DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); | |
50 | ||
51 | drm_framebuffer_cleanup(fb); | |
52 | ||
53 | for (i = 0; i < n; i++) { | |
54 | struct drm_gem_object *bo = msm_fb->planes[i]; | |
e73a8569 ME |
55 | |
56 | drm_gem_object_unreference_unlocked(bo); | |
c8afe684 RC |
57 | } |
58 | ||
59 | kfree(msm_fb); | |
60 | } | |
61 | ||
c8afe684 RC |
62 | static const struct drm_framebuffer_funcs msm_framebuffer_funcs = { |
63 | .create_handle = msm_framebuffer_create_handle, | |
64 | .destroy = msm_framebuffer_destroy, | |
c8afe684 RC |
65 | }; |
66 | ||
67 | #ifdef CONFIG_DEBUG_FS | |
68 | void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) | |
69 | { | |
70 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
bcb0b461 | 71 | int i, n = fb->format->num_planes; |
c8afe684 RC |
72 | |
73 | seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n", | |
438b74a5 | 74 | fb->width, fb->height, (char *)&fb->format->format, |
747a598f | 75 | drm_framebuffer_read_refcount(fb), fb->base.id); |
c8afe684 RC |
76 | |
77 | for (i = 0; i < n; i++) { | |
78 | seq_printf(m, " %d: offset=%d pitch=%d, obj: ", | |
79 | i, fb->offsets[i], fb->pitches[i]); | |
80 | msm_gem_describe(msm_fb->planes[i], m); | |
81 | } | |
82 | } | |
83 | #endif | |
84 | ||
2638d90a RC |
85 | /* prepare/pin all the fb's bo's for scanout. Note that it is not valid |
86 | * to prepare an fb more multiple different initiator 'id's. But that | |
87 | * should be fine, since only the scanout (mdpN) side of things needs | |
88 | * this, the gpu doesn't care about fb's. | |
89 | */ | |
8bdcd949 RC |
90 | int msm_framebuffer_prepare(struct drm_framebuffer *fb, |
91 | struct msm_gem_address_space *aspace) | |
2638d90a RC |
92 | { |
93 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
bcb0b461 | 94 | int ret, i, n = fb->format->num_planes; |
78babc16 | 95 | uint64_t iova; |
2638d90a RC |
96 | |
97 | for (i = 0; i < n; i++) { | |
8bdcd949 | 98 | ret = msm_gem_get_iova(msm_fb->planes[i], aspace, &iova); |
78babc16 | 99 | DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret); |
2638d90a RC |
100 | if (ret) |
101 | return ret; | |
102 | } | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
8bdcd949 RC |
107 | void msm_framebuffer_cleanup(struct drm_framebuffer *fb, |
108 | struct msm_gem_address_space *aspace) | |
2638d90a RC |
109 | { |
110 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
bcb0b461 | 111 | int i, n = fb->format->num_planes; |
2638d90a RC |
112 | |
113 | for (i = 0; i < n; i++) | |
8bdcd949 | 114 | msm_gem_put_iova(msm_fb->planes[i], aspace); |
2638d90a RC |
115 | } |
116 | ||
8bdcd949 RC |
117 | uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, |
118 | struct msm_gem_address_space *aspace, int plane) | |
2638d90a RC |
119 | { |
120 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
3e2f29e4 RC |
121 | if (!msm_fb->planes[plane]) |
122 | return 0; | |
8bdcd949 | 123 | return msm_gem_iova(msm_fb->planes[plane], aspace) + fb->offsets[plane]; |
2638d90a RC |
124 | } |
125 | ||
c8afe684 RC |
126 | struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane) |
127 | { | |
128 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
129 | return msm_fb->planes[plane]; | |
130 | } | |
131 | ||
132 | const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb) | |
133 | { | |
134 | struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); | |
135 | return msm_fb->format; | |
136 | } | |
137 | ||
138 | struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, | |
1eb83451 | 139 | struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) |
c8afe684 RC |
140 | { |
141 | struct drm_gem_object *bos[4] = {0}; | |
142 | struct drm_framebuffer *fb; | |
143 | int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); | |
144 | ||
145 | for (i = 0; i < n; i++) { | |
a8ad0bd8 | 146 | bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); |
c8afe684 RC |
147 | if (!bos[i]) { |
148 | ret = -ENXIO; | |
149 | goto out_unref; | |
150 | } | |
151 | } | |
152 | ||
153 | fb = msm_framebuffer_init(dev, mode_cmd, bos); | |
154 | if (IS_ERR(fb)) { | |
155 | ret = PTR_ERR(fb); | |
156 | goto out_unref; | |
157 | } | |
158 | ||
159 | return fb; | |
160 | ||
161 | out_unref: | |
162 | for (i = 0; i < n; i++) | |
163 | drm_gem_object_unreference_unlocked(bos[i]); | |
164 | return ERR_PTR(ret); | |
165 | } | |
166 | ||
6c0693b1 | 167 | static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, |
1eb83451 | 168 | const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) |
c8afe684 RC |
169 | { |
170 | struct msm_drm_private *priv = dev->dev_private; | |
171 | struct msm_kms *kms = priv->kms; | |
7194b62c SV |
172 | struct msm_framebuffer *msm_fb = NULL; |
173 | struct drm_framebuffer *fb; | |
c8afe684 RC |
174 | const struct msm_format *format; |
175 | int ret, i, n; | |
176 | unsigned int hsub, vsub; | |
177 | ||
178 | DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", | |
179 | dev, mode_cmd, mode_cmd->width, mode_cmd->height, | |
180 | (char *)&mode_cmd->pixel_format); | |
181 | ||
182 | n = drm_format_num_planes(mode_cmd->pixel_format); | |
183 | hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); | |
184 | vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); | |
185 | ||
186 | format = kms->funcs->get_format(kms, mode_cmd->pixel_format); | |
187 | if (!format) { | |
188 | dev_err(dev->dev, "unsupported pixel format: %4.4s\n", | |
189 | (char *)&mode_cmd->pixel_format); | |
190 | ret = -EINVAL; | |
191 | goto fail; | |
192 | } | |
193 | ||
194 | msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL); | |
195 | if (!msm_fb) { | |
196 | ret = -ENOMEM; | |
197 | goto fail; | |
198 | } | |
199 | ||
200 | fb = &msm_fb->base; | |
201 | ||
202 | msm_fb->format = format; | |
203 | ||
10291bff RC |
204 | if (n > ARRAY_SIZE(msm_fb->planes)) { |
205 | ret = -EINVAL; | |
206 | goto fail; | |
207 | } | |
208 | ||
c8afe684 RC |
209 | for (i = 0; i < n; i++) { |
210 | unsigned int width = mode_cmd->width / (i ? hsub : 1); | |
211 | unsigned int height = mode_cmd->height / (i ? vsub : 1); | |
212 | unsigned int min_size; | |
213 | ||
214 | min_size = (height - 1) * mode_cmd->pitches[i] | |
215 | + width * drm_format_plane_cpp(mode_cmd->pixel_format, i) | |
216 | + mode_cmd->offsets[i]; | |
217 | ||
218 | if (bos[i]->size < min_size) { | |
219 | ret = -EINVAL; | |
220 | goto fail; | |
221 | } | |
222 | ||
223 | msm_fb->planes[i] = bos[i]; | |
224 | } | |
225 | ||
a3f913ca | 226 | drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); |
c8afe684 RC |
227 | |
228 | ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs); | |
229 | if (ret) { | |
230 | dev_err(dev->dev, "framebuffer init failed: %d\n", ret); | |
231 | goto fail; | |
232 | } | |
233 | ||
234 | DBG("create: FB ID: %d (%p)", fb->base.id, fb); | |
235 | ||
236 | return fb; | |
237 | ||
238 | fail: | |
7194b62c | 239 | kfree(msm_fb); |
c8afe684 RC |
240 | |
241 | return ERR_PTR(ret); | |
242 | } | |
466e5606 RC |
243 | |
244 | struct drm_framebuffer * | |
245 | msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format) | |
246 | { | |
247 | struct drm_mode_fb_cmd2 mode_cmd = { | |
248 | .pixel_format = format, | |
249 | .width = w, | |
250 | .height = h, | |
251 | .pitches = { p }, | |
252 | }; | |
253 | struct drm_gem_object *bo; | |
254 | struct drm_framebuffer *fb; | |
255 | int size; | |
256 | ||
257 | /* allocate backing bo */ | |
258 | size = mode_cmd.pitches[0] * mode_cmd.height; | |
259 | DBG("allocating %d bytes for fb %d", size, dev->primary->index); | |
260 | bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN); | |
261 | if (IS_ERR(bo)) { | |
262 | dev_warn(dev->dev, "could not allocate stolen bo\n"); | |
263 | /* try regular bo: */ | |
264 | bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC); | |
265 | } | |
266 | if (IS_ERR(bo)) { | |
267 | dev_err(dev->dev, "failed to allocate buffer object\n"); | |
268 | return ERR_CAST(bo); | |
269 | } | |
270 | ||
271 | fb = msm_framebuffer_init(dev, &mode_cmd, &bo); | |
272 | if (IS_ERR(fb)) { | |
273 | dev_err(dev->dev, "failed to allocate fb\n"); | |
274 | /* note: if fb creation failed, we can't rely on fb destroy | |
275 | * to unref the bo: | |
276 | */ | |
277 | drm_gem_object_unreference_unlocked(bo); | |
278 | return ERR_CAST(fb); | |
279 | } | |
280 | ||
281 | return fb; | |
282 | } |