MAINTAINERS: mmc: Move the mmc tree to kernel.org
[cascardo/linux.git] / drivers / gpu / drm / sun4i / sun4i_layer.c
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_crtc.h>
15 #include <drm/drm_plane_helper.h>
16 #include <drm/drmP.h>
17
18 #include "sun4i_backend.h"
19 #include "sun4i_drv.h"
20 #include "sun4i_layer.h"
21
22 #define SUN4I_NUM_LAYERS        2
23
24 static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
25                                             struct drm_plane_state *state)
26 {
27         return 0;
28 }
29
30 static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
31                                                struct drm_plane_state *old_state)
32 {
33         struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
34         struct sun4i_drv *drv = layer->drv;
35         struct sun4i_backend *backend = drv->backend;
36
37         sun4i_backend_layer_enable(backend, layer->id, false);
38 }
39
40 static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
41                                               struct drm_plane_state *old_state)
42 {
43         struct sun4i_layer *layer = plane_to_sun4i_layer(plane);
44         struct sun4i_drv *drv = layer->drv;
45         struct sun4i_backend *backend = drv->backend;
46
47         sun4i_backend_update_layer_coord(backend, layer->id, plane);
48         sun4i_backend_update_layer_formats(backend, layer->id, plane);
49         sun4i_backend_update_layer_buffer(backend, layer->id, plane);
50         sun4i_backend_layer_enable(backend, layer->id, true);
51 }
52
53 static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
54         .atomic_check   = sun4i_backend_layer_atomic_check,
55         .atomic_disable = sun4i_backend_layer_atomic_disable,
56         .atomic_update  = sun4i_backend_layer_atomic_update,
57 };
58
59 static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
60         .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
61         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
62         .destroy                = drm_plane_cleanup,
63         .disable_plane          = drm_atomic_helper_disable_plane,
64         .reset                  = drm_atomic_helper_plane_reset,
65         .update_plane           = drm_atomic_helper_update_plane,
66 };
67
68 static const uint32_t sun4i_backend_layer_formats[] = {
69         DRM_FORMAT_ARGB8888,
70         DRM_FORMAT_XRGB8888,
71         DRM_FORMAT_RGB888,
72 };
73
74 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
75                                                 enum drm_plane_type type)
76 {
77         struct sun4i_drv *drv = drm->dev_private;
78         struct sun4i_layer *layer;
79         int ret;
80
81         layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
82         if (!layer)
83                 return ERR_PTR(-ENOMEM);
84
85         ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
86                                        &sun4i_backend_layer_funcs,
87                                        sun4i_backend_layer_formats,
88                                        ARRAY_SIZE(sun4i_backend_layer_formats),
89                                        type,
90                                        NULL);
91         if (ret) {
92                 dev_err(drm->dev, "Couldn't initialize layer\n");
93                 return ERR_PTR(ret);
94         }
95
96         drm_plane_helper_add(&layer->plane,
97                              &sun4i_backend_layer_helper_funcs);
98         layer->drv = drv;
99
100         if (type == DRM_PLANE_TYPE_PRIMARY)
101                 drv->primary = &layer->plane;
102
103         return layer;
104 }
105
106 struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
107 {
108         struct sun4i_drv *drv = drm->dev_private;
109         struct sun4i_layer **layers;
110         int i;
111
112         layers = devm_kcalloc(drm->dev, SUN4I_NUM_LAYERS, sizeof(**layers),
113                               GFP_KERNEL);
114         if (!layers)
115                 return ERR_PTR(-ENOMEM);
116
117         /*
118          * The hardware is a bit unusual here.
119          *
120          * Even though it supports 4 layers, it does the composition
121          * in two separate steps.
122          *
123          * The first one is assigning a layer to one of its two
124          * pipes. If more that 1 layer is assigned to the same pipe,
125          * and if pixels overlaps, the pipe will take the pixel from
126          * the layer with the highest priority.
127          *
128          * The second step is the actual alpha blending, that takes
129          * the two pipes as input, and uses the eventual alpha
130          * component to do the transparency between the two.
131          *
132          * This two steps scenario makes us unable to guarantee a
133          * robust alpha blending between the 4 layers in all
134          * situations. So we just expose two layers, one per pipe. On
135          * SoCs that support it, sprites could fill the need for more
136          * layers.
137          */
138         for (i = 0; i < SUN4I_NUM_LAYERS; i++) {
139                 enum drm_plane_type type = (i == 0)
140                                          ? DRM_PLANE_TYPE_PRIMARY
141                                          : DRM_PLANE_TYPE_OVERLAY;
142                 struct sun4i_layer *layer = layers[i];
143
144                 layer = sun4i_layer_init_one(drm, type);
145                 if (IS_ERR(layer)) {
146                         dev_err(drm->dev, "Couldn't initialize %s plane\n",
147                                 i ? "overlay" : "primary");
148                         return ERR_CAST(layer);
149                 };
150
151                 DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
152                                  i ? "overlay" : "primary", i);
153                 regmap_update_bits(drv->backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
154                                    SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
155                                    SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(i));
156
157                 layer->id = i;
158         };
159
160         return layers;
161 }