Linux Audio

Check our new training course

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