xref: /linux/drivers/gpu/drm/msm/msm_fb.c (revision fdc290ff4ab19c7e0dde36c4cd1e2771b61f6bf5)
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_damage_helper.h>
9 #include <drm/drm_fourcc.h>
10 #include <drm/drm_framebuffer.h>
11 #include <drm/drm_gem_framebuffer_helper.h>
12 #include <drm/drm_probe_helper.h>
13 
14 #include "msm_drv.h"
15 #include "msm_kms.h"
16 #include "msm_gem.h"
17 
18 struct msm_framebuffer {
19 	struct drm_framebuffer base;
20 	const struct msm_format *format;
21 
22 	/* Count of # of attached planes which need dirtyfb: */
23 	refcount_t dirtyfb;
24 
25 	/* Framebuffer per-plane address, if pinned, else zero: */
26 	uint64_t iova[DRM_FORMAT_MAX_PLANES];
27 	atomic_t prepare_count;
28 };
29 #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
30 
31 static int msm_framebuffer_dirtyfb(struct drm_framebuffer *fb,
32 				   struct drm_file *file_priv, unsigned int flags,
33 				   unsigned int color, struct drm_clip_rect *clips,
34 				   unsigned int num_clips)
35 {
36 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
37 
38 	/* If this fb is not used on any display requiring pixel data to be
39 	 * flushed, then skip dirtyfb
40 	 */
41 	if (refcount_read(&msm_fb->dirtyfb) == 1)
42 		return 0;
43 
44 	return drm_atomic_helper_dirtyfb(fb, file_priv, flags, color,
45 					 clips, num_clips);
46 }
47 
48 static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
49 	.create_handle = drm_gem_fb_create_handle,
50 	.destroy = drm_gem_fb_destroy,
51 	.dirty = msm_framebuffer_dirtyfb,
52 };
53 
54 #ifdef CONFIG_DEBUG_FS
55 void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
56 {
57 	struct msm_gem_stats stats = {};
58 	int i, n = fb->format->num_planes;
59 
60 	seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
61 			fb->width, fb->height, (char *)&fb->format->format,
62 			drm_framebuffer_read_refcount(fb), fb->base.id);
63 
64 	for (i = 0; i < n; i++) {
65 		seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
66 				i, fb->offsets[i], fb->pitches[i]);
67 		msm_gem_describe(fb->obj[i], m, &stats);
68 	}
69 }
70 #endif
71 
72 /* prepare/pin all the fb's bo's for scanout.
73  */
74 int msm_framebuffer_prepare(struct drm_framebuffer *fb, bool needs_dirtyfb)
75 {
76 	struct msm_drm_private *priv = fb->dev->dev_private;
77 	struct drm_gpuvm *vm = priv->kms->vm;
78 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
79 	int ret, i, n = fb->format->num_planes;
80 
81 	if (needs_dirtyfb)
82 		refcount_inc(&msm_fb->dirtyfb);
83 
84 	if (atomic_inc_return(&msm_fb->prepare_count) > 1)
85 		return 0;
86 
87 	for (i = 0; i < n; i++) {
88 		msm_gem_vma_get(fb->obj[i]);
89 		ret = msm_gem_get_and_pin_iova(fb->obj[i], vm, &msm_fb->iova[i]);
90 		drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)\n",
91 			      fb->base.id, i, msm_fb->iova[i], ret);
92 		if (ret)
93 			return ret;
94 	}
95 
96 	return 0;
97 }
98 
99 void msm_framebuffer_cleanup(struct drm_framebuffer *fb, bool needed_dirtyfb)
100 {
101 	struct msm_drm_private *priv = fb->dev->dev_private;
102 	struct drm_gpuvm *vm = priv->kms->vm;
103 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
104 	int i, n = fb->format->num_planes;
105 
106 	if (needed_dirtyfb)
107 		refcount_dec(&msm_fb->dirtyfb);
108 
109 	if (atomic_dec_return(&msm_fb->prepare_count))
110 		return;
111 
112 	memset(msm_fb->iova, 0, sizeof(msm_fb->iova));
113 
114 	for (i = 0; i < n; i++) {
115 		msm_gem_unpin_iova(fb->obj[i], vm);
116 		msm_gem_vma_put(fb->obj[i]);
117 	}
118 }
119 
120 uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, int plane)
121 {
122 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
123 	return msm_fb->iova[plane] + fb->offsets[plane];
124 }
125 
126 struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
127 {
128 	return drm_gem_fb_get_obj(fb, plane);
129 }
130 
131 const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
132 {
133 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
134 	return msm_fb->format;
135 }
136 
137 static struct drm_framebuffer *
138 msm_framebuffer_init(struct drm_device *dev, const struct drm_format_info *info,
139 		     const struct drm_mode_fb_cmd2 *mode_cmd,
140 		     struct drm_gem_object **bos)
141 {
142 	struct msm_drm_private *priv = dev->dev_private;
143 	struct msm_kms *kms = priv->kms;
144 	struct msm_framebuffer *msm_fb = NULL;
145 	struct drm_framebuffer *fb;
146 	const struct msm_format *format;
147 	int ret, i, n;
148 
149 	drm_dbg_state(dev, "create framebuffer: mode_cmd=%p (%dx%d@%p4cc)\n",
150 		      mode_cmd, mode_cmd->width, mode_cmd->height,
151 		      &mode_cmd->pixel_format);
152 
153 	n = info->num_planes;
154 	format = mdp_get_format(kms, mode_cmd->pixel_format,
155 			mode_cmd->modifier[0]);
156 	if (!format) {
157 		DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %p4cc\n",
158 			      &mode_cmd->pixel_format);
159 		ret = -EINVAL;
160 		goto fail;
161 	}
162 
163 	msm_fb = kzalloc_obj(*msm_fb);
164 	if (!msm_fb) {
165 		ret = -ENOMEM;
166 		goto fail;
167 	}
168 
169 	fb = &msm_fb->base;
170 
171 	msm_fb->format = format;
172 
173 	if (n > ARRAY_SIZE(fb->obj)) {
174 		ret = -EINVAL;
175 		goto fail;
176 	}
177 
178 	for (i = 0; i < n; i++) {
179 		unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
180 		unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
181 		unsigned int min_size;
182 
183 		min_size = (height - 1) * mode_cmd->pitches[i]
184 			 + width * info->cpp[i]
185 			 + mode_cmd->offsets[i];
186 
187 		if (bos[i]->size < min_size) {
188 			ret = UERR(EINVAL, dev, "plane %d too small", i);
189 			goto fail;
190 		}
191 
192 		if (to_msm_bo(bos[i])->flags & MSM_BO_NO_SHARE) {
193 			ret = UERR(EINVAL, dev, "Cannot map _NO_SHARE to kms vm");
194 			goto fail;
195 		}
196 
197 		msm_fb->base.obj[i] = bos[i];
198 	}
199 
200 	drm_helper_mode_fill_fb_struct(dev, fb, info, mode_cmd);
201 
202 	ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
203 	if (ret) {
204 		DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
205 		goto fail;
206 	}
207 
208 	refcount_set(&msm_fb->dirtyfb, 1);
209 
210 	drm_dbg_state(dev, "create: FB ID: %d (%p)\n", fb->base.id, fb);
211 
212 	return fb;
213 
214 fail:
215 	kfree(msm_fb);
216 
217 	return ERR_PTR(ret);
218 }
219 
220 struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
221 					       struct drm_file *file,
222 					       const struct drm_format_info *info,
223 					       const struct drm_mode_fb_cmd2 *mode_cmd)
224 {
225 	struct drm_gem_object *bos[4] = {0};
226 	struct drm_framebuffer *fb;
227 	int ret, i, n = info->num_planes;
228 
229 	for (i = 0; i < n; i++) {
230 		bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
231 		if (!bos[i]) {
232 			ret = -ENXIO;
233 			goto out_unref;
234 		}
235 	}
236 
237 	fb = msm_framebuffer_init(dev, info, mode_cmd, bos);
238 	if (IS_ERR(fb)) {
239 		ret = PTR_ERR(fb);
240 		goto out_unref;
241 	}
242 
243 	return fb;
244 
245 out_unref:
246 	for (i = 0; i < n; i++)
247 		drm_gem_object_put(bos[i]);
248 	return ERR_PTR(ret);
249 }
250