GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / gpu / drm / armada / armada_plane.c
1 /*
2  * Copyright (C) 2012 Russell King
3  *  Rewritten from the dovefb driver, and Armada510 manuals.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9 #include <drm/drmP.h>
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_plane_helper.h>
13 #include "armada_crtc.h"
14 #include "armada_drm.h"
15 #include "armada_fb.h"
16 #include "armada_gem.h"
17 #include "armada_hw.h"
18 #include "armada_plane.h"
19 #include "armada_trace.h"
20
21 static const uint32_t armada_primary_formats[] = {
22         DRM_FORMAT_UYVY,
23         DRM_FORMAT_YUYV,
24         DRM_FORMAT_VYUY,
25         DRM_FORMAT_YVYU,
26         DRM_FORMAT_ARGB8888,
27         DRM_FORMAT_ABGR8888,
28         DRM_FORMAT_XRGB8888,
29         DRM_FORMAT_XBGR8888,
30         DRM_FORMAT_RGB888,
31         DRM_FORMAT_BGR888,
32         DRM_FORMAT_ARGB1555,
33         DRM_FORMAT_ABGR1555,
34         DRM_FORMAT_RGB565,
35         DRM_FORMAT_BGR565,
36 };
37
38 void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3],
39         u16 pitches[3], bool interlaced)
40 {
41         struct drm_framebuffer *fb = state->fb;
42         const struct drm_format_info *format = fb->format;
43         unsigned int num_planes = format->num_planes;
44         unsigned int x = state->src.x1 >> 16;
45         unsigned int y = state->src.y1 >> 16;
46         u32 addr = drm_fb_obj(fb)->dev_addr;
47         int i;
48
49         DRM_DEBUG_KMS("pitch %u x %d y %d bpp %d\n",
50                       fb->pitches[0], x, y, format->cpp[0] * 8);
51
52         if (num_planes > 3)
53                 num_planes = 3;
54
55         addrs[0][0] = addr + fb->offsets[0] + y * fb->pitches[0] +
56                       x * format->cpp[0];
57         pitches[0] = fb->pitches[0];
58
59         y /= format->vsub;
60         x /= format->hsub;
61
62         for (i = 1; i < num_planes; i++) {
63                 addrs[0][i] = addr + fb->offsets[i] + y * fb->pitches[i] +
64                               x * format->cpp[i];
65                 pitches[i] = fb->pitches[i];
66         }
67         for (; i < 3; i++) {
68                 addrs[0][i] = 0;
69                 pitches[i] = 0;
70         }
71         if (interlaced) {
72                 for (i = 0; i < 3; i++) {
73                         addrs[1][i] = addrs[0][i] + pitches[i];
74                         pitches[i] *= 2;
75                 }
76         } else {
77                 for (i = 0; i < 3; i++)
78                         addrs[1][i] = addrs[0][i];
79         }
80 }
81
82 static unsigned armada_drm_crtc_calc_fb(struct drm_plane_state *state,
83         struct armada_regs *regs, bool interlaced)
84 {
85         u16 pitches[3];
86         u32 addrs[2][3];
87         unsigned i = 0;
88
89         armada_drm_plane_calc(state, addrs, pitches, interlaced);
90
91         /* write offset, base, and pitch */
92         armada_reg_queue_set(regs, i, addrs[0][0], LCD_CFG_GRA_START_ADDR0);
93         armada_reg_queue_set(regs, i, addrs[1][0], LCD_CFG_GRA_START_ADDR1);
94         armada_reg_queue_mod(regs, i, pitches[0], 0xffff, LCD_CFG_GRA_PITCH);
95
96         return i;
97 }
98
99 int armada_drm_plane_prepare_fb(struct drm_plane *plane,
100         struct drm_plane_state *state)
101 {
102         DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
103                 plane->base.id, plane->name,
104                 state->fb ? state->fb->base.id : 0);
105
106         /*
107          * Take a reference on the new framebuffer - we want to
108          * hold on to it while the hardware is displaying it.
109          */
110         if (state->fb)
111                 drm_framebuffer_get(state->fb);
112         return 0;
113 }
114
115 void armada_drm_plane_cleanup_fb(struct drm_plane *plane,
116         struct drm_plane_state *old_state)
117 {
118         DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
119                 plane->base.id, plane->name,
120                 old_state->fb ? old_state->fb->base.id : 0);
121
122         if (old_state->fb)
123                 drm_framebuffer_put(old_state->fb);
124 }
125
126 int armada_drm_plane_atomic_check(struct drm_plane *plane,
127         struct drm_plane_state *state)
128 {
129         if (state->fb && !WARN_ON(!state->crtc)) {
130                 struct drm_crtc *crtc = state->crtc;
131                 struct drm_crtc_state *crtc_state;
132
133                 if (state->state)
134                         crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
135                 else
136                         crtc_state = crtc->state;
137                 return drm_atomic_helper_check_plane_state(state, crtc_state,
138                                                            0, INT_MAX,
139                                                            true, false);
140         } else {
141                 state->visible = false;
142         }
143         return 0;
144 }
145
146 static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
147         struct drm_plane_state *old_state)
148 {
149         struct drm_plane_state *state = plane->state;
150         struct armada_crtc *dcrtc;
151         struct armada_regs *regs;
152         u32 cfg, cfg_mask, val;
153         unsigned int idx;
154
155         DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
156
157         if (!state->fb || WARN_ON(!state->crtc))
158                 return;
159
160         DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n",
161                 plane->base.id, plane->name,
162                 state->crtc->base.id, state->crtc->name,
163                 state->fb->base.id,
164                 old_state->visible, state->visible);
165
166         dcrtc = drm_to_armada_crtc(state->crtc);
167         regs = dcrtc->regs + dcrtc->regs_idx;
168
169         idx = 0;
170         if (!old_state->visible && state->visible) {
171                 val = CFG_PDWN64x66;
172                 if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
173                         val |= CFG_PDWN256x24;
174                 armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1);
175         }
176         val = armada_rect_hw_fp(&state->src);
177         if (armada_rect_hw_fp(&old_state->src) != val)
178                 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN);
179         val = armada_rect_yx(&state->dst);
180         if (armada_rect_yx(&old_state->dst) != val)
181                 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN);
182         val = armada_rect_hw(&state->dst);
183         if (armada_rect_hw(&old_state->dst) != val)
184                 armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN);
185         if (old_state->src.x1 != state->src.x1 ||
186             old_state->src.y1 != state->src.y1 ||
187             old_state->fb != state->fb ||
188             state->crtc->state->mode_changed) {
189                 idx += armada_drm_crtc_calc_fb(state, regs + idx,
190                                                dcrtc->interlaced);
191         }
192         if (old_state->fb != state->fb ||
193             state->crtc->state->mode_changed) {
194                 cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) |
195                       CFG_GRA_MOD(drm_fb_to_armada_fb(state->fb)->mod);
196                 if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
197                         cfg |= CFG_PALETTE_ENA;
198                 if (state->visible)
199                         cfg |= CFG_GRA_ENA;
200                 if (dcrtc->interlaced)
201                         cfg |= CFG_GRA_FTOGGLE;
202                 cfg_mask = CFG_GRAFORMAT |
203                            CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
204                                        CFG_SWAPYU | CFG_YUV2RGB) |
205                            CFG_PALETTE_ENA | CFG_GRA_FTOGGLE |
206                            CFG_GRA_ENA;
207         } else if (old_state->visible != state->visible) {
208                 cfg = state->visible ? CFG_GRA_ENA : 0;
209                 cfg_mask = CFG_GRA_ENA;
210         } else {
211                 cfg = cfg_mask = 0;
212         }
213         if (drm_rect_width(&old_state->src) != drm_rect_width(&state->src) ||
214             drm_rect_width(&old_state->dst) != drm_rect_width(&state->dst)) {
215                 cfg_mask |= CFG_GRA_HSMOOTH;
216                 if (drm_rect_width(&state->src) >> 16 !=
217                     drm_rect_width(&state->dst))
218                         cfg |= CFG_GRA_HSMOOTH;
219         }
220
221         if (cfg_mask)
222                 armada_reg_queue_mod(regs, idx, cfg, cfg_mask,
223                                      LCD_SPU_DMA_CTRL0);
224
225         dcrtc->regs_idx += idx;
226 }
227
228 static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane,
229         struct drm_plane_state *old_state)
230 {
231         struct armada_crtc *dcrtc;
232         struct armada_regs *regs;
233         unsigned int idx = 0;
234
235         DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
236
237         if (!old_state->crtc)
238                 return;
239
240         DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n",
241                 plane->base.id, plane->name,
242                 old_state->crtc->base.id, old_state->crtc->name,
243                 old_state->fb->base.id);
244
245         dcrtc = drm_to_armada_crtc(old_state->crtc);
246         regs = dcrtc->regs + dcrtc->regs_idx;
247
248         /* Disable plane and power down most RAMs and FIFOs */
249         armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0);
250         armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 |
251                              CFG_PDWN256x8 | CFG_PDWN32x32 | CFG_PDWN64x66,
252                              0, LCD_SPU_SRAM_PARA1);
253
254         dcrtc->regs_idx += idx;
255 }
256
257 static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = {
258         .prepare_fb     = armada_drm_plane_prepare_fb,
259         .cleanup_fb     = armada_drm_plane_cleanup_fb,
260         .atomic_check   = armada_drm_plane_atomic_check,
261         .atomic_update  = armada_drm_primary_plane_atomic_update,
262         .atomic_disable = armada_drm_primary_plane_atomic_disable,
263 };
264
265 static const struct drm_plane_funcs armada_primary_plane_funcs = {
266         .update_plane   = drm_atomic_helper_update_plane,
267         .disable_plane  = drm_atomic_helper_disable_plane,
268         .destroy        = drm_primary_helper_destroy,
269         .reset          = drm_atomic_helper_plane_reset,
270         .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
271         .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
272 };
273
274 int armada_drm_primary_plane_init(struct drm_device *drm,
275         struct drm_plane *primary)
276 {
277         int ret;
278
279         drm_plane_helper_add(primary, &armada_primary_plane_helper_funcs);
280
281         ret = drm_universal_plane_init(drm, primary, 0,
282                                        &armada_primary_plane_funcs,
283                                        armada_primary_formats,
284                                        ARRAY_SIZE(armada_primary_formats),
285                                        NULL,
286                                        DRM_PLANE_TYPE_PRIMARY, NULL);
287
288         return ret;
289 }