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