xref: /openbmc/linux/drivers/gpu/drm/msm/msm_fb.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c8afe684SRob Clark /*
3c8afe684SRob Clark  * Copyright (C) 2013 Red Hat
4c8afe684SRob Clark  * Author: Rob Clark <robdclark@gmail.com>
5c8afe684SRob Clark  */
6c8afe684SRob Clark 
778f27b1cSMasahiro Yamada #include <drm/drm_crtc.h>
8648fdc3fSBrian Masney #include <drm/drm_damage_helper.h>
9feea39a8SSam Ravnborg #include <drm/drm_file.h>
10feea39a8SSam Ravnborg #include <drm/drm_fourcc.h>
11*720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
1208020509SDaniel Stone #include <drm/drm_gem_framebuffer_helper.h>
13fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
1478f27b1cSMasahiro Yamada 
15c8afe684SRob Clark #include "msm_drv.h"
16dd2da6e3SRob Clark #include "msm_kms.h"
17466e5606SRob Clark #include "msm_gem.h"
18c8afe684SRob Clark 
19c8afe684SRob Clark struct msm_framebuffer {
20c8afe684SRob Clark 	struct drm_framebuffer base;
21c8afe684SRob Clark 	const struct msm_format *format;
229e4dde28SRob Clark 
239e4dde28SRob Clark 	/* Count of # of attached planes which need dirtyfb: */
249e4dde28SRob Clark 	refcount_t dirtyfb;
25d413e6f9SRob Clark 
26d413e6f9SRob Clark 	/* Framebuffer per-plane address, if pinned, else zero: */
27d413e6f9SRob Clark 	uint64_t iova[DRM_FORMAT_MAX_PLANES];
28d413e6f9SRob Clark 	atomic_t prepare_count;
29c8afe684SRob Clark };
30c8afe684SRob Clark #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base)
31c8afe684SRob Clark 
326c0693b1SRob Clark static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
336c0693b1SRob Clark 		const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos);
34c8afe684SRob Clark 
msm_framebuffer_dirtyfb(struct drm_framebuffer * fb,struct drm_file * file_priv,unsigned int flags,unsigned int color,struct drm_clip_rect * clips,unsigned int num_clips)359e4dde28SRob Clark static int msm_framebuffer_dirtyfb(struct drm_framebuffer *fb,
369e4dde28SRob Clark 				   struct drm_file *file_priv, unsigned int flags,
379e4dde28SRob Clark 				   unsigned int color, struct drm_clip_rect *clips,
389e4dde28SRob Clark 				   unsigned int num_clips)
399e4dde28SRob Clark {
409e4dde28SRob Clark 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
419e4dde28SRob Clark 
429e4dde28SRob Clark 	/* If this fb is not used on any display requiring pixel data to be
439e4dde28SRob Clark 	 * flushed, then skip dirtyfb
449e4dde28SRob Clark 	 */
459225b337SRob Clark 	if (refcount_read(&msm_fb->dirtyfb) == 1)
469e4dde28SRob Clark 		return 0;
479e4dde28SRob Clark 
489e4dde28SRob Clark 	return drm_atomic_helper_dirtyfb(fb, file_priv, flags, color,
499e4dde28SRob Clark 					 clips, num_clips);
509e4dde28SRob Clark }
519e4dde28SRob Clark 
52c8afe684SRob Clark static const struct drm_framebuffer_funcs msm_framebuffer_funcs = {
5308020509SDaniel Stone 	.create_handle = drm_gem_fb_create_handle,
5408020509SDaniel Stone 	.destroy = drm_gem_fb_destroy,
559e4dde28SRob Clark 	.dirty = msm_framebuffer_dirtyfb,
56c8afe684SRob Clark };
57c8afe684SRob Clark 
58c8afe684SRob Clark #ifdef CONFIG_DEBUG_FS
msm_framebuffer_describe(struct drm_framebuffer * fb,struct seq_file * m)59c8afe684SRob Clark void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
60c8afe684SRob Clark {
61528107c8SRob Clark 	struct msm_gem_stats stats = {};
62bcb0b461SVille Syrjälä 	int i, n = fb->format->num_planes;
63c8afe684SRob Clark 
64c8afe684SRob Clark 	seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
65438b74a5SVille Syrjälä 			fb->width, fb->height, (char *)&fb->format->format,
66747a598fSDave Airlie 			drm_framebuffer_read_refcount(fb), fb->base.id);
67c8afe684SRob Clark 
68c8afe684SRob Clark 	for (i = 0; i < n; i++) {
69c8afe684SRob Clark 		seq_printf(m, "   %d: offset=%d pitch=%d, obj: ",
70c8afe684SRob Clark 				i, fb->offsets[i], fb->pitches[i]);
71528107c8SRob Clark 		msm_gem_describe(fb->obj[i], m, &stats);
72c8afe684SRob Clark 	}
73c8afe684SRob Clark }
74c8afe684SRob Clark #endif
75c8afe684SRob Clark 
769e4dde28SRob Clark /* prepare/pin all the fb's bo's for scanout.
772638d90aSRob Clark  */
msm_framebuffer_prepare(struct drm_framebuffer * fb,struct msm_gem_address_space * aspace,bool needs_dirtyfb)788bdcd949SRob Clark int msm_framebuffer_prepare(struct drm_framebuffer *fb,
799e4dde28SRob Clark 		struct msm_gem_address_space *aspace,
809e4dde28SRob Clark 		bool needs_dirtyfb)
812638d90aSRob Clark {
829e4dde28SRob Clark 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
83bcb0b461SVille Syrjälä 	int ret, i, n = fb->format->num_planes;
842638d90aSRob Clark 
859e4dde28SRob Clark 	if (needs_dirtyfb)
869e4dde28SRob Clark 		refcount_inc(&msm_fb->dirtyfb);
879e4dde28SRob Clark 
88d413e6f9SRob Clark 	atomic_inc(&msm_fb->prepare_count);
89d413e6f9SRob Clark 
902638d90aSRob Clark 	for (i = 0; i < n; i++) {
91d413e6f9SRob Clark 		ret = msm_gem_get_and_pin_iova(fb->obj[i], aspace, &msm_fb->iova[i]);
92d413e6f9SRob Clark 		drm_dbg_state(fb->dev, "FB[%u]: iova[%d]: %08llx (%d)",
93d413e6f9SRob Clark 			      fb->base.id, i, msm_fb->iova[i], ret);
942638d90aSRob Clark 		if (ret)
952638d90aSRob Clark 			return ret;
962638d90aSRob Clark 	}
972638d90aSRob Clark 
982638d90aSRob Clark 	return 0;
992638d90aSRob Clark }
1002638d90aSRob Clark 
msm_framebuffer_cleanup(struct drm_framebuffer * fb,struct msm_gem_address_space * aspace,bool needed_dirtyfb)1018bdcd949SRob Clark void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
1029e4dde28SRob Clark 		struct msm_gem_address_space *aspace,
1039e4dde28SRob Clark 		bool needed_dirtyfb)
1042638d90aSRob Clark {
1059e4dde28SRob Clark 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
106bcb0b461SVille Syrjälä 	int i, n = fb->format->num_planes;
1072638d90aSRob Clark 
1089e4dde28SRob Clark 	if (needed_dirtyfb)
1099e4dde28SRob Clark 		refcount_dec(&msm_fb->dirtyfb);
1109e4dde28SRob Clark 
1112638d90aSRob Clark 	for (i = 0; i < n; i++)
1127ad0e8cfSJordan Crouse 		msm_gem_unpin_iova(fb->obj[i], aspace);
113d413e6f9SRob Clark 
114d413e6f9SRob Clark 	if (!atomic_dec_return(&msm_fb->prepare_count))
115d413e6f9SRob Clark 		memset(msm_fb->iova, 0, sizeof(msm_fb->iova));
1162638d90aSRob Clark }
1172638d90aSRob Clark 
msm_framebuffer_iova(struct drm_framebuffer * fb,struct msm_gem_address_space * aspace,int plane)1188bdcd949SRob Clark uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
1198bdcd949SRob Clark 		struct msm_gem_address_space *aspace, int plane)
1202638d90aSRob Clark {
121d413e6f9SRob Clark 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
122cec4e5cbSRob Clark 	return msm_fb->iova[plane] + fb->offsets[plane];
1232638d90aSRob Clark }
1242638d90aSRob Clark 
msm_framebuffer_bo(struct drm_framebuffer * fb,int plane)125c8afe684SRob Clark struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
126c8afe684SRob Clark {
12708020509SDaniel Stone 	return drm_gem_fb_get_obj(fb, plane);
128c8afe684SRob Clark }
129c8afe684SRob Clark 
msm_framebuffer_format(struct drm_framebuffer * fb)130c8afe684SRob Clark const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb)
131c8afe684SRob Clark {
132c8afe684SRob Clark 	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
133c8afe684SRob Clark 	return msm_fb->format;
134c8afe684SRob Clark }
135c8afe684SRob Clark 
msm_framebuffer_create(struct drm_device * dev,struct drm_file * file,const struct drm_mode_fb_cmd2 * mode_cmd)136c8afe684SRob Clark struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev,
1371eb83451SVille Syrjälä 		struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
138c8afe684SRob Clark {
13905c452c1SMaxime Ripard 	const struct drm_format_info *info = drm_get_format_info(dev,
14005c452c1SMaxime Ripard 								 mode_cmd);
141c8afe684SRob Clark 	struct drm_gem_object *bos[4] = {0};
142c8afe684SRob Clark 	struct drm_framebuffer *fb;
14305c452c1SMaxime Ripard 	int ret, i, n = info->num_planes;
144c8afe684SRob Clark 
145c8afe684SRob Clark 	for (i = 0; i < n; i++) {
146a8ad0bd8SChris Wilson 		bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
147c8afe684SRob Clark 		if (!bos[i]) {
148c8afe684SRob Clark 			ret = -ENXIO;
149c8afe684SRob Clark 			goto out_unref;
150c8afe684SRob Clark 		}
151c8afe684SRob Clark 	}
152c8afe684SRob Clark 
153c8afe684SRob Clark 	fb = msm_framebuffer_init(dev, mode_cmd, bos);
154c8afe684SRob Clark 	if (IS_ERR(fb)) {
155c8afe684SRob Clark 		ret = PTR_ERR(fb);
156c8afe684SRob Clark 		goto out_unref;
157c8afe684SRob Clark 	}
158c8afe684SRob Clark 
159c8afe684SRob Clark 	return fb;
160c8afe684SRob Clark 
161c8afe684SRob Clark out_unref:
162c8afe684SRob Clark 	for (i = 0; i < n; i++)
163f7d33950SEmil Velikov 		drm_gem_object_put(bos[i]);
164c8afe684SRob Clark 	return ERR_PTR(ret);
165c8afe684SRob Clark }
166c8afe684SRob Clark 
msm_framebuffer_init(struct drm_device * dev,const struct drm_mode_fb_cmd2 * mode_cmd,struct drm_gem_object ** bos)1676c0693b1SRob Clark static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev,
1681eb83451SVille Syrjälä 		const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
169c8afe684SRob Clark {
17005c452c1SMaxime Ripard 	const struct drm_format_info *info = drm_get_format_info(dev,
17105c452c1SMaxime Ripard 								 mode_cmd);
172c8afe684SRob Clark 	struct msm_drm_private *priv = dev->dev_private;
173c8afe684SRob Clark 	struct msm_kms *kms = priv->kms;
1747194b62cSStephane Viau 	struct msm_framebuffer *msm_fb = NULL;
1757194b62cSStephane Viau 	struct drm_framebuffer *fb;
176c8afe684SRob Clark 	const struct msm_format *format;
177c8afe684SRob Clark 	int ret, i, n;
178c8afe684SRob Clark 
1797cb017dbSStephen Boyd 	drm_dbg_state(dev, "create framebuffer: mode_cmd=%p (%dx%d@%4.4s)",
1807cb017dbSStephen Boyd 			mode_cmd, mode_cmd->width, mode_cmd->height,
181c8afe684SRob Clark 			(char *)&mode_cmd->pixel_format);
182c8afe684SRob Clark 
18305c452c1SMaxime Ripard 	n = info->num_planes;
184f2f3df0aSJeykumar Sankaran 	format = kms->funcs->get_format(kms, mode_cmd->pixel_format,
185f2f3df0aSJeykumar Sankaran 			mode_cmd->modifier[0]);
186c8afe684SRob Clark 	if (!format) {
1876a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev, "unsupported pixel format: %4.4s\n",
188c8afe684SRob Clark 				(char *)&mode_cmd->pixel_format);
189c8afe684SRob Clark 		ret = -EINVAL;
190c8afe684SRob Clark 		goto fail;
191c8afe684SRob Clark 	}
192c8afe684SRob Clark 
193c8afe684SRob Clark 	msm_fb = kzalloc(sizeof(*msm_fb), GFP_KERNEL);
194c8afe684SRob Clark 	if (!msm_fb) {
195c8afe684SRob Clark 		ret = -ENOMEM;
196c8afe684SRob Clark 		goto fail;
197c8afe684SRob Clark 	}
198c8afe684SRob Clark 
199c8afe684SRob Clark 	fb = &msm_fb->base;
200c8afe684SRob Clark 
201c8afe684SRob Clark 	msm_fb->format = format;
202c8afe684SRob Clark 
20308020509SDaniel Stone 	if (n > ARRAY_SIZE(fb->obj)) {
20410291bffSRob Clark 		ret = -EINVAL;
20510291bffSRob Clark 		goto fail;
20610291bffSRob Clark 	}
20710291bffSRob Clark 
208c8afe684SRob Clark 	for (i = 0; i < n; i++) {
209f3e9632cSMaxime Ripard 		unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
210f3e9632cSMaxime Ripard 		unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
211c8afe684SRob Clark 		unsigned int min_size;
212c8afe684SRob Clark 
213c8afe684SRob Clark 		min_size = (height - 1) * mode_cmd->pitches[i]
214b0f986b4SMaxime Ripard 			 + width * info->cpp[i]
215c8afe684SRob Clark 			 + mode_cmd->offsets[i];
216c8afe684SRob Clark 
217c8afe684SRob Clark 		if (bos[i]->size < min_size) {
218c8afe684SRob Clark 			ret = -EINVAL;
219c8afe684SRob Clark 			goto fail;
220c8afe684SRob Clark 		}
221c8afe684SRob Clark 
22208020509SDaniel Stone 		msm_fb->base.obj[i] = bos[i];
223c8afe684SRob Clark 	}
224c8afe684SRob Clark 
225a3f913caSVille Syrjälä 	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
226c8afe684SRob Clark 
227c8afe684SRob Clark 	ret = drm_framebuffer_init(dev, fb, &msm_framebuffer_funcs);
228c8afe684SRob Clark 	if (ret) {
2296a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev, "framebuffer init failed: %d\n", ret);
230c8afe684SRob Clark 		goto fail;
231c8afe684SRob Clark 	}
232c8afe684SRob Clark 
2339225b337SRob Clark 	refcount_set(&msm_fb->dirtyfb, 1);
2349225b337SRob Clark 
2357cb017dbSStephen Boyd 	drm_dbg_state(dev, "create: FB ID: %d (%p)", fb->base.id, fb);
236c8afe684SRob Clark 
237c8afe684SRob Clark 	return fb;
238c8afe684SRob Clark 
239c8afe684SRob Clark fail:
2407194b62cSStephane Viau 	kfree(msm_fb);
241c8afe684SRob Clark 
242c8afe684SRob Clark 	return ERR_PTR(ret);
243c8afe684SRob Clark }
244466e5606SRob Clark 
245466e5606SRob Clark struct drm_framebuffer *
msm_alloc_stolen_fb(struct drm_device * dev,int w,int h,int p,uint32_t format)246466e5606SRob Clark msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format)
247466e5606SRob Clark {
248466e5606SRob Clark 	struct drm_mode_fb_cmd2 mode_cmd = {
249466e5606SRob Clark 		.pixel_format = format,
250466e5606SRob Clark 		.width = w,
251466e5606SRob Clark 		.height = h,
252466e5606SRob Clark 		.pitches = { p },
253466e5606SRob Clark 	};
254466e5606SRob Clark 	struct drm_gem_object *bo;
255466e5606SRob Clark 	struct drm_framebuffer *fb;
256466e5606SRob Clark 	int size;
257466e5606SRob Clark 
258466e5606SRob Clark 	/* allocate backing bo */
259466e5606SRob Clark 	size = mode_cmd.pitches[0] * mode_cmd.height;
260466e5606SRob Clark 	DBG("allocating %d bytes for fb %d", size, dev->primary->index);
261466e5606SRob Clark 	bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN);
262466e5606SRob Clark 	if (IS_ERR(bo)) {
263466e5606SRob Clark 		dev_warn(dev->dev, "could not allocate stolen bo\n");
264466e5606SRob Clark 		/* try regular bo: */
265466e5606SRob Clark 		bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC);
266466e5606SRob Clark 	}
267466e5606SRob Clark 	if (IS_ERR(bo)) {
2686a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n");
269466e5606SRob Clark 		return ERR_CAST(bo);
270466e5606SRob Clark 	}
271466e5606SRob Clark 
2720815d774SJordan Crouse 	msm_gem_object_set_name(bo, "stolenfb");
2730815d774SJordan Crouse 
274466e5606SRob Clark 	fb = msm_framebuffer_init(dev, &mode_cmd, &bo);
275466e5606SRob Clark 	if (IS_ERR(fb)) {
2766a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n");
277466e5606SRob Clark 		/* note: if fb creation failed, we can't rely on fb destroy
278466e5606SRob Clark 		 * to unref the bo:
279466e5606SRob Clark 		 */
280f7d33950SEmil Velikov 		drm_gem_object_put(bo);
281466e5606SRob Clark 		return ERR_CAST(fb);
282466e5606SRob Clark 	}
283466e5606SRob Clark 
284466e5606SRob Clark 	return fb;
285466e5606SRob Clark }
286