aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/armada/armada_overlay.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/armada/armada_overlay.c')
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c121
1 files changed, 75 insertions, 46 deletions
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index c5b06fdb459c..e939faba7fcc 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -7,6 +7,7 @@
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 */ 8 */
9#include <drm/drmP.h> 9#include <drm/drmP.h>
10#include <drm/drm_plane_helper.h>
10#include "armada_crtc.h" 11#include "armada_crtc.h"
11#include "armada_drm.h" 12#include "armada_drm.h"
12#include "armada_fb.h" 13#include "armada_fb.h"
@@ -85,16 +86,8 @@ static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
85 86
86 if (fb) 87 if (fb)
87 armada_drm_queue_unref_work(dcrtc->crtc.dev, fb); 88 armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
88}
89 89
90static unsigned armada_limit(int start, unsigned size, unsigned max) 90 wake_up(&dplane->vbl.wait);
91{
92 int end = start + size;
93 if (end < 0)
94 return 0;
95 if (start < 0)
96 start = 0;
97 return (unsigned)end > max ? max - start : end - start;
98} 91}
99 92
100static int 93static int
@@ -105,26 +98,39 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
105{ 98{
106 struct armada_plane *dplane = drm_to_armada_plane(plane); 99 struct armada_plane *dplane = drm_to_armada_plane(plane);
107 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); 100 struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
101 struct drm_rect src = {
102 .x1 = src_x,
103 .y1 = src_y,
104 .x2 = src_x + src_w,
105 .y2 = src_y + src_h,
106 };
107 struct drm_rect dest = {
108 .x1 = crtc_x,
109 .y1 = crtc_y,
110 .x2 = crtc_x + crtc_w,
111 .y2 = crtc_y + crtc_h,
112 };
113 const struct drm_rect clip = {
114 .x2 = crtc->mode.hdisplay,
115 .y2 = crtc->mode.vdisplay,
116 };
108 uint32_t val, ctrl0; 117 uint32_t val, ctrl0;
109 unsigned idx = 0; 118 unsigned idx = 0;
119 bool visible;
110 int ret; 120 int ret;
111 121
112 crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay); 122 ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
113 crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay); 123 0, INT_MAX, true, false, &visible);
124 if (ret)
125 return ret;
126
114 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) | 127 ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
115 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) | 128 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
116 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA; 129 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
117 130
118 /* Does the position/size result in nothing to display? */ 131 /* Does the position/size result in nothing to display? */
119 if (crtc_w == 0 || crtc_h == 0) { 132 if (!visible)
120 ctrl0 &= ~CFG_DMA_ENA; 133 ctrl0 &= ~CFG_DMA_ENA;
121 }
122
123 /*
124 * FIXME: if the starting point is off screen, we need to
125 * adjust src_x, src_y, src_w, src_h appropriately, and
126 * according to the scale.
127 */
128 134
129 if (!dcrtc->plane) { 135 if (!dcrtc->plane) {
130 dcrtc->plane = plane; 136 dcrtc->plane = plane;
@@ -134,15 +140,19 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
134 /* FIXME: overlay on an interlaced display */ 140 /* FIXME: overlay on an interlaced display */
135 /* Just updating the position/size? */ 141 /* Just updating the position/size? */
136 if (plane->fb == fb && dplane->ctrl0 == ctrl0) { 142 if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
137 val = (src_h & 0xffff0000) | src_w >> 16; 143 val = (drm_rect_height(&src) & 0xffff0000) |
144 drm_rect_width(&src) >> 16;
138 dplane->src_hw = val; 145 dplane->src_hw = val;
139 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN); 146 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
140 val = crtc_h << 16 | crtc_w; 147
148 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
141 dplane->dst_hw = val; 149 dplane->dst_hw = val;
142 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN); 150 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
143 val = crtc_y << 16 | crtc_x; 151
152 val = dest.y1 << 16 | dest.x1;
144 dplane->dst_yx = val; 153 dplane->dst_yx = val;
145 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN); 154 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
155
146 return 0; 156 return 0;
147 } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) { 157 } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
148 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ 158 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
@@ -150,15 +160,14 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
150 dcrtc->base + LCD_SPU_SRAM_PARA1); 160 dcrtc->base + LCD_SPU_SRAM_PARA1);
151 } 161 }
152 162
153 ret = wait_event_timeout(dplane->vbl.wait, 163 wait_event_timeout(dplane->vbl.wait,
154 list_empty(&dplane->vbl.update.node), 164 list_empty(&dplane->vbl.update.node),
155 HZ/25); 165 HZ/25);
156 if (ret < 0)
157 return ret;
158 166
159 if (plane->fb != fb) { 167 if (plane->fb != fb) {
160 struct armada_gem_object *obj = drm_fb_obj(fb); 168 struct armada_gem_object *obj = drm_fb_obj(fb);
161 uint32_t sy, su, sv; 169 uint32_t addr[3], pixel_format;
170 int i, num_planes, hsub;
162 171
163 /* 172 /*
164 * Take a reference on the new framebuffer - we want to 173 * Take a reference on the new framebuffer - we want to
@@ -178,26 +187,39 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
178 older_fb); 187 older_fb);
179 } 188 }
180 189
181 src_y >>= 16; 190 src_y = src.y1 >> 16;
182 src_x >>= 16; 191 src_x = src.x1 >> 16;
183 sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
184 src_x * fb->bits_per_pixel / 8;
185 su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
186 src_x;
187 sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
188 src_x;
189 192
190 armada_reg_queue_set(dplane->vbl.regs, idx, sy, 193 pixel_format = fb->pixel_format;
194 hsub = drm_format_horz_chroma_subsampling(pixel_format);
195 num_planes = drm_format_num_planes(pixel_format);
196
197 /*
198 * Annoyingly, shifting a YUYV-format image by one pixel
199 * causes the U/V planes to toggle. Toggle the UV swap.
200 * (Unfortunately, this causes momentary colour flickering.)
201 */
202 if (src_x & (hsub - 1) && num_planes == 1)
203 ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);
204
205 for (i = 0; i < num_planes; i++)
206 addr[i] = obj->dev_addr + fb->offsets[i] +
207 src_y * fb->pitches[i] +
208 src_x * drm_format_plane_cpp(pixel_format, i);
209 for (; i < ARRAY_SIZE(addr); i++)
210 addr[i] = 0;
211
212 armada_reg_queue_set(dplane->vbl.regs, idx, addr[0],
191 LCD_SPU_DMA_START_ADDR_Y0); 213 LCD_SPU_DMA_START_ADDR_Y0);
192 armada_reg_queue_set(dplane->vbl.regs, idx, su, 214 armada_reg_queue_set(dplane->vbl.regs, idx, addr[1],
193 LCD_SPU_DMA_START_ADDR_U0); 215 LCD_SPU_DMA_START_ADDR_U0);
194 armada_reg_queue_set(dplane->vbl.regs, idx, sv, 216 armada_reg_queue_set(dplane->vbl.regs, idx, addr[2],
195 LCD_SPU_DMA_START_ADDR_V0); 217 LCD_SPU_DMA_START_ADDR_V0);
196 armada_reg_queue_set(dplane->vbl.regs, idx, sy, 218 armada_reg_queue_set(dplane->vbl.regs, idx, addr[0],
197 LCD_SPU_DMA_START_ADDR_Y1); 219 LCD_SPU_DMA_START_ADDR_Y1);
198 armada_reg_queue_set(dplane->vbl.regs, idx, su, 220 armada_reg_queue_set(dplane->vbl.regs, idx, addr[1],
199 LCD_SPU_DMA_START_ADDR_U1); 221 LCD_SPU_DMA_START_ADDR_U1);
200 armada_reg_queue_set(dplane->vbl.regs, idx, sv, 222 armada_reg_queue_set(dplane->vbl.regs, idx, addr[2],
201 LCD_SPU_DMA_START_ADDR_V1); 223 LCD_SPU_DMA_START_ADDR_V1);
202 224
203 val = fb->pitches[0] << 16 | fb->pitches[0]; 225 val = fb->pitches[0] << 16 | fb->pitches[0];
@@ -208,24 +230,27 @@ armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
208 LCD_SPU_DMA_PITCH_UV); 230 LCD_SPU_DMA_PITCH_UV);
209 } 231 }
210 232
211 val = (src_h & 0xffff0000) | src_w >> 16; 233 val = (drm_rect_height(&src) & 0xffff0000) | drm_rect_width(&src) >> 16;
212 if (dplane->src_hw != val) { 234 if (dplane->src_hw != val) {
213 dplane->src_hw = val; 235 dplane->src_hw = val;
214 armada_reg_queue_set(dplane->vbl.regs, idx, val, 236 armada_reg_queue_set(dplane->vbl.regs, idx, val,
215 LCD_SPU_DMA_HPXL_VLN); 237 LCD_SPU_DMA_HPXL_VLN);
216 } 238 }
217 val = crtc_h << 16 | crtc_w; 239
240 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
218 if (dplane->dst_hw != val) { 241 if (dplane->dst_hw != val) {
219 dplane->dst_hw = val; 242 dplane->dst_hw = val;
220 armada_reg_queue_set(dplane->vbl.regs, idx, val, 243 armada_reg_queue_set(dplane->vbl.regs, idx, val,
221 LCD_SPU_DZM_HPXL_VLN); 244 LCD_SPU_DZM_HPXL_VLN);
222 } 245 }
223 val = crtc_y << 16 | crtc_x; 246
247 val = dest.y1 << 16 | dest.x1;
224 if (dplane->dst_yx != val) { 248 if (dplane->dst_yx != val) {
225 dplane->dst_yx = val; 249 dplane->dst_yx = val;
226 armada_reg_queue_set(dplane->vbl.regs, idx, val, 250 armada_reg_queue_set(dplane->vbl.regs, idx, val,
227 LCD_SPU_DMA_OVSA_HPXL_VLN); 251 LCD_SPU_DMA_OVSA_HPXL_VLN);
228 } 252 }
253
229 if (dplane->ctrl0 != ctrl0) { 254 if (dplane->ctrl0 != ctrl0) {
230 dplane->ctrl0 = ctrl0; 255 dplane->ctrl0 = ctrl0;
231 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0, 256 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
@@ -279,7 +304,11 @@ static int armada_plane_disable(struct drm_plane *plane)
279 304
280static void armada_plane_destroy(struct drm_plane *plane) 305static void armada_plane_destroy(struct drm_plane *plane)
281{ 306{
282 kfree(plane); 307 struct armada_plane *dplane = drm_to_armada_plane(plane);
308
309 drm_plane_cleanup(plane);
310
311 kfree(dplane);
283} 312}
284 313
285static int armada_plane_set_property(struct drm_plane *plane, 314static int armada_plane_set_property(struct drm_plane *plane,