1 /*
2  * Copyright (C) 2015 Free Electrons
3  * Copyright (C) 2015 NextThing Co
4  *
5  * Maxime Ripard <maxime.ripard@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  */
12 
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_plane_helper.h>
15 #include <drm/drmP.h>
16 
17 #include "sun4i_backend.h"
18 #include "sun4i_layer.h"
19 #include "sunxi_engine.h"
20 
21 struct sun4i_plane_desc {
22 	       enum drm_plane_type     type;
23 	       u8                      pipe;
24 	       const uint32_t          *formats;
25 	       uint32_t                nformats;
26 };
27 
28 static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
29 					       struct drm_plane_state *old_state)
30 {
31 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
32 	struct sun4i_backend *backend = layer->backend;
33 
34 	sun4i_backend_layer_enable(backend, layer->id, false);
35 }
36 
37 static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
38 					      struct drm_plane_state *old_state)
39 {
40 	struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
41 	struct sun4i_backend *backend = layer->backend;
42 
43 	sun4i_backend_update_layer_coord(backend, layer->id, plane);
44 	sun4i_backend_update_layer_formats(backend, layer->id, plane);
45 	sun4i_backend_update_layer_buffer(backend, layer->id, plane);
46 	sun4i_backend_layer_enable(backend, layer->id, true);
47 }
48 
49 static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
50 	.atomic_disable	= sun4i_backend_layer_atomic_disable,
51 	.atomic_update	= sun4i_backend_layer_atomic_update,
52 };
53 
54 static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
55 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
56 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
57 	.destroy		= drm_plane_cleanup,
58 	.disable_plane		= drm_atomic_helper_disable_plane,
59 	.reset			= drm_atomic_helper_plane_reset,
60 	.update_plane		= drm_atomic_helper_update_plane,
61 };
62 
63 static const uint32_t sun4i_backend_layer_formats_primary[] = {
64 	DRM_FORMAT_ARGB8888,
65 	DRM_FORMAT_RGB888,
66 	DRM_FORMAT_RGB565,
67 	DRM_FORMAT_XRGB8888,
68 };
69 
70 static const uint32_t sun4i_backend_layer_formats_overlay[] = {
71 	DRM_FORMAT_ARGB8888,
72 	DRM_FORMAT_ARGB4444,
73 	DRM_FORMAT_ARGB1555,
74 	DRM_FORMAT_RGBA5551,
75 	DRM_FORMAT_RGBA4444,
76 	DRM_FORMAT_RGB888,
77 	DRM_FORMAT_RGB565,
78 	DRM_FORMAT_XRGB8888,
79 };
80 
81 static const struct sun4i_plane_desc sun4i_backend_planes[] = {
82 	{
83 		.type = DRM_PLANE_TYPE_PRIMARY,
84 		.pipe = 0,
85 		.formats = sun4i_backend_layer_formats_primary,
86 		.nformats = ARRAY_SIZE(sun4i_backend_layer_formats_primary),
87 	},
88 	{
89 		.type = DRM_PLANE_TYPE_OVERLAY,
90 		.pipe = 1,
91 		.formats = sun4i_backend_layer_formats_overlay,
92 		.nformats = ARRAY_SIZE(sun4i_backend_layer_formats_overlay),
93 	},
94 };
95 
96 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
97 						struct sun4i_backend *backend,
98 						const struct sun4i_plane_desc *plane)
99 {
100 	struct sun4i_layer *layer;
101 	int ret;
102 
103 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
104 	if (!layer)
105 		return ERR_PTR(-ENOMEM);
106 
107 	/* possible crtcs are set later */
108 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
109 				       &sun4i_backend_layer_funcs,
110 				       plane->formats, plane->nformats,
111 				       NULL, plane->type, NULL);
112 	if (ret) {
113 		dev_err(drm->dev, "Couldn't initialize layer\n");
114 		return ERR_PTR(ret);
115 	}
116 
117 	drm_plane_helper_add(&layer->plane,
118 			     &sun4i_backend_layer_helper_funcs);
119 	layer->backend = backend;
120 
121 	return layer;
122 }
123 
124 struct drm_plane **sun4i_layers_init(struct drm_device *drm,
125 				     struct sunxi_engine *engine)
126 {
127 	struct drm_plane **planes;
128 	struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
129 	int i;
130 
131 	planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
132 			      sizeof(*planes), GFP_KERNEL);
133 	if (!planes)
134 		return ERR_PTR(-ENOMEM);
135 
136 	/*
137 	 * The hardware is a bit unusual here.
138 	 *
139 	 * Even though it supports 4 layers, it does the composition
140 	 * in two separate steps.
141 	 *
142 	 * The first one is assigning a layer to one of its two
143 	 * pipes. If more that 1 layer is assigned to the same pipe,
144 	 * and if pixels overlaps, the pipe will take the pixel from
145 	 * the layer with the highest priority.
146 	 *
147 	 * The second step is the actual alpha blending, that takes
148 	 * the two pipes as input, and uses the eventual alpha
149 	 * component to do the transparency between the two.
150 	 *
151 	 * This two steps scenario makes us unable to guarantee a
152 	 * robust alpha blending between the 4 layers in all
153 	 * situations. So we just expose two layers, one per pipe. On
154 	 * SoCs that support it, sprites could fill the need for more
155 	 * layers.
156 	 */
157 	for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) {
158 		const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i];
159 		struct sun4i_layer *layer;
160 
161 		layer = sun4i_layer_init_one(drm, backend, plane);
162 		if (IS_ERR(layer)) {
163 			dev_err(drm->dev, "Couldn't initialize %s plane\n",
164 				i ? "overlay" : "primary");
165 			return ERR_CAST(layer);
166 		};
167 
168 		DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
169 				 i ? "overlay" : "primary", plane->pipe);
170 		regmap_update_bits(engine->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
171 				   SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
172 				   SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe));
173 
174 		layer->id = i;
175 		planes[i] = &layer->plane;
176 	};
177 
178 	return planes;
179 }
180