1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2012 Russell King
4 */
5
6#include <drm/drm_modeset_helper.h>
7#include <drm/drm_fb_helper.h>
8#include <drm/drm_fourcc.h>
9#include <drm/drm_gem_framebuffer_helper.h>
10
11#include "armada_drm.h"
12#include "armada_fb.h"
13#include "armada_gem.h"
14#include "armada_hw.h"
15
16static const struct drm_framebuffer_funcs armada_fb_funcs = {
17	.destroy	= drm_gem_fb_destroy,
18	.create_handle	= drm_gem_fb_create_handle,
19};
20
21struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev,
22	const struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj)
23{
24	struct armada_framebuffer *dfb;
25	uint8_t format, config;
26	int ret;
27
28	switch (mode->pixel_format) {
29#define FMT(drm, fmt, mod)		\
30	case DRM_FORMAT_##drm:		\
31		format = CFG_##fmt;	\
32		config = mod;		\
33		break
34	FMT(RGB565,	565,		CFG_SWAPRB);
35	FMT(BGR565,	565,		0);
36	FMT(ARGB1555,	1555,		CFG_SWAPRB);
37	FMT(ABGR1555,	1555,		0);
38	FMT(RGB888,	888PACK,	CFG_SWAPRB);
39	FMT(BGR888,	888PACK,	0);
40	FMT(XRGB8888,	X888,		CFG_SWAPRB);
41	FMT(XBGR8888,	X888,		0);
42	FMT(ARGB8888,	8888,		CFG_SWAPRB);
43	FMT(ABGR8888,	8888,		0);
44	FMT(YUYV,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);
45	FMT(UYVY,	422PACK,	CFG_YUV2RGB);
46	FMT(VYUY,	422PACK,	CFG_YUV2RGB | CFG_SWAPUV);
47	FMT(YVYU,	422PACK,	CFG_YUV2RGB | CFG_SWAPYU);
48	FMT(YUV422,	422,		CFG_YUV2RGB);
49	FMT(YVU422,	422,		CFG_YUV2RGB | CFG_SWAPUV);
50	FMT(YUV420,	420,		CFG_YUV2RGB);
51	FMT(YVU420,	420,		CFG_YUV2RGB | CFG_SWAPUV);
52	FMT(C8,		PSEUDO8,	0);
53#undef FMT
54	default:
55		return ERR_PTR(-EINVAL);
56	}
57
58	dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
59	if (!dfb) {
60		DRM_ERROR("failed to allocate Armada fb object\n");
61		return ERR_PTR(-ENOMEM);
62	}
63
64	dfb->fmt = format;
65	dfb->mod = config;
66	dfb->fb.obj[0] = &obj->obj;
67
68	drm_helper_mode_fill_fb_struct(dev, &dfb->fb, mode);
69
70	ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs);
71	if (ret) {
72		kfree(dfb);
73		return ERR_PTR(ret);
74	}
75
76	/*
77	 * Take a reference on our object as we're successful - the
78	 * caller already holds a reference, which keeps us safe for
79	 * the above call, but the caller will drop their reference
80	 * to it.  Hence we need to take our own reference.
81	 */
82	drm_gem_object_get(&obj->obj);
83
84	return dfb;
85}
86
87struct drm_framebuffer *armada_fb_create(struct drm_device *dev,
88	struct drm_file *dfile, const struct drm_mode_fb_cmd2 *mode)
89{
90	const struct drm_format_info *info = drm_get_format_info(dev, mode);
91	struct armada_gem_object *obj;
92	struct armada_framebuffer *dfb;
93	int ret;
94
95	DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n",
96		mode->width, mode->height, mode->pixel_format,
97		mode->flags, mode->pitches[0], mode->pitches[1],
98		mode->pitches[2]);
99
100	/* We can only handle a single plane at the moment */
101	if (info->num_planes > 1 &&
102	    (mode->handles[0] != mode->handles[1] ||
103	     mode->handles[0] != mode->handles[2])) {
104		ret = -EINVAL;
105		goto err;
106	}
107
108	obj = armada_gem_object_lookup(dfile, mode->handles[0]);
109	if (!obj) {
110		ret = -ENOENT;
111		goto err;
112	}
113
114	if (obj->obj.import_attach && !obj->sgt) {
115		ret = armada_gem_map_import(obj);
116		if (ret)
117			goto err_unref;
118	}
119
120	/* Framebuffer objects must have a valid device address for scanout */
121	if (!obj->mapped) {
122		ret = -EINVAL;
123		goto err_unref;
124	}
125
126	dfb = armada_framebuffer_create(dev, mode, obj);
127	if (IS_ERR(dfb)) {
128		ret = PTR_ERR(dfb);
129		goto err;
130	}
131
132	drm_gem_object_put(&obj->obj);
133
134	return &dfb->fb;
135
136 err_unref:
137	drm_gem_object_put(&obj->obj);
138 err:
139	DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
140	return ERR_PTR(ret);
141}
142