1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
4  * Author: James.Qian.Wang <james.qian.wang@arm.com>
5  *
6  */
7 #include <drm/drm_gem.h>
8 #include <drm/drm_gem_framebuffer_helper.h>
9 #include <drm/drm_fb_cma_helper.h>
10 #include <drm/drm_gem_cma_helper.h>
11 #include "komeda_framebuffer.h"
12 #include "komeda_dev.h"
13 
14 static void komeda_fb_destroy(struct drm_framebuffer *fb)
15 {
16 	struct komeda_fb *kfb = to_kfb(fb);
17 	u32 i;
18 
19 	for (i = 0; i < fb->format->num_planes; i++)
20 		drm_gem_object_put_unlocked(fb->obj[i]);
21 
22 	drm_framebuffer_cleanup(fb);
23 	kfree(kfb);
24 }
25 
26 static int komeda_fb_create_handle(struct drm_framebuffer *fb,
27 				   struct drm_file *file, u32 *handle)
28 {
29 	return drm_gem_handle_create(file, fb->obj[0], handle);
30 }
31 
32 static const struct drm_framebuffer_funcs komeda_fb_funcs = {
33 	.destroy	= komeda_fb_destroy,
34 	.create_handle	= komeda_fb_create_handle,
35 };
36 
37 static int
38 komeda_fb_none_afbc_size_check(struct komeda_dev *mdev, struct komeda_fb *kfb,
39 			       struct drm_file *file,
40 			       const struct drm_mode_fb_cmd2 *mode_cmd)
41 {
42 	struct drm_framebuffer *fb = &kfb->base;
43 	struct drm_gem_object *obj;
44 	u32 min_size = 0;
45 	u32 i;
46 
47 	for (i = 0; i < fb->format->num_planes; i++) {
48 		obj = drm_gem_object_lookup(file, mode_cmd->handles[i]);
49 		if (!obj) {
50 			DRM_DEBUG_KMS("Failed to lookup GEM object\n");
51 			fb->obj[i] = NULL;
52 
53 			return -ENOENT;
54 		}
55 
56 		kfb->aligned_w = fb->width / (i ? fb->format->hsub : 1);
57 		kfb->aligned_h = fb->height / (i ? fb->format->vsub : 1);
58 
59 		if (fb->pitches[i] % mdev->chip.bus_width) {
60 			DRM_DEBUG_KMS("Pitch[%d]: 0x%x doesn't align to 0x%x\n",
61 				      i, fb->pitches[i], mdev->chip.bus_width);
62 			drm_gem_object_put_unlocked(obj);
63 			fb->obj[i] = NULL;
64 
65 			return -EINVAL;
66 		}
67 
68 		min_size = ((kfb->aligned_h / kfb->format_caps->tile_size - 1)
69 			    * fb->pitches[i])
70 			    + (kfb->aligned_w * fb->format->cpp[i]
71 			       * kfb->format_caps->tile_size)
72 			    + fb->offsets[i];
73 
74 		if (obj->size < min_size) {
75 			DRM_DEBUG_KMS("Fail to check none afbc fb size.\n");
76 			drm_gem_object_put_unlocked(obj);
77 			fb->obj[i] = NULL;
78 
79 			return -EINVAL;
80 		}
81 
82 		fb->obj[i] = obj;
83 	}
84 
85 	if (fb->format->num_planes == 3) {
86 		if (fb->pitches[1] != fb->pitches[2]) {
87 			DRM_DEBUG_KMS("The pitch[1] and [2] are not same\n");
88 			return -EINVAL;
89 		}
90 	}
91 
92 	return 0;
93 }
94 
95 struct drm_framebuffer *
96 komeda_fb_create(struct drm_device *dev, struct drm_file *file,
97 		 const struct drm_mode_fb_cmd2 *mode_cmd)
98 {
99 	struct komeda_dev *mdev = dev->dev_private;
100 	struct komeda_fb *kfb;
101 	int ret = 0, i;
102 
103 	kfb = kzalloc(sizeof(*kfb), GFP_KERNEL);
104 	if (!kfb)
105 		return ERR_PTR(-ENOMEM);
106 
107 	kfb->format_caps = komeda_get_format_caps(&mdev->fmt_tbl,
108 						  mode_cmd->pixel_format,
109 						  mode_cmd->modifier[0]);
110 	if (!kfb->format_caps) {
111 		DRM_DEBUG_KMS("FMT %x is not supported.\n",
112 			      mode_cmd->pixel_format);
113 		kfree(kfb);
114 		return ERR_PTR(-EINVAL);
115 	}
116 
117 	drm_helper_mode_fill_fb_struct(dev, &kfb->base, mode_cmd);
118 
119 	ret = komeda_fb_none_afbc_size_check(mdev, kfb, file, mode_cmd);
120 	if (ret < 0)
121 		goto err_cleanup;
122 
123 	ret = drm_framebuffer_init(dev, &kfb->base, &komeda_fb_funcs);
124 	if (ret < 0) {
125 		DRM_DEBUG_KMS("failed to initialize fb\n");
126 
127 		goto err_cleanup;
128 	}
129 
130 	return &kfb->base;
131 
132 err_cleanup:
133 	for (i = 0; i < kfb->base.format->num_planes; i++)
134 		drm_gem_object_put_unlocked(kfb->base.obj[i]);
135 
136 	kfree(kfb);
137 	return ERR_PTR(ret);
138 }
139 
140 dma_addr_t
141 komeda_fb_get_pixel_addr(struct komeda_fb *kfb, int x, int y, int plane)
142 {
143 	struct drm_framebuffer *fb = &kfb->base;
144 	const struct drm_gem_cma_object *obj;
145 	u32 plane_x, plane_y, cpp, pitch, offset;
146 
147 	if (plane >= fb->format->num_planes) {
148 		DRM_DEBUG_KMS("Out of max plane num.\n");
149 		return -EINVAL;
150 	}
151 
152 	obj = drm_fb_cma_get_gem_obj(fb, plane);
153 
154 	offset = fb->offsets[plane];
155 	if (!fb->modifier) {
156 		plane_x = x / (plane ? fb->format->hsub : 1);
157 		plane_y = y / (plane ? fb->format->vsub : 1);
158 		cpp = fb->format->cpp[plane];
159 		pitch = fb->pitches[plane];
160 		offset += plane_x * cpp *  kfb->format_caps->tile_size +
161 				(plane_y * pitch) / kfb->format_caps->tile_size;
162 	}
163 
164 	return obj->paddr + offset;
165 }
166