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