xref: /openbmc/linux/drivers/gpu/drm/msm/msm_fb.c (revision 03638e62)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 Red Hat
4  * Author: Rob Clark <robdclark@gmail.com>
5  */
6 
7 #include <drm/drm_crtc.h>
8 #include <drm/drm_gem_framebuffer_helper.h>
9 #include <drm/drm_probe_helper.h>
10 
11 #include "msm_drv.h"
12 #include "msm_kms.h"
13 #include "msm_gem.h"
14 
15 struct msm_framebuffer {
16 	struct drm_framebuffer base;
17 	const struct msm_format *format;
18 };
19 #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
20 
21 static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
22 		const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
23 
24 static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
25 	.create_handle = drm_gem_fb_create_handle,
26 	.destroy = drm_gem_fb_destroy,
27 };
28 
29 #ifdef CONFIG_DEBUG_FS
30 void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
31 {
32 	int i, n = fb->format->num_planes;
33 
34 	seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
35 			fb->width, fb->height, (char *)&fb->format->format,
36 			drm_framebuffer_read_refcount(fb), fb->base.id);
37 
38 	for (i = 0; i < n; i++) {
39 		seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
40 				i, fb->offsets[i], fb->pitches[i]);
41 		msm_gem_describe(fb->obj[i], m);
42 	}
43 }
44 #endif
45 
46 /* prepare/pin all the fb's bo's for scanout.  Note that it is not valid
47  * to prepare an fb more multiple different initiator 'id's.  But that
48  * should be fine, since only the scanout (mdpN) side of things needs
49  * this, the gpu doesn't care about fb's.
50  */
51 int msm_framebuffer_prepare(struct drm_framebuffer *fb,
52 		struct msm_gem_address_space *aspace)
53 {
54 	int ret, i, n = fb->format->num_planes;
55 	uint64_t iova;
56 
57 	for (i = 0; i < n; i++) {
58 		ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &iova);
59 		DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret);
60 		if (ret)
61 			return ret;
62 	}
63 
64 	return 0;
65 }
66 
67 void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
68 		struct msm_gem_address_space *aspace)
69 {
70 	int i, n = fb->format->num_planes;
71 
72 	for (i = 0; i < n; i++)
73 		msm_gem_unpin_iova(fb->obj[i], aspace);
74 }
75 
76 uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
77 		struct msm_gem_address_space *aspace, int plane)
78 {
79 	if (!fb->obj[plane])
80 		return 0;
81 	return msm_gem_iova(fb->obj[plane], aspace) + fb->offsets[plane];
82 }
83 
84 struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
85 {
86 	return drm_gem_fb_get_obj(fb, plane);
87 }
88 
89 const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
90 {
91 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
92 	return msm_fb->format;
93 }
94 
95 struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
96 		struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
97 {
98 	struct drm_gem_object *bos[4] = {0};
99 	struct drm_framebuffer *fb;
100 	int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
101 
102 	for (i = 0; i < n; i++) {
103 		bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
104 		if (!bos[i]) {
105 			ret = -ENXIO;
106 			goto out_unref;
107 		}
108 	}
109 
110 	fb = msm_framebuffer_init(dev, mode_cmd, bos);
111 	if (IS_ERR(fb)) {
112 		ret = PTR_ERR(fb);
113 		goto out_unref;
114 	}
115 
116 	return fb;
117 
118 out_unref:
119 	for (i = 0; i < n; i++)
120 		drm_gem_object_put_unlocked(bos[i]);
121 	return ERR_PTR(ret);
122 }
123 
124 static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
125 		const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
126 {
127 	struct msm_drm_private *priv = dev->dev_private;
128 	struct msm_kms *kms = priv->kms;
129 	struct msm_framebuffer *msm_fb = NULL;
130 	struct drm_framebuffer *fb;
131 	const struct msm_format *format;
132 	int ret, i, n;
133 	unsigned int hsub, vsub;
134 
135 	DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)",
136 			dev, mode_cmd, mode_cmd->width, mode_cmd->height,
137 			(char *)&mode_cmd->pixel_format);
138 
139 	n = drm_format_num_planes(mode_cmd->pixel_format);
140 	hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
141 	vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
142 
143 	format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
144 			mode_cmd->modifier[0]);
145 	if (!format) {
146 		DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %4.4s\n",
147 				(char *)&mode_cmd->pixel_format);
148 		ret = -EINVAL;
149 		goto fail;
150 	}
151 
152 	msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
153 	if (!msm_fb) {
154 		ret = -ENOMEM;
155 		goto fail;
156 	}
157 
158 	fb = &msm_fb->base;
159 
160 	msm_fb->format = format;
161 
162 	if (n > ARRAY_SIZE(fb->obj)) {
163 		ret = -EINVAL;
164 		goto fail;
165 	}
166 
167 	for (i = 0; i < n; i++) {
168 		unsigned int width = mode_cmd->width / (i ? hsub : 1);
169 		unsigned int height = mode_cmd->height / (i ? vsub : 1);
170 		unsigned int min_size;
171 
172 		min_size = (height - 1) * mode_cmd->pitches[i]
173 			 + width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
174 			 + mode_cmd->offsets[i];
175 
176 		if (bos[i]->size < min_size) {
177 			ret = -EINVAL;
178 			goto fail;
179 		}
180 
181 		msm_fb->base.obj[i] = bos[i];
182 	}
183 
184 	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
185 
186 	ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
187 	if (ret) {
188 		DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
189 		goto fail;
190 	}
191 
192 	DBG("create: FB ID: %d (%p)", fb->base.id, fb);
193 
194 	return fb;
195 
196 fail:
197 	kfree(msm_fb);
198 
199 	return ERR_PTR(ret);
200 }
201 
202 struct drm_framebuffer *
203 msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format)
204 {
205 	struct drm_mode_fb_cmd2 mode_cmd = {
206 		.pixel_format = format,
207 		.width = w,
208 		.height = h,
209 		.pitches = { p },
210 	};
211 	struct drm_gem_object *bo;
212 	struct drm_framebuffer *fb;
213 	int size;
214 
215 	/* allocate backing bo */
216 	size = mode_cmd.pitches[0] * mode_cmd.height;
217 	DBG("allocating %d bytes for fb %d", size, dev->primary->index);
218 	bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN);
219 	if (IS_ERR(bo)) {
220 		dev_warn(dev->dev, "could not allocate stolen bo\n");
221 		/* try regular bo: */
222 		bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
223 	}
224 	if (IS_ERR(bo)) {
225 		DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n");
226 		return ERR_CAST(bo);
227 	}
228 
229 	msm_gem_object_set_name(bo, "stolenfb");
230 
231 	fb = msm_framebuffer_init(dev, &mode_cmd, &bo);
232 	if (IS_ERR(fb)) {
233 		DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");
234 		/* note: if fb creation failed, we can't rely on fb destroy
235 		 * to unref the bo:
236 		 */
237 		drm_gem_object_put_unlocked(bo);
238 		return ERR_CAST(fb);
239 	}
240 
241 	return fb;
242 }
243