blob: e8bf244cd6120a8c60f7bd6bf853982e7e193c45 [file] [log] [blame]
Rob Clarkc8afe682013-06-26 12:44:06 -04001/*
Alan Kwong578cdaf2017-01-28 17:25:43 -08002 * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
Rob Clarkc8afe682013-06-26 12:44:06 -04003 * Copyright (C) 2013 Red Hat
4 * Author: Rob Clark <robdclark@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
Alan Kwong578cdaf2017-01-28 17:25:43 -080019#include <linux/dma-mapping.h>
20#include <linux/dma-buf.h>
21
Rob Clarkc8afe682013-06-26 12:44:06 -040022#include "msm_drv.h"
Rob Clarkdd2da6e2013-11-30 16:12:10 -050023#include "msm_kms.h"
Rob Clarkc8afe682013-06-26 12:44:06 -040024
25#include "drm_crtc.h"
26#include "drm_crtc_helper.h"
27
Alan Kwong578cdaf2017-01-28 17:25:43 -080028#define MSM_FRAMEBUFFER_FLAG_KMAP BIT(0)
29
Rob Clarkc8afe682013-06-26 12:44:06 -040030struct msm_framebuffer {
31 struct drm_framebuffer base;
32 const struct msm_format *format;
Stephane Viau7ca12712014-12-08 10:48:57 -050033 struct drm_gem_object *planes[MAX_PLANE];
Alan Kwong578cdaf2017-01-28 17:25:43 -080034 void *vaddr[MAX_PLANE];
35 atomic_t kmap_count;
36 u32 flags;
Rob Clarkc8afe682013-06-26 12:44:06 -040037};
38#define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
39
40
41static int msm_framebuffer_create_handle(struct drm_framebuffer *fb,
42 struct drm_file *file_priv,
43 unsigned int *handle)
44{
Harsh Sahu97b52a72017-10-13 11:32:28 -070045 struct msm_framebuffer *msm_fb;
46
47 if (!fb) {
48 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
49 return -EINVAL;
50 }
51
52 msm_fb = to_msm_framebuffer(fb);
53
Rob Clarkc8afe682013-06-26 12:44:06 -040054 return drm_gem_handle_create(file_priv,
55 msm_fb->planes[0], handle);
56}
57
58static void msm_framebuffer_destroy(struct drm_framebuffer *fb)
59{
Harsh Sahu97b52a72017-10-13 11:32:28 -070060 struct msm_framebuffer *msm_fb;
61 int i, n;
62
63 if (!fb) {
64 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
65 return;
66 }
67
68 msm_fb = to_msm_framebuffer(fb);
69 n = drm_format_num_planes(fb->pixel_format);
Rob Clarkc8afe682013-06-26 12:44:06 -040070
71 DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
72
73 drm_framebuffer_cleanup(fb);
74
75 for (i = 0; i < n; i++) {
76 struct drm_gem_object *bo = msm_fb->planes[i];
Markus Elfringe73a8562016-07-13 19:15:35 +020077
78 drm_gem_object_unreference_unlocked(bo);
Rob Clarkc8afe682013-06-26 12:44:06 -040079 }
80
81 kfree(msm_fb);
82}
83
Rob Clarkc8afe682013-06-26 12:44:06 -040084static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
85 .create_handle = msm_framebuffer_create_handle,
86 .destroy = msm_framebuffer_destroy,
Rob Clarkc8afe682013-06-26 12:44:06 -040087};
88
89#ifdef CONFIG_DEBUG_FS
90void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
91{
Harsh Sahu97b52a72017-10-13 11:32:28 -070092 struct msm_framebuffer *msm_fb;
93 int i, n;
Rob Clarkc8afe682013-06-26 12:44:06 -040094
Harsh Sahu97b52a72017-10-13 11:32:28 -070095 if (!fb) {
96 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
97 return;
98 }
99
100 msm_fb = to_msm_framebuffer(fb);
101 n = drm_format_num_planes(fb->pixel_format);
Rob Clarkc8afe682013-06-26 12:44:06 -0400102 seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
103 fb->width, fb->height, (char *)&fb->pixel_format,
Dave Airlie747a5982016-04-15 15:10:35 +1000104 drm_framebuffer_read_refcount(fb), fb->base.id);
Rob Clarkc8afe682013-06-26 12:44:06 -0400105
106 for (i = 0; i < n; i++) {
107 seq_printf(m, " %d: offset=%d pitch=%d, obj: ",
108 i, fb->offsets[i], fb->pitches[i]);
109 msm_gem_describe(msm_fb->planes[i], m);
110 }
111}
112#endif
113
Alan Kwong578cdaf2017-01-28 17:25:43 -0800114void msm_framebuffer_set_kmap(struct drm_framebuffer *fb, bool enable)
115{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700116 struct msm_framebuffer *msm_fb;
Alan Kwong578cdaf2017-01-28 17:25:43 -0800117
Harsh Sahu97b52a72017-10-13 11:32:28 -0700118 if (!fb) {
119 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
120 return;
121 }
122
123 msm_fb = to_msm_framebuffer(fb);
Alan Kwong578cdaf2017-01-28 17:25:43 -0800124 if (enable)
125 msm_fb->flags |= MSM_FRAMEBUFFER_FLAG_KMAP;
126 else
127 msm_fb->flags &= ~MSM_FRAMEBUFFER_FLAG_KMAP;
128}
129
130static int msm_framebuffer_kmap(struct drm_framebuffer *fb)
131{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700132 struct msm_framebuffer *msm_fb;
133 int i, n;
Alan Kwong578cdaf2017-01-28 17:25:43 -0800134 struct drm_gem_object *bo;
135
Harsh Sahu97b52a72017-10-13 11:32:28 -0700136 if (!fb) {
137 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
138 return -EINVAL;
139 }
140
141 msm_fb = to_msm_framebuffer(fb);
142 n = drm_format_num_planes(fb->pixel_format);
Alan Kwong578cdaf2017-01-28 17:25:43 -0800143 if (atomic_inc_return(&msm_fb->kmap_count) > 1)
144 return 0;
145
146 for (i = 0; i < n; i++) {
147 bo = msm_framebuffer_bo(fb, i);
148 if (!bo || !bo->dma_buf) {
149 msm_fb->vaddr[i] = NULL;
150 continue;
151 }
152 dma_buf_begin_cpu_access(bo->dma_buf, DMA_BIDIRECTIONAL);
153 msm_fb->vaddr[i] = dma_buf_kmap(bo->dma_buf, 0);
154 DRM_INFO("FB[%u]: vaddr[%d]:%ux%u:0x%llx\n", fb->base.id, i,
155 fb->width, fb->height, (u64) msm_fb->vaddr[i]);
156 }
157
158 return 0;
159}
160
161static void msm_framebuffer_kunmap(struct drm_framebuffer *fb)
162{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700163 struct msm_framebuffer *msm_fb;
164 int i, n;
Alan Kwong578cdaf2017-01-28 17:25:43 -0800165 struct drm_gem_object *bo;
166
Harsh Sahu97b52a72017-10-13 11:32:28 -0700167 if (!fb) {
168 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
169 return;
170 }
171
172 msm_fb = to_msm_framebuffer(fb);
173 n = drm_format_num_planes(fb->pixel_format);
Alan Kwong578cdaf2017-01-28 17:25:43 -0800174 if (atomic_dec_return(&msm_fb->kmap_count) > 0)
175 return;
176
177 for (i = 0; i < n; i++) {
178 bo = msm_framebuffer_bo(fb, i);
179 if (!bo || !msm_fb->vaddr[i])
180 continue;
181 if (bo->dma_buf) {
182 dma_buf_kunmap(bo->dma_buf, 0, msm_fb->vaddr[i]);
183 dma_buf_end_cpu_access(bo->dma_buf, DMA_BIDIRECTIONAL);
184 }
185 msm_fb->vaddr[i] = NULL;
186 }
187}
188
Rob Clark2638d902014-11-08 09:13:37 -0500189/* prepare/pin all the fb's bo's for scanout. Note that it is not valid
190 * to prepare an fb more multiple different initiator 'id's. But that
191 * should be fine, since only the scanout (mdpN) side of things needs
192 * this, the gpu doesn't care about fb's.
193 */
Jordan Croused8e96522017-02-13 10:14:16 -0700194int msm_framebuffer_prepare(struct drm_framebuffer *fb,
195 struct msm_gem_address_space *aspace)
Rob Clark2638d902014-11-08 09:13:37 -0500196{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700197 struct msm_framebuffer *msm_fb;
198 int ret, i, n;
Rob Clark2638d902014-11-08 09:13:37 -0500199 uint32_t iova;
200
Harsh Sahu97b52a72017-10-13 11:32:28 -0700201 if (!fb) {
202 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
203 return -EINVAL;
204 }
205
206 msm_fb = to_msm_framebuffer(fb);
207 n = drm_format_num_planes(fb->pixel_format);
Rob Clark2638d902014-11-08 09:13:37 -0500208 for (i = 0; i < n; i++) {
Jordan Croused8e96522017-02-13 10:14:16 -0700209 ret = msm_gem_get_iova(msm_fb->planes[i], aspace, &iova);
Rob Clark2638d902014-11-08 09:13:37 -0500210 DBG("FB[%u]: iova[%d]: %08x (%d)", fb->base.id, i, iova, ret);
211 if (ret)
212 return ret;
213 }
214
Alan Kwong578cdaf2017-01-28 17:25:43 -0800215 if (msm_fb->flags & MSM_FRAMEBUFFER_FLAG_KMAP)
216 msm_framebuffer_kmap(fb);
217
Rob Clark2638d902014-11-08 09:13:37 -0500218 return 0;
219}
220
Jordan Croused8e96522017-02-13 10:14:16 -0700221void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
222 struct msm_gem_address_space *aspace)
Rob Clark2638d902014-11-08 09:13:37 -0500223{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700224 struct msm_framebuffer *msm_fb;
225 int i, n;
226
227 if (fb == NULL) {
228 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
229 return;
230 }
231
232 msm_fb = to_msm_framebuffer(fb);
233 n = drm_format_num_planes(fb->pixel_format);
Rob Clark2638d902014-11-08 09:13:37 -0500234
Alan Kwong578cdaf2017-01-28 17:25:43 -0800235 if (msm_fb->flags & MSM_FRAMEBUFFER_FLAG_KMAP)
236 msm_framebuffer_kunmap(fb);
237
Rob Clark2638d902014-11-08 09:13:37 -0500238 for (i = 0; i < n; i++)
Jordan Croused8e96522017-02-13 10:14:16 -0700239 msm_gem_put_iova(msm_fb->planes[i], aspace);
Rob Clark2638d902014-11-08 09:13:37 -0500240}
241
Jordan Croused8e96522017-02-13 10:14:16 -0700242uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
243 struct msm_gem_address_space *aspace, int plane)
Rob Clark2638d902014-11-08 09:13:37 -0500244{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700245 struct msm_framebuffer *msm_fb;
246
247 if (!fb) {
248 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
249 return -EINVAL;
250 }
251
252 msm_fb = to_msm_framebuffer(fb);
Rob Clark3e2f29e2014-11-19 12:29:33 -0500253 if (!msm_fb->planes[plane])
254 return 0;
Jordan Croused8e96522017-02-13 10:14:16 -0700255 return msm_gem_iova(msm_fb->planes[plane], aspace) + fb->offsets[plane];
Rob Clark2638d902014-11-08 09:13:37 -0500256}
257
Abhijit Kulkarni1b3340c2017-06-22 12:39:37 -0700258uint32_t msm_framebuffer_phys(struct drm_framebuffer *fb,
259 int plane)
260{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700261 struct msm_framebuffer *msm_fb;
Abhijit Kulkarni1b3340c2017-06-22 12:39:37 -0700262 dma_addr_t phys_addr;
263
Harsh Sahu97b52a72017-10-13 11:32:28 -0700264 if (!fb) {
265 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
266 return -EINVAL;
267 }
268
269 msm_fb = to_msm_framebuffer(fb);
Abhijit Kulkarni1b3340c2017-06-22 12:39:37 -0700270 if (!msm_fb->planes[plane])
271 return 0;
272
273 phys_addr = msm_gem_get_dma_addr(msm_fb->planes[plane]);
274 if (!phys_addr)
275 return 0;
276
277 return phys_addr + fb->offsets[plane];
278}
279
Rob Clarkc8afe682013-06-26 12:44:06 -0400280struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
281{
Harsh Sahu97b52a72017-10-13 11:32:28 -0700282 struct msm_framebuffer *msm_fb;
283
284 if (!fb) {
285 DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
286 return ERR_PTR(-EINVAL);
287 }
288
289 msm_fb = to_msm_framebuffer(fb);
Rob Clarkc8afe682013-06-26 12:44:06 -0400290 return msm_fb->planes[plane];
291}
292
293const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
294{
Dhaval Patelccbcb3d2016-08-22 11:58:14 -0700295 return fb ? (to_msm_framebuffer(fb))->format : NULL;
Rob Clarkc8afe682013-06-26 12:44:06 -0400296}
297
298struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
Ville Syrjälä1eb83452015-11-11 19:11:29 +0200299 struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
Rob Clarkc8afe682013-06-26 12:44:06 -0400300{
301 struct drm_gem_object *bos[4] = {0};
302 struct drm_framebuffer *fb;
303 int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
304
305 for (i = 0; i < n; i++) {
Chris Wilsona8ad0bd2016-05-09 11:04:54 +0100306 bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
Rob Clarkc8afe682013-06-26 12:44:06 -0400307 if (!bos[i]) {
308 ret = -ENXIO;
309 goto out_unref;
310 }
311 }
312
313 fb = msm_framebuffer_init(dev, mode_cmd, bos);
314 if (IS_ERR(fb)) {
315 ret = PTR_ERR(fb);
316 goto out_unref;
317 }
318
319 return fb;
320
321out_unref:
322 for (i = 0; i < n; i++)
323 drm_gem_object_unreference_unlocked(bos[i]);
324 return ERR_PTR(ret);
325}
326
327struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
Ville Syrjälä1eb83452015-11-11 19:11:29 +0200328 const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
Rob Clarkc8afe682013-06-26 12:44:06 -0400329{
330 struct msm_drm_private *priv = dev->dev_private;
331 struct msm_kms *kms = priv->kms;
Stephane Viau7194b622015-05-05 09:47:57 -0400332 struct msm_framebuffer *msm_fb = NULL;
333 struct drm_framebuffer *fb;
Rob Clarkc8afe682013-06-26 12:44:06 -0400334 const struct msm_format *format;
Lloyd Atkinson9a673492016-07-05 11:41:57 -0400335 int ret, i, num_planes;
Rob Clarkc8afe682013-06-26 12:44:06 -0400336 unsigned int hsub, vsub;
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400337 bool is_modified = false;
Rob Clarkc8afe682013-06-26 12:44:06 -0400338
339 DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
340 dev, mode_cmd, mode_cmd->width, mode_cmd->height,
341 (char *)&mode_cmd->pixel_format);
342
Lloyd Atkinson9a673492016-07-05 11:41:57 -0400343 num_planes = drm_format_num_planes(mode_cmd->pixel_format);
Rob Clarkc8afe682013-06-26 12:44:06 -0400344 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
345 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
346
Lloyd Atkinson9a673492016-07-05 11:41:57 -0400347 format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
348 mode_cmd->modifier, num_planes);
Rob Clarkc8afe682013-06-26 12:44:06 -0400349 if (!format) {
350 dev_err(dev->dev, "unsupported pixel format: %4.4s\n",
351 (char *)&mode_cmd->pixel_format);
352 ret = -EINVAL;
353 goto fail;
354 }
355
356 msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
357 if (!msm_fb) {
358 ret = -ENOMEM;
359 goto fail;
360 }
361
362 fb = &msm_fb->base;
363
364 msm_fb->format = format;
Alan Kwong578cdaf2017-01-28 17:25:43 -0800365 atomic_set(&msm_fb->kmap_count, 0);
Rob Clarkc8afe682013-06-26 12:44:06 -0400366
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400367 if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) {
368 for (i = 0; i < ARRAY_SIZE(mode_cmd->modifier); i++) {
369 if (mode_cmd->modifier[i]) {
370 is_modified = true;
371 break;
372 }
373 }
374 }
375
Lloyd Atkinson9a673492016-07-05 11:41:57 -0400376 if (num_planes > ARRAY_SIZE(msm_fb->planes)) {
Rob Clark10291bf2014-11-08 09:20:28 -0500377 ret = -EINVAL;
378 goto fail;
379 }
380
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400381 if (is_modified) {
382 if (!kms->funcs->check_modified_format) {
383 dev_err(dev->dev, "can't check modified fb format\n");
Rob Clarkc8afe682013-06-26 12:44:06 -0400384 ret = -EINVAL;
385 goto fail;
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400386 } else {
387 ret = kms->funcs->check_modified_format(
388 kms, msm_fb->format, mode_cmd, bos);
389 if (ret)
390 goto fail;
Rob Clarkc8afe682013-06-26 12:44:06 -0400391 }
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400392 } else {
393 for (i = 0; i < num_planes; i++) {
394 unsigned int width = mode_cmd->width / (i ? hsub : 1);
395 unsigned int height = mode_cmd->height / (i ? vsub : 1);
396 unsigned int min_size;
397 unsigned int cpp;
Rob Clarkc8afe682013-06-26 12:44:06 -0400398
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400399 cpp = drm_format_plane_cpp(mode_cmd->pixel_format, i);
400
401 min_size = (height - 1) * mode_cmd->pitches[i]
402 + width * cpp
403 + mode_cmd->offsets[i];
404
405 if (bos[i]->size < min_size) {
406 ret = -EINVAL;
407 goto fail;
408 }
409 }
Rob Clarkc8afe682013-06-26 12:44:06 -0400410 }
411
Lloyd Atkinson80be5672016-05-25 15:13:23 -0400412 for (i = 0; i < num_planes; i++)
413 msm_fb->planes[i] = bos[i];
414
Rob Clarkc8afe682013-06-26 12:44:06 -0400415 drm_helper_mode_fill_fb_struct(fb, mode_cmd);
416
417 ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
418 if (ret) {
419 dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
420 goto fail;
421 }
422
423 DBG("create: FB ID: %d (%p)", fb->base.id, fb);
424
425 return fb;
426
427fail:
Stephane Viau7194b622015-05-05 09:47:57 -0400428 kfree(msm_fb);
Rob Clarkc8afe682013-06-26 12:44:06 -0400429
430 return ERR_PTR(ret);
431}