blob: 9d1eec1dc720ce36c1601155226fd8223f90b3c5 [file] [log] [blame]
Russell Kingd40af7b2018-07-30 11:52:34 +01001/*
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
21static 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
38void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb,
39 int x, int y)
40{
41 const struct drm_format_info *format = fb->format;
42 unsigned int num_planes = format->num_planes;
43 u32 addr = drm_fb_obj(fb)->dev_addr;
44 int i;
45
46 if (num_planes > 3)
47 num_planes = 3;
48
49 addrs[0] = addr + fb->offsets[0] + y * fb->pitches[0] +
50 x * format->cpp[0];
51
52 y /= format->vsub;
53 x /= format->hsub;
54
55 for (i = 1; i < num_planes; i++)
56 addrs[i] = addr + fb->offsets[i] + y * fb->pitches[i] +
57 x * format->cpp[i];
58 for (; i < 3; i++)
59 addrs[i] = 0;
60}
61
62static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
63 int x, int y, struct armada_regs *regs, bool interlaced)
64{
65 unsigned pitch = fb->pitches[0];
66 u32 addrs[3], addr_odd, addr_even;
67 unsigned i = 0;
68
69 DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n",
70 pitch, x, y, fb->format->cpp[0] * 8);
71
72 armada_drm_plane_calc_addrs(addrs, fb, x, y);
73
74 addr_odd = addr_even = addrs[0];
75
76 if (interlaced) {
77 addr_even += pitch;
78 pitch *= 2;
79 }
80
81 /* write offset, base, and pitch */
82 armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0);
83 armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1);
84 armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH);
85
86 return i;
87}
88
89int armada_drm_plane_prepare_fb(struct drm_plane *plane,
90 struct drm_plane_state *state)
91{
92 DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
93 plane->base.id, plane->name,
94 state->fb ? state->fb->base.id : 0);
95
96 /*
97 * Take a reference on the new framebuffer - we want to
98 * hold on to it while the hardware is displaying it.
99 */
100 if (state->fb)
101 drm_framebuffer_get(state->fb);
102 return 0;
103}
104
105void armada_drm_plane_cleanup_fb(struct drm_plane *plane,
106 struct drm_plane_state *old_state)
107{
108 DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
109 plane->base.id, plane->name,
110 old_state->fb ? old_state->fb->base.id : 0);
111
112 if (old_state->fb)
113 drm_framebuffer_put(old_state->fb);
114}
115
116int armada_drm_plane_atomic_check(struct drm_plane *plane,
117 struct drm_plane_state *state)
118{
119 if (state->fb && !WARN_ON(!state->crtc)) {
120 struct drm_crtc *crtc = state->crtc;
121 struct drm_crtc_state *crtc_state;
122
123 if (state->state)
124 crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
125 else
126 crtc_state = crtc->state;
127 return drm_atomic_helper_check_plane_state(state, crtc_state,
128 0, INT_MAX,
129 true, false);
130 } else {
131 state->visible = false;
132 }
133 return 0;
134}
135
136static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
137 struct drm_plane_state *old_state)
138{
139 struct drm_plane_state *state = plane->state;
140 struct armada_crtc *dcrtc;
141 struct armada_regs *regs;
142 u32 cfg, cfg_mask, val;
143 unsigned int idx;
144
145 DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
146
147 if (!state->fb || WARN_ON(!state->crtc))
148 return;
149
150 DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n",
151 plane->base.id, plane->name,
152 state->crtc->base.id, state->crtc->name,
153 state->fb->base.id,
154 old_state->visible, state->visible);
155
156 dcrtc = drm_to_armada_crtc(state->crtc);
157 regs = dcrtc->regs + dcrtc->regs_idx;
158
159 idx = 0;
160 if (!old_state->visible && state->visible) {
161 val = CFG_PDWN64x66;
162 if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
163 val |= CFG_PDWN256x24;
164 armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1);
165 }
166 val = armada_rect_hw_fp(&state->src);
167 if (armada_rect_hw_fp(&old_state->src) != val)
168 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN);
169 val = armada_rect_yx(&state->dst);
170 if (armada_rect_yx(&old_state->dst) != val)
171 armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN);
172 val = armada_rect_hw(&state->dst);
173 if (armada_rect_hw(&old_state->dst) != val)
174 armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN);
175 if (old_state->src.x1 != state->src.x1 ||
176 old_state->src.y1 != state->src.y1 ||
177 old_state->fb != state->fb) {
178 idx += armada_drm_crtc_calc_fb(state->fb,
179 state->src.x1 >> 16,
180 state->src.y1 >> 16,
181 regs + idx,
182 dcrtc->interlaced);
183 }
184 if (old_state->fb != state->fb) {
185 cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) |
186 CFG_GRA_MOD(drm_fb_to_armada_fb(state->fb)->mod);
187 if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
188 cfg |= CFG_PALETTE_ENA;
189 if (state->visible)
190 cfg |= CFG_GRA_ENA;
191 if (dcrtc->interlaced)
192 cfg |= CFG_GRA_FTOGGLE;
193 cfg_mask = CFG_GRAFORMAT |
194 CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
195 CFG_SWAPYU | CFG_YUV2RGB) |
196 CFG_PALETTE_ENA | CFG_GRA_FTOGGLE |
197 CFG_GRA_ENA;
198 } else if (old_state->visible != state->visible) {
199 cfg = state->visible ? CFG_GRA_ENA : 0;
200 cfg_mask = CFG_GRA_ENA;
201 } else {
202 cfg = cfg_mask = 0;
203 }
204 if (drm_rect_width(&old_state->src) != drm_rect_width(&state->src) ||
205 drm_rect_width(&old_state->dst) != drm_rect_width(&state->dst)) {
206 cfg_mask |= CFG_GRA_HSMOOTH;
207 if (drm_rect_width(&state->src) >> 16 !=
208 drm_rect_width(&state->dst))
209 cfg |= CFG_GRA_HSMOOTH;
210 }
211
212 if (cfg_mask)
213 armada_reg_queue_mod(regs, idx, cfg, cfg_mask,
214 LCD_SPU_DMA_CTRL0);
215
216 dcrtc->regs_idx += idx;
217}
218
219static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane,
220 struct drm_plane_state *old_state)
221{
222 struct armada_crtc *dcrtc;
223 struct armada_regs *regs;
224 unsigned int idx = 0;
225
226 DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
227
228 if (!old_state->crtc)
229 return;
230
231 DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n",
232 plane->base.id, plane->name,
233 old_state->crtc->base.id, old_state->crtc->name,
234 old_state->fb->base.id);
235
236 dcrtc = drm_to_armada_crtc(old_state->crtc);
237 regs = dcrtc->regs + dcrtc->regs_idx;
238
239 /* Disable plane and power down most RAMs and FIFOs */
240 armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0);
241 armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 |
242 CFG_PDWN256x8 | CFG_PDWN32x32 | CFG_PDWN64x66,
243 0, LCD_SPU_SRAM_PARA1);
244
245 dcrtc->regs_idx += idx;
246}
247
248static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = {
249 .prepare_fb = armada_drm_plane_prepare_fb,
250 .cleanup_fb = armada_drm_plane_cleanup_fb,
251 .atomic_check = armada_drm_plane_atomic_check,
252 .atomic_update = armada_drm_primary_plane_atomic_update,
253 .atomic_disable = armada_drm_primary_plane_atomic_disable,
254};
255
256static const struct drm_plane_funcs armada_primary_plane_funcs = {
257 .update_plane = drm_plane_helper_update,
258 .disable_plane = drm_plane_helper_disable,
259 .destroy = drm_primary_helper_destroy,
260 .reset = drm_atomic_helper_plane_reset,
261 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
262 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
263};
264
265int armada_drm_plane_init(struct armada_plane *plane)
266{
267 unsigned int i;
268
269 for (i = 0; i < ARRAY_SIZE(plane->works); i++)
270 plane->works[i].plane = &plane->base;
271
272 init_waitqueue_head(&plane->frame_wait);
273
274 return 0;
275}
276
277int armada_drm_primary_plane_init(struct drm_device *drm,
278 struct armada_plane *primary)
279{
280 int ret;
281
282 ret = armada_drm_plane_init(primary);
283 if (ret)
284 return ret;
285
286 drm_plane_helper_add(&primary->base,
287 &armada_primary_plane_helper_funcs);
288
289 ret = drm_universal_plane_init(drm, &primary->base, 0,
290 &armada_primary_plane_funcs,
291 armada_primary_formats,
292 ARRAY_SIZE(armada_primary_formats),
293 NULL,
294 DRM_PLANE_TYPE_PRIMARY, NULL);
295
296 return ret;
297}