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