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