Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: add Atmel HLCDC Display Controller support

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

This display controller supports at least one primary plane and might
provide several overlays and an hardware cursor depending on the IP
version.

At the moment, this driver only implements an RGB connector to interface
with LCD panels, but support for other kind of external devices might be
added later.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Reviewed-by: Rob Clark <robdclark@gmail.com>
Tested-by: Anthony Harivel <anthony.harivel@emtrion.de>
Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

+3459
+2
drivers/gpu/drm/Kconfig
··· 183 183 184 184 source "drivers/gpu/drm/armada/Kconfig" 185 185 186 + source "drivers/gpu/drm/atmel-hlcdc/Kconfig" 187 + 186 188 source "drivers/gpu/drm/rcar-du/Kconfig" 187 189 188 190 source "drivers/gpu/drm/shmobile/Kconfig"
+1
drivers/gpu/drm/Makefile
··· 54 54 obj-$(CONFIG_DRM_UDL) += udl/ 55 55 obj-$(CONFIG_DRM_AST) += ast/ 56 56 obj-$(CONFIG_DRM_ARMADA) += armada/ 57 + obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ 57 58 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ 58 59 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ 59 60 obj-$(CONFIG_DRM_OMAP) += omapdrm/
+11
drivers/gpu/drm/atmel-hlcdc/Kconfig
··· 1 + config DRM_ATMEL_HLCDC 2 + tristate "DRM Support for ATMEL HLCDC Display Controller" 3 + depends on DRM && OF && COMMON_CLK && MFD_ATMEL_HLCDC 4 + select DRM_GEM_CMA_HELPER 5 + select DRM_KMS_HELPER 6 + select DRM_KMS_FB_HELPER 7 + select DRM_KMS_CMA_HELPER 8 + select DRM_PANEL 9 + help 10 + Choose this option if you have an ATMEL SoC with an HLCDC display 11 + controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
+7
drivers/gpu/drm/atmel-hlcdc/Makefile
··· 1 + atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \ 2 + atmel_hlcdc_dc.o \ 3 + atmel_hlcdc_layer.o \ 4 + atmel_hlcdc_output.o \ 5 + atmel_hlcdc_plane.o 6 + 7 + obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc-dc.o
+406
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
··· 1 + /* 2 + * Copyright (C) 2014 Traphandler 3 + * Copyright (C) 2014 Free Electrons 4 + * 5 + * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 6 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License version 2 as published by 10 + * the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but WITHOUT 13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 + * more details. 16 + * 17 + * You should have received a copy of the GNU General Public License along with 18 + * this program. If not, see <http://www.gnu.org/licenses/>. 19 + */ 20 + 21 + #include <linux/clk.h> 22 + #include <linux/pm.h> 23 + #include <linux/pm_runtime.h> 24 + 25 + #include <drm/drm_crtc.h> 26 + #include <drm/drm_crtc_helper.h> 27 + #include <drm/drmP.h> 28 + 29 + #include <video/videomode.h> 30 + 31 + #include "atmel_hlcdc_dc.h" 32 + 33 + /** 34 + * Atmel HLCDC CRTC structure 35 + * 36 + * @base: base DRM CRTC structure 37 + * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device 38 + * @event: pointer to the current page flip event 39 + * @id: CRTC id (returned by drm_crtc_index) 40 + * @dpms: DPMS mode 41 + */ 42 + struct atmel_hlcdc_crtc { 43 + struct drm_crtc base; 44 + struct atmel_hlcdc_dc *dc; 45 + struct drm_pending_vblank_event *event; 46 + int id; 47 + int dpms; 48 + }; 49 + 50 + static inline struct atmel_hlcdc_crtc * 51 + drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) 52 + { 53 + return container_of(crtc, struct atmel_hlcdc_crtc, base); 54 + } 55 + 56 + static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode) 57 + { 58 + struct drm_device *dev = c->dev; 59 + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 60 + struct regmap *regmap = crtc->dc->hlcdc->regmap; 61 + unsigned int status; 62 + 63 + if (mode != DRM_MODE_DPMS_ON) 64 + mode = DRM_MODE_DPMS_OFF; 65 + 66 + if (crtc->dpms == mode) 67 + return; 68 + 69 + pm_runtime_get_sync(dev->dev); 70 + 71 + if (mode != DRM_MODE_DPMS_ON) { 72 + regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); 73 + while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 74 + (status & ATMEL_HLCDC_DISP)) 75 + cpu_relax(); 76 + 77 + regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); 78 + while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 79 + (status & ATMEL_HLCDC_SYNC)) 80 + cpu_relax(); 81 + 82 + regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); 83 + while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 84 + (status & ATMEL_HLCDC_PIXEL_CLK)) 85 + cpu_relax(); 86 + 87 + clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 88 + 89 + pm_runtime_allow(dev->dev); 90 + } else { 91 + pm_runtime_forbid(dev->dev); 92 + 93 + clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 94 + 95 + regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); 96 + while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 97 + !(status & ATMEL_HLCDC_PIXEL_CLK)) 98 + cpu_relax(); 99 + 100 + 101 + regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); 102 + while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 103 + !(status & ATMEL_HLCDC_SYNC)) 104 + cpu_relax(); 105 + 106 + regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); 107 + while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 108 + !(status & ATMEL_HLCDC_DISP)) 109 + cpu_relax(); 110 + } 111 + 112 + pm_runtime_put_sync(dev->dev); 113 + 114 + crtc->dpms = mode; 115 + } 116 + 117 + static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c, 118 + struct drm_display_mode *mode, 119 + struct drm_display_mode *adj, 120 + int x, int y, 121 + struct drm_framebuffer *old_fb) 122 + { 123 + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 124 + struct regmap *regmap = crtc->dc->hlcdc->regmap; 125 + struct drm_plane *plane = c->primary; 126 + struct drm_framebuffer *fb; 127 + unsigned long mode_rate; 128 + struct videomode vm; 129 + unsigned long prate; 130 + unsigned int cfg; 131 + int div; 132 + 133 + if (atmel_hlcdc_dc_mode_valid(crtc->dc, adj) != MODE_OK) 134 + return -EINVAL; 135 + 136 + vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; 137 + vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; 138 + vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start; 139 + vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay; 140 + vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end; 141 + vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start; 142 + 143 + regmap_write(regmap, ATMEL_HLCDC_CFG(1), 144 + (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16)); 145 + 146 + regmap_write(regmap, ATMEL_HLCDC_CFG(2), 147 + (vm.vfront_porch - 1) | (vm.vback_porch << 16)); 148 + 149 + regmap_write(regmap, ATMEL_HLCDC_CFG(3), 150 + (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16)); 151 + 152 + regmap_write(regmap, ATMEL_HLCDC_CFG(4), 153 + (adj->crtc_hdisplay - 1) | 154 + ((adj->crtc_vdisplay - 1) << 16)); 155 + 156 + cfg = ATMEL_HLCDC_CLKPOL; 157 + 158 + prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); 159 + mode_rate = mode->crtc_clock * 1000; 160 + if ((prate / 2) < mode_rate) { 161 + prate *= 2; 162 + cfg |= ATMEL_HLCDC_CLKSEL; 163 + } 164 + 165 + div = DIV_ROUND_UP(prate, mode_rate); 166 + if (div < 2) 167 + div = 2; 168 + 169 + cfg |= ATMEL_HLCDC_CLKDIV(div); 170 + 171 + regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), 172 + ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK | 173 + ATMEL_HLCDC_CLKPOL, cfg); 174 + 175 + cfg = 0; 176 + 177 + if (mode->flags & DRM_MODE_FLAG_NVSYNC) 178 + cfg |= ATMEL_HLCDC_VSPOL; 179 + 180 + if (mode->flags & DRM_MODE_FLAG_NHSYNC) 181 + cfg |= ATMEL_HLCDC_HSPOL; 182 + 183 + regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), 184 + ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | 185 + ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | 186 + ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | 187 + ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | 188 + ATMEL_HLCDC_GUARDTIME_MASK, 189 + cfg); 190 + 191 + fb = plane->fb; 192 + plane->fb = old_fb; 193 + 194 + return atmel_hlcdc_plane_update_with_mode(plane, c, fb, 0, 0, 195 + adj->hdisplay, adj->vdisplay, 196 + x << 16, y << 16, 197 + adj->hdisplay << 16, 198 + adj->vdisplay << 16, 199 + adj); 200 + } 201 + 202 + int atmel_hlcdc_crtc_mode_set_base(struct drm_crtc *c, int x, int y, 203 + struct drm_framebuffer *old_fb) 204 + { 205 + struct drm_plane *plane = c->primary; 206 + struct drm_framebuffer *fb = plane->fb; 207 + struct drm_display_mode *mode = &c->hwmode; 208 + 209 + plane->fb = old_fb; 210 + 211 + return plane->funcs->update_plane(plane, c, fb, 212 + 0, 0, 213 + mode->hdisplay, 214 + mode->vdisplay, 215 + x << 16, y << 16, 216 + mode->hdisplay << 16, 217 + mode->vdisplay << 16); 218 + } 219 + 220 + static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc) 221 + { 222 + atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 223 + } 224 + 225 + static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc) 226 + { 227 + atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); 228 + } 229 + 230 + static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc, 231 + const struct drm_display_mode *mode, 232 + struct drm_display_mode *adjusted_mode) 233 + { 234 + return true; 235 + } 236 + 237 + static void atmel_hlcdc_crtc_disable(struct drm_crtc *crtc) 238 + { 239 + struct drm_plane *plane; 240 + 241 + atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 242 + crtc->primary->funcs->disable_plane(crtc->primary); 243 + 244 + drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) { 245 + if (plane->crtc != crtc) 246 + continue; 247 + 248 + plane->funcs->disable_plane(crtc->primary); 249 + plane->crtc = NULL; 250 + } 251 + } 252 + 253 + static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { 254 + .mode_fixup = atmel_hlcdc_crtc_mode_fixup, 255 + .dpms = atmel_hlcdc_crtc_dpms, 256 + .mode_set = atmel_hlcdc_crtc_mode_set, 257 + .mode_set_base = atmel_hlcdc_crtc_mode_set_base, 258 + .prepare = atmel_hlcdc_crtc_prepare, 259 + .commit = atmel_hlcdc_crtc_commit, 260 + .disable = atmel_hlcdc_crtc_disable, 261 + }; 262 + 263 + static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) 264 + { 265 + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 266 + 267 + drm_crtc_cleanup(c); 268 + kfree(crtc); 269 + } 270 + 271 + void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c, 272 + struct drm_file *file) 273 + { 274 + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 275 + struct drm_pending_vblank_event *event; 276 + struct drm_device *dev = c->dev; 277 + unsigned long flags; 278 + 279 + spin_lock_irqsave(&dev->event_lock, flags); 280 + event = crtc->event; 281 + if (event && event->base.file_priv == file) { 282 + event->base.destroy(&event->base); 283 + drm_vblank_put(dev, crtc->id); 284 + crtc->event = NULL; 285 + } 286 + spin_unlock_irqrestore(&dev->event_lock, flags); 287 + } 288 + 289 + static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc) 290 + { 291 + struct drm_device *dev = crtc->base.dev; 292 + unsigned long flags; 293 + 294 + spin_lock_irqsave(&dev->event_lock, flags); 295 + if (crtc->event) { 296 + drm_send_vblank_event(dev, crtc->id, crtc->event); 297 + drm_vblank_put(dev, crtc->id); 298 + crtc->event = NULL; 299 + } 300 + spin_unlock_irqrestore(&dev->event_lock, flags); 301 + } 302 + 303 + void atmel_hlcdc_crtc_irq(struct drm_crtc *c) 304 + { 305 + drm_handle_vblank(c->dev, 0); 306 + atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); 307 + } 308 + 309 + static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c, 310 + struct drm_framebuffer *fb, 311 + struct drm_pending_vblank_event *event, 312 + uint32_t page_flip_flags) 313 + { 314 + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 315 + struct atmel_hlcdc_plane_update_req req; 316 + struct drm_plane *plane = c->primary; 317 + struct drm_device *dev = c->dev; 318 + unsigned long flags; 319 + int ret = 0; 320 + 321 + spin_lock_irqsave(&dev->event_lock, flags); 322 + if (crtc->event) 323 + ret = -EBUSY; 324 + spin_unlock_irqrestore(&dev->event_lock, flags); 325 + 326 + if (ret) 327 + return ret; 328 + 329 + memset(&req, 0, sizeof(req)); 330 + req.crtc_x = 0; 331 + req.crtc_y = 0; 332 + req.crtc_h = c->mode.crtc_vdisplay; 333 + req.crtc_w = c->mode.crtc_hdisplay; 334 + req.src_x = c->x << 16; 335 + req.src_y = c->y << 16; 336 + req.src_w = req.crtc_w << 16; 337 + req.src_h = req.crtc_h << 16; 338 + req.fb = fb; 339 + 340 + ret = atmel_hlcdc_plane_prepare_update_req(plane, &req, &c->hwmode); 341 + if (ret) 342 + return ret; 343 + 344 + if (event) { 345 + drm_vblank_get(c->dev, crtc->id); 346 + spin_lock_irqsave(&dev->event_lock, flags); 347 + crtc->event = event; 348 + spin_unlock_irqrestore(&dev->event_lock, flags); 349 + } 350 + 351 + ret = atmel_hlcdc_plane_apply_update_req(plane, &req); 352 + if (ret) 353 + crtc->event = NULL; 354 + else 355 + plane->fb = fb; 356 + 357 + return ret; 358 + } 359 + 360 + static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { 361 + .page_flip = atmel_hlcdc_crtc_page_flip, 362 + .set_config = drm_crtc_helper_set_config, 363 + .destroy = atmel_hlcdc_crtc_destroy, 364 + }; 365 + 366 + int atmel_hlcdc_crtc_create(struct drm_device *dev) 367 + { 368 + struct atmel_hlcdc_dc *dc = dev->dev_private; 369 + struct atmel_hlcdc_planes *planes = dc->planes; 370 + struct atmel_hlcdc_crtc *crtc; 371 + int ret; 372 + int i; 373 + 374 + crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 375 + if (!crtc) 376 + return -ENOMEM; 377 + 378 + crtc->dpms = DRM_MODE_DPMS_OFF; 379 + crtc->dc = dc; 380 + 381 + ret = drm_crtc_init_with_planes(dev, &crtc->base, 382 + &planes->primary->base, 383 + planes->cursor ? &planes->cursor->base : NULL, 384 + &atmel_hlcdc_crtc_funcs); 385 + if (ret < 0) 386 + goto fail; 387 + 388 + crtc->id = drm_crtc_index(&crtc->base); 389 + 390 + if (planes->cursor) 391 + planes->cursor->base.possible_crtcs = 1 << crtc->id; 392 + 393 + for (i = 0; i < planes->noverlays; i++) 394 + planes->overlays[i]->base.possible_crtcs = 1 << crtc->id; 395 + 396 + drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); 397 + 398 + dc->crtc = &crtc->base; 399 + 400 + return 0; 401 + 402 + fail: 403 + atmel_hlcdc_crtc_destroy(&crtc->base); 404 + return ret; 405 + } 406 +
+579
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
··· 1 + /* 2 + * Copyright (C) 2014 Traphandler 3 + * Copyright (C) 2014 Free Electrons 4 + * Copyright (C) 2014 Atmel 5 + * 6 + * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify it 10 + * under the terms of the GNU General Public License version 2 as published by 11 + * the Free Software Foundation. 12 + * 13 + * This program is distributed in the hope that it will be useful, but WITHOUT 14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 + * more details. 17 + * 18 + * You should have received a copy of the GNU General Public License along with 19 + * this program. If not, see <http://www.gnu.org/licenses/>. 20 + */ 21 + 22 + #include <linux/clk.h> 23 + #include <linux/irq.h> 24 + #include <linux/irqchip.h> 25 + #include <linux/module.h> 26 + #include <linux/pm_runtime.h> 27 + 28 + #include "atmel_hlcdc_dc.h" 29 + 30 + #define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8 31 + 32 + static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { 33 + { 34 + .name = "base", 35 + .formats = &atmel_hlcdc_plane_rgb_formats, 36 + .regs_offset = 0x40, 37 + .id = 0, 38 + .type = ATMEL_HLCDC_BASE_LAYER, 39 + .nconfigs = 7, 40 + .layout = { 41 + .xstride = { 2 }, 42 + .default_color = 3, 43 + .general_config = 4, 44 + .disc_pos = 5, 45 + .disc_size = 6, 46 + }, 47 + }, 48 + { 49 + .name = "overlay1", 50 + .formats = &atmel_hlcdc_plane_rgb_formats, 51 + .regs_offset = 0x140, 52 + .id = 1, 53 + .type = ATMEL_HLCDC_OVERLAY_LAYER, 54 + .nconfigs = 10, 55 + .layout = { 56 + .pos = 2, 57 + .size = 3, 58 + .xstride = { 4 }, 59 + .pstride = { 5 }, 60 + .default_color = 6, 61 + .chroma_key = 7, 62 + .chroma_key_mask = 8, 63 + .general_config = 9, 64 + }, 65 + }, 66 + { 67 + .name = "overlay2", 68 + .formats = &atmel_hlcdc_plane_rgb_formats, 69 + .regs_offset = 0x240, 70 + .id = 2, 71 + .type = ATMEL_HLCDC_OVERLAY_LAYER, 72 + .nconfigs = 10, 73 + .layout = { 74 + .pos = 2, 75 + .size = 3, 76 + .xstride = { 4 }, 77 + .pstride = { 5 }, 78 + .default_color = 6, 79 + .chroma_key = 7, 80 + .chroma_key_mask = 8, 81 + .general_config = 9, 82 + }, 83 + }, 84 + { 85 + .name = "high-end-overlay", 86 + .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, 87 + .regs_offset = 0x340, 88 + .id = 3, 89 + .type = ATMEL_HLCDC_OVERLAY_LAYER, 90 + .nconfigs = 42, 91 + .layout = { 92 + .pos = 2, 93 + .size = 3, 94 + .memsize = 4, 95 + .xstride = { 5, 7 }, 96 + .pstride = { 6, 8 }, 97 + .default_color = 9, 98 + .chroma_key = 10, 99 + .chroma_key_mask = 11, 100 + .general_config = 12, 101 + .csc = 14, 102 + }, 103 + }, 104 + { 105 + .name = "cursor", 106 + .formats = &atmel_hlcdc_plane_rgb_formats, 107 + .regs_offset = 0x440, 108 + .id = 4, 109 + .type = ATMEL_HLCDC_CURSOR_LAYER, 110 + .nconfigs = 10, 111 + .max_width = 128, 112 + .max_height = 128, 113 + .layout = { 114 + .pos = 2, 115 + .size = 3, 116 + .xstride = { 4 }, 117 + .pstride = { 5 }, 118 + .default_color = 6, 119 + .chroma_key = 7, 120 + .chroma_key_mask = 8, 121 + .general_config = 9, 122 + }, 123 + }, 124 + }; 125 + 126 + static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = { 127 + .min_width = 0, 128 + .min_height = 0, 129 + .max_width = 2048, 130 + .max_height = 2048, 131 + .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), 132 + .layers = atmel_hlcdc_sama5d3_layers, 133 + }; 134 + 135 + static const struct of_device_id atmel_hlcdc_of_match[] = { 136 + { 137 + .compatible = "atmel,sama5d3-hlcdc", 138 + .data = &atmel_hlcdc_dc_sama5d3, 139 + }, 140 + { /* sentinel */ }, 141 + }; 142 + 143 + int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 144 + struct drm_display_mode *mode) 145 + { 146 + int vfront_porch = mode->vsync_start - mode->vdisplay; 147 + int vback_porch = mode->vtotal - mode->vsync_end; 148 + int vsync_len = mode->vsync_end - mode->vsync_start; 149 + int hfront_porch = mode->hsync_start - mode->hdisplay; 150 + int hback_porch = mode->htotal - mode->hsync_end; 151 + int hsync_len = mode->hsync_end - mode->hsync_start; 152 + 153 + if (hsync_len > 0x40 || hsync_len < 1) 154 + return MODE_HSYNC; 155 + 156 + if (vsync_len > 0x40 || vsync_len < 1) 157 + return MODE_VSYNC; 158 + 159 + if (hfront_porch > 0x200 || hfront_porch < 1 || 160 + hback_porch > 0x200 || hback_porch < 1 || 161 + mode->hdisplay < 1) 162 + return MODE_H_ILLEGAL; 163 + 164 + if (vfront_porch > 0x40 || vfront_porch < 1 || 165 + vback_porch > 0x40 || vback_porch < 0 || 166 + mode->vdisplay < 1) 167 + return MODE_V_ILLEGAL; 168 + 169 + return MODE_OK; 170 + } 171 + 172 + static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 173 + { 174 + struct drm_device *dev = data; 175 + struct atmel_hlcdc_dc *dc = dev->dev_private; 176 + unsigned long status; 177 + unsigned int imr, isr; 178 + int i; 179 + 180 + regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 181 + regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 182 + status = imr & isr; 183 + if (!status) 184 + return IRQ_NONE; 185 + 186 + if (status & ATMEL_HLCDC_SOF) 187 + atmel_hlcdc_crtc_irq(dc->crtc); 188 + 189 + for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 190 + struct atmel_hlcdc_layer *layer = dc->layers[i]; 191 + 192 + if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer) 193 + continue; 194 + 195 + atmel_hlcdc_layer_irq(layer); 196 + } 197 + 198 + return IRQ_HANDLED; 199 + } 200 + 201 + static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 202 + struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) 203 + { 204 + return drm_fb_cma_create(dev, file_priv, mode_cmd); 205 + } 206 + 207 + static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) 208 + { 209 + struct atmel_hlcdc_dc *dc = dev->dev_private; 210 + 211 + if (dc->fbdev) { 212 + drm_fbdev_cma_hotplug_event(dc->fbdev); 213 + } else { 214 + dc->fbdev = drm_fbdev_cma_init(dev, 24, 215 + dev->mode_config.num_crtc, 216 + dev->mode_config.num_connector); 217 + if (IS_ERR(dc->fbdev)) 218 + dc->fbdev = NULL; 219 + } 220 + } 221 + 222 + static const struct drm_mode_config_funcs mode_config_funcs = { 223 + .fb_create = atmel_hlcdc_fb_create, 224 + .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 225 + }; 226 + 227 + static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 228 + { 229 + struct atmel_hlcdc_dc *dc = dev->dev_private; 230 + struct atmel_hlcdc_planes *planes; 231 + int ret; 232 + int i; 233 + 234 + drm_mode_config_init(dev); 235 + 236 + ret = atmel_hlcdc_create_outputs(dev); 237 + if (ret) { 238 + dev_err(dev->dev, "failed to create panel: %d\n", ret); 239 + return ret; 240 + } 241 + 242 + planes = atmel_hlcdc_create_planes(dev); 243 + if (IS_ERR(planes)) { 244 + dev_err(dev->dev, "failed to create planes\n"); 245 + return PTR_ERR(planes); 246 + } 247 + 248 + dc->planes = planes; 249 + 250 + dc->layers[planes->primary->layer.desc->id] = 251 + &planes->primary->layer; 252 + 253 + if (planes->cursor) 254 + dc->layers[planes->cursor->layer.desc->id] = 255 + &planes->cursor->layer; 256 + 257 + for (i = 0; i < planes->noverlays; i++) 258 + dc->layers[planes->overlays[i]->layer.desc->id] = 259 + &planes->overlays[i]->layer; 260 + 261 + ret = atmel_hlcdc_crtc_create(dev); 262 + if (ret) { 263 + dev_err(dev->dev, "failed to create crtc\n"); 264 + return ret; 265 + } 266 + 267 + dev->mode_config.min_width = dc->desc->min_width; 268 + dev->mode_config.min_height = dc->desc->min_height; 269 + dev->mode_config.max_width = dc->desc->max_width; 270 + dev->mode_config.max_height = dc->desc->max_height; 271 + dev->mode_config.funcs = &mode_config_funcs; 272 + 273 + return 0; 274 + } 275 + 276 + static int atmel_hlcdc_dc_load(struct drm_device *dev) 277 + { 278 + struct platform_device *pdev = to_platform_device(dev->dev); 279 + const struct of_device_id *match; 280 + struct atmel_hlcdc_dc *dc; 281 + int ret; 282 + 283 + match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 284 + if (!match) { 285 + dev_err(&pdev->dev, "invalid compatible string\n"); 286 + return -ENODEV; 287 + } 288 + 289 + if (!match->data) { 290 + dev_err(&pdev->dev, "invalid hlcdc description\n"); 291 + return -EINVAL; 292 + } 293 + 294 + dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 295 + if (!dc) 296 + return -ENOMEM; 297 + 298 + dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 299 + if (!dc->wq) 300 + return -ENOMEM; 301 + 302 + dc->desc = match->data; 303 + dc->hlcdc = dev_get_drvdata(dev->dev->parent); 304 + dev->dev_private = dc; 305 + 306 + ret = clk_prepare_enable(dc->hlcdc->periph_clk); 307 + if (ret) { 308 + dev_err(dev->dev, "failed to enable periph_clk\n"); 309 + goto err_destroy_wq; 310 + } 311 + 312 + pm_runtime_enable(dev->dev); 313 + 314 + pm_runtime_put_sync(dev->dev); 315 + 316 + ret = atmel_hlcdc_dc_modeset_init(dev); 317 + if (ret < 0) { 318 + dev_err(dev->dev, "failed to initialize mode setting\n"); 319 + goto err_periph_clk_disable; 320 + } 321 + 322 + ret = drm_vblank_init(dev, 1); 323 + if (ret < 0) { 324 + dev_err(dev->dev, "failed to initialize vblank\n"); 325 + goto err_periph_clk_disable; 326 + } 327 + 328 + pm_runtime_get_sync(dev->dev); 329 + ret = drm_irq_install(dev, dc->hlcdc->irq); 330 + pm_runtime_put_sync(dev->dev); 331 + if (ret < 0) { 332 + dev_err(dev->dev, "failed to install IRQ handler\n"); 333 + goto err_periph_clk_disable; 334 + } 335 + 336 + platform_set_drvdata(pdev, dev); 337 + 338 + drm_kms_helper_poll_init(dev); 339 + 340 + /* force connectors detection */ 341 + drm_helper_hpd_irq_event(dev); 342 + 343 + return 0; 344 + 345 + err_periph_clk_disable: 346 + pm_runtime_disable(dev->dev); 347 + clk_disable_unprepare(dc->hlcdc->periph_clk); 348 + 349 + err_destroy_wq: 350 + destroy_workqueue(dc->wq); 351 + 352 + return ret; 353 + } 354 + 355 + static void atmel_hlcdc_dc_unload(struct drm_device *dev) 356 + { 357 + struct atmel_hlcdc_dc *dc = dev->dev_private; 358 + 359 + if (dc->fbdev) 360 + drm_fbdev_cma_fini(dc->fbdev); 361 + flush_workqueue(dc->wq); 362 + drm_kms_helper_poll_fini(dev); 363 + drm_mode_config_cleanup(dev); 364 + drm_vblank_cleanup(dev); 365 + 366 + pm_runtime_get_sync(dev->dev); 367 + drm_irq_uninstall(dev); 368 + pm_runtime_put_sync(dev->dev); 369 + 370 + dev->dev_private = NULL; 371 + 372 + pm_runtime_disable(dev->dev); 373 + clk_disable_unprepare(dc->hlcdc->periph_clk); 374 + destroy_workqueue(dc->wq); 375 + } 376 + 377 + static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev) 378 + { 379 + struct drm_connector *connector, *failed; 380 + int ret; 381 + 382 + mutex_lock(&dev->mode_config.mutex); 383 + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 384 + ret = drm_connector_register(connector); 385 + if (ret) { 386 + failed = connector; 387 + goto err; 388 + } 389 + } 390 + mutex_unlock(&dev->mode_config.mutex); 391 + return 0; 392 + 393 + err: 394 + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 395 + if (failed == connector) 396 + break; 397 + 398 + drm_connector_unregister(connector); 399 + } 400 + mutex_unlock(&dev->mode_config.mutex); 401 + 402 + return ret; 403 + } 404 + 405 + static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) 406 + { 407 + mutex_lock(&dev->mode_config.mutex); 408 + drm_connector_unplug_all(dev); 409 + mutex_unlock(&dev->mode_config.mutex); 410 + } 411 + 412 + static void atmel_hlcdc_dc_preclose(struct drm_device *dev, 413 + struct drm_file *file) 414 + { 415 + struct drm_crtc *crtc; 416 + 417 + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 418 + atmel_hlcdc_crtc_cancel_page_flip(crtc, file); 419 + } 420 + 421 + static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 422 + { 423 + struct atmel_hlcdc_dc *dc = dev->dev_private; 424 + 425 + drm_fbdev_cma_restore_mode(dc->fbdev); 426 + } 427 + 428 + static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 429 + { 430 + struct atmel_hlcdc_dc *dc = dev->dev_private; 431 + unsigned int cfg = 0; 432 + int i; 433 + 434 + /* Enable interrupts on activated layers */ 435 + for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 436 + if (dc->layers[i]) 437 + cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 438 + } 439 + 440 + regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 441 + 442 + return 0; 443 + } 444 + 445 + static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 446 + { 447 + struct atmel_hlcdc_dc *dc = dev->dev_private; 448 + unsigned int isr; 449 + 450 + regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 451 + regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 452 + } 453 + 454 + static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc) 455 + { 456 + struct atmel_hlcdc_dc *dc = dev->dev_private; 457 + 458 + /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 459 + regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 460 + 461 + return 0; 462 + } 463 + 464 + static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc) 465 + { 466 + struct atmel_hlcdc_dc *dc = dev->dev_private; 467 + 468 + regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 469 + } 470 + 471 + static const struct file_operations fops = { 472 + .owner = THIS_MODULE, 473 + .open = drm_open, 474 + .release = drm_release, 475 + .unlocked_ioctl = drm_ioctl, 476 + #ifdef CONFIG_COMPAT 477 + .compat_ioctl = drm_compat_ioctl, 478 + #endif 479 + .poll = drm_poll, 480 + .read = drm_read, 481 + .llseek = no_llseek, 482 + .mmap = drm_gem_cma_mmap, 483 + }; 484 + 485 + static struct drm_driver atmel_hlcdc_dc_driver = { 486 + .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, 487 + .preclose = atmel_hlcdc_dc_preclose, 488 + .lastclose = atmel_hlcdc_dc_lastclose, 489 + .irq_handler = atmel_hlcdc_dc_irq_handler, 490 + .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 491 + .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 492 + .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 493 + .get_vblank_counter = drm_vblank_count, 494 + .enable_vblank = atmel_hlcdc_dc_enable_vblank, 495 + .disable_vblank = atmel_hlcdc_dc_disable_vblank, 496 + .gem_free_object = drm_gem_cma_free_object, 497 + .gem_vm_ops = &drm_gem_cma_vm_ops, 498 + .dumb_create = drm_gem_cma_dumb_create, 499 + .dumb_map_offset = drm_gem_cma_dumb_map_offset, 500 + .dumb_destroy = drm_gem_dumb_destroy, 501 + .fops = &fops, 502 + .name = "atmel-hlcdc", 503 + .desc = "Atmel HLCD Controller DRM", 504 + .date = "20141504", 505 + .major = 1, 506 + .minor = 0, 507 + }; 508 + 509 + static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 510 + { 511 + struct drm_device *ddev; 512 + int ret; 513 + 514 + ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 515 + if (!ddev) 516 + return -ENOMEM; 517 + 518 + ret = drm_dev_set_unique(ddev, dev_name(ddev->dev)); 519 + if (ret) 520 + goto err_unref; 521 + 522 + ret = atmel_hlcdc_dc_load(ddev); 523 + if (ret) 524 + goto err_unref; 525 + 526 + ret = drm_dev_register(ddev, 0); 527 + if (ret) 528 + goto err_unload; 529 + 530 + ret = atmel_hlcdc_dc_connector_plug_all(ddev); 531 + if (ret) 532 + goto err_unregister; 533 + 534 + return 0; 535 + 536 + err_unregister: 537 + drm_dev_unregister(ddev); 538 + 539 + err_unload: 540 + atmel_hlcdc_dc_unload(ddev); 541 + 542 + err_unref: 543 + drm_dev_unref(ddev); 544 + 545 + return ret; 546 + } 547 + 548 + static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 549 + { 550 + struct drm_device *ddev = platform_get_drvdata(pdev); 551 + 552 + atmel_hlcdc_dc_connector_unplug_all(ddev); 553 + drm_dev_unregister(ddev); 554 + atmel_hlcdc_dc_unload(ddev); 555 + drm_dev_unref(ddev); 556 + 557 + return 0; 558 + } 559 + 560 + static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 561 + { .compatible = "atmel,hlcdc-display-controller" }, 562 + { }, 563 + }; 564 + 565 + static struct platform_driver atmel_hlcdc_dc_platform_driver = { 566 + .probe = atmel_hlcdc_dc_drm_probe, 567 + .remove = atmel_hlcdc_dc_drm_remove, 568 + .driver = { 569 + .name = "atmel-hlcdc-display-controller", 570 + .of_match_table = atmel_hlcdc_dc_of_match, 571 + }, 572 + }; 573 + module_platform_driver(atmel_hlcdc_dc_platform_driver); 574 + 575 + MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 576 + MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 577 + MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 578 + MODULE_LICENSE("GPL"); 579 + MODULE_ALIAS("platform:atmel-hlcdc-dc");
+213
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
··· 1 + /* 2 + * Copyright (C) 2014 Traphandler 3 + * Copyright (C) 2014 Free Electrons 4 + * Copyright (C) 2014 Atmel 5 + * 6 + * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify it 10 + * under the terms of the GNU General Public License version 2 as published by 11 + * the Free Software Foundation. 12 + * 13 + * This program is distributed in the hope that it will be useful, but WITHOUT 14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 + * more details. 17 + * 18 + * You should have received a copy of the GNU General Public License along with 19 + * this program. If not, see <http://www.gnu.org/licenses/>. 20 + */ 21 + 22 + #ifndef DRM_ATMEL_HLCDC_H 23 + #define DRM_ATMEL_HLCDC_H 24 + 25 + #include <linux/clk.h> 26 + #include <linux/irqdomain.h> 27 + #include <linux/pwm.h> 28 + 29 + #include <drm/drm_crtc.h> 30 + #include <drm/drm_crtc_helper.h> 31 + #include <drm/drm_fb_cma_helper.h> 32 + #include <drm/drm_gem_cma_helper.h> 33 + #include <drm/drm_panel.h> 34 + #include <drm/drmP.h> 35 + 36 + #include "atmel_hlcdc_layer.h" 37 + 38 + #define ATMEL_HLCDC_MAX_LAYERS 5 39 + 40 + /** 41 + * Atmel HLCDC Display Controller description structure. 42 + * 43 + * This structure describe the HLCDC IP capabilities and depends on the 44 + * HLCDC IP version (or Atmel SoC family). 45 + * 46 + * @min_width: minimum width supported by the Display Controller 47 + * @min_height: minimum height supported by the Display Controller 48 + * @max_width: maximum width supported by the Display Controller 49 + * @max_height: maximum height supported by the Display Controller 50 + * @layers: a layer description table describing available layers 51 + * @nlayers: layer description table size 52 + */ 53 + struct atmel_hlcdc_dc_desc { 54 + int min_width; 55 + int min_height; 56 + int max_width; 57 + int max_height; 58 + const struct atmel_hlcdc_layer_desc *layers; 59 + int nlayers; 60 + }; 61 + 62 + /** 63 + * Atmel HLCDC Plane properties. 64 + * 65 + * This structure stores plane property definitions. 66 + * 67 + * @alpha: alpha blending (or transparency) property 68 + * @rotation: rotation property 69 + */ 70 + struct atmel_hlcdc_plane_properties { 71 + struct drm_property *alpha; 72 + struct drm_property *rotation; 73 + }; 74 + 75 + /** 76 + * Atmel HLCDC Plane. 77 + * 78 + * @base: base DRM plane structure 79 + * @layer: HLCDC layer structure 80 + * @properties: pointer to the property definitions structure 81 + * @rotation: current rotation status 82 + */ 83 + struct atmel_hlcdc_plane { 84 + struct drm_plane base; 85 + struct atmel_hlcdc_layer layer; 86 + struct atmel_hlcdc_plane_properties *properties; 87 + unsigned int rotation; 88 + }; 89 + 90 + static inline struct atmel_hlcdc_plane * 91 + drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p) 92 + { 93 + return container_of(p, struct atmel_hlcdc_plane, base); 94 + } 95 + 96 + static inline struct atmel_hlcdc_plane * 97 + atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l) 98 + { 99 + return container_of(l, struct atmel_hlcdc_plane, layer); 100 + } 101 + 102 + /** 103 + * Atmel HLCDC Plane update request structure. 104 + * 105 + * @crtc_x: x position of the plane relative to the CRTC 106 + * @crtc_y: y position of the plane relative to the CRTC 107 + * @crtc_w: visible width of the plane 108 + * @crtc_h: visible height of the plane 109 + * @src_x: x buffer position 110 + * @src_y: y buffer position 111 + * @src_w: buffer width 112 + * @src_h: buffer height 113 + * @fb: framebuffer object object 114 + * @bpp: bytes per pixel deduced from pixel_format 115 + * @offsets: offsets to apply to the GEM buffers 116 + * @xstride: value to add to the pixel pointer between each line 117 + * @pstride: value to add to the pixel pointer between each pixel 118 + * @nplanes: number of planes (deduced from pixel_format) 119 + */ 120 + struct atmel_hlcdc_plane_update_req { 121 + int crtc_x; 122 + int crtc_y; 123 + unsigned int crtc_w; 124 + unsigned int crtc_h; 125 + uint32_t src_x; 126 + uint32_t src_y; 127 + uint32_t src_w; 128 + uint32_t src_h; 129 + struct drm_framebuffer *fb; 130 + 131 + /* These fields are private and should not be touched */ 132 + int bpp[ATMEL_HLCDC_MAX_PLANES]; 133 + unsigned int offsets[ATMEL_HLCDC_MAX_PLANES]; 134 + int xstride[ATMEL_HLCDC_MAX_PLANES]; 135 + int pstride[ATMEL_HLCDC_MAX_PLANES]; 136 + int nplanes; 137 + }; 138 + 139 + /** 140 + * Atmel HLCDC Planes. 141 + * 142 + * This structure stores the instantiated HLCDC Planes and can be accessed by 143 + * the HLCDC Display Controller or the HLCDC CRTC. 144 + * 145 + * @primary: primary plane 146 + * @cursor: hardware cursor plane 147 + * @overlays: overlay plane table 148 + * @noverlays: number of overlay planes 149 + */ 150 + struct atmel_hlcdc_planes { 151 + struct atmel_hlcdc_plane *primary; 152 + struct atmel_hlcdc_plane *cursor; 153 + struct atmel_hlcdc_plane **overlays; 154 + int noverlays; 155 + }; 156 + 157 + /** 158 + * Atmel HLCDC Display Controller. 159 + * 160 + * @desc: HLCDC Display Controller description 161 + * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device 162 + * @fbdev: framebuffer device attached to the Display Controller 163 + * @crtc: CRTC provided by the display controller 164 + * @planes: instantiated planes 165 + * @layers: active HLCDC layer 166 + * @wq: display controller workqueue 167 + */ 168 + struct atmel_hlcdc_dc { 169 + const struct atmel_hlcdc_dc_desc *desc; 170 + struct atmel_hlcdc *hlcdc; 171 + struct drm_fbdev_cma *fbdev; 172 + struct drm_crtc *crtc; 173 + struct atmel_hlcdc_planes *planes; 174 + struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS]; 175 + struct workqueue_struct *wq; 176 + }; 177 + 178 + extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; 179 + extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats; 180 + 181 + int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 182 + struct drm_display_mode *mode); 183 + 184 + struct atmel_hlcdc_planes * 185 + atmel_hlcdc_create_planes(struct drm_device *dev); 186 + 187 + int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p, 188 + struct atmel_hlcdc_plane_update_req *req, 189 + const struct drm_display_mode *mode); 190 + 191 + int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p, 192 + struct atmel_hlcdc_plane_update_req *req); 193 + 194 + int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p, 195 + struct drm_crtc *crtc, 196 + struct drm_framebuffer *fb, 197 + int crtc_x, int crtc_y, 198 + unsigned int crtc_w, 199 + unsigned int crtc_h, 200 + uint32_t src_x, uint32_t src_y, 201 + uint32_t src_w, uint32_t src_h, 202 + const struct drm_display_mode *mode); 203 + 204 + void atmel_hlcdc_crtc_irq(struct drm_crtc *c); 205 + 206 + void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, 207 + struct drm_file *file); 208 + 209 + int atmel_hlcdc_crtc_create(struct drm_device *dev); 210 + 211 + int atmel_hlcdc_create_outputs(struct drm_device *dev); 212 + 213 + #endif /* DRM_ATMEL_HLCDC_H */
+667
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
··· 1 + /* 2 + * Copyright (C) 2014 Free Electrons 3 + * Copyright (C) 2014 Atmel 4 + * 5 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as published by 9 + * the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, but WITHOUT 12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 + * more details. 15 + * 16 + * You should have received a copy of the GNU General Public License along with 17 + * this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include <linux/dma-mapping.h> 21 + #include <linux/interrupt.h> 22 + 23 + #include "atmel_hlcdc_dc.h" 24 + 25 + static void 26 + atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val) 27 + { 28 + struct atmel_hlcdc_layer_fb_flip *flip = val; 29 + 30 + if (flip->fb) 31 + drm_framebuffer_unreference(flip->fb); 32 + kfree(flip); 33 + } 34 + 35 + static void 36 + atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip) 37 + { 38 + if (flip->fb) 39 + drm_framebuffer_unreference(flip->fb); 40 + kfree(flip->task); 41 + kfree(flip); 42 + } 43 + 44 + static void 45 + atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer, 46 + struct atmel_hlcdc_layer_fb_flip *flip) 47 + { 48 + int i; 49 + 50 + if (!flip) 51 + return; 52 + 53 + for (i = 0; i < layer->max_planes; i++) { 54 + if (!flip->dscrs[i]) 55 + break; 56 + 57 + flip->dscrs[i]->status = 0; 58 + flip->dscrs[i] = NULL; 59 + } 60 + 61 + drm_flip_work_queue_task(&layer->gc, flip->task); 62 + drm_flip_work_commit(&layer->gc, layer->wq); 63 + } 64 + 65 + static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer, 66 + int id) 67 + { 68 + struct atmel_hlcdc_layer_update *upd = &layer->update; 69 + struct atmel_hlcdc_layer_update_slot *slot; 70 + 71 + if (id < 0 || id > 1) 72 + return; 73 + 74 + slot = &upd->slots[id]; 75 + bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs); 76 + memset(slot->configs, 0, 77 + sizeof(*slot->configs) * layer->desc->nconfigs); 78 + 79 + if (slot->fb_flip) { 80 + atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip); 81 + slot->fb_flip = NULL; 82 + } 83 + } 84 + 85 + static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer) 86 + { 87 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 88 + const struct atmel_hlcdc_layer_desc *desc = layer->desc; 89 + struct atmel_hlcdc_layer_update *upd = &layer->update; 90 + struct regmap *regmap = layer->hlcdc->regmap; 91 + struct atmel_hlcdc_layer_update_slot *slot; 92 + struct atmel_hlcdc_layer_fb_flip *fb_flip; 93 + struct atmel_hlcdc_dma_channel_dscr *dscr; 94 + unsigned int cfg; 95 + u32 action = 0; 96 + int i = 0; 97 + 98 + if (upd->pending < 0 || upd->pending > 1) 99 + return; 100 + 101 + slot = &upd->slots[upd->pending]; 102 + 103 + for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) { 104 + regmap_write(regmap, 105 + desc->regs_offset + 106 + ATMEL_HLCDC_LAYER_CFG(layer, cfg), 107 + slot->configs[cfg]); 108 + action |= ATMEL_HLCDC_LAYER_UPDATE; 109 + } 110 + 111 + fb_flip = slot->fb_flip; 112 + 113 + if (!fb_flip->fb) 114 + goto apply; 115 + 116 + if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) { 117 + for (i = 0; i < fb_flip->ngems; i++) { 118 + dscr = fb_flip->dscrs[i]; 119 + dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH | 120 + ATMEL_HLCDC_LAYER_DMA_IRQ | 121 + ATMEL_HLCDC_LAYER_ADD_IRQ | 122 + ATMEL_HLCDC_LAYER_DONE_IRQ; 123 + 124 + regmap_write(regmap, 125 + desc->regs_offset + 126 + ATMEL_HLCDC_LAYER_PLANE_ADDR(i), 127 + dscr->addr); 128 + regmap_write(regmap, 129 + desc->regs_offset + 130 + ATMEL_HLCDC_LAYER_PLANE_CTRL(i), 131 + dscr->ctrl); 132 + regmap_write(regmap, 133 + desc->regs_offset + 134 + ATMEL_HLCDC_LAYER_PLANE_NEXT(i), 135 + dscr->next); 136 + } 137 + 138 + action |= ATMEL_HLCDC_LAYER_DMA_CHAN; 139 + dma->status = ATMEL_HLCDC_LAYER_ENABLED; 140 + } else { 141 + for (i = 0; i < fb_flip->ngems; i++) { 142 + dscr = fb_flip->dscrs[i]; 143 + dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH | 144 + ATMEL_HLCDC_LAYER_DMA_IRQ | 145 + ATMEL_HLCDC_LAYER_DSCR_IRQ | 146 + ATMEL_HLCDC_LAYER_DONE_IRQ; 147 + 148 + regmap_write(regmap, 149 + desc->regs_offset + 150 + ATMEL_HLCDC_LAYER_PLANE_HEAD(i), 151 + dscr->next); 152 + } 153 + 154 + action |= ATMEL_HLCDC_LAYER_A2Q; 155 + } 156 + 157 + /* Release unneeded descriptors */ 158 + for (i = fb_flip->ngems; i < layer->max_planes; i++) { 159 + fb_flip->dscrs[i]->status = 0; 160 + fb_flip->dscrs[i] = NULL; 161 + } 162 + 163 + dma->queue = fb_flip; 164 + slot->fb_flip = NULL; 165 + 166 + apply: 167 + if (action) 168 + regmap_write(regmap, 169 + desc->regs_offset + ATMEL_HLCDC_LAYER_CHER, 170 + action); 171 + 172 + atmel_hlcdc_layer_update_reset(layer, upd->pending); 173 + 174 + upd->pending = -1; 175 + } 176 + 177 + void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer) 178 + { 179 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 180 + const struct atmel_hlcdc_layer_desc *desc = layer->desc; 181 + struct regmap *regmap = layer->hlcdc->regmap; 182 + struct atmel_hlcdc_layer_fb_flip *flip; 183 + unsigned long flags; 184 + unsigned int isr, imr; 185 + unsigned int status; 186 + unsigned int plane_status; 187 + u32 flip_status; 188 + 189 + int i; 190 + 191 + regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr); 192 + regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr); 193 + status = imr & isr; 194 + if (!status) 195 + return; 196 + 197 + spin_lock_irqsave(&layer->lock, flags); 198 + 199 + flip = dma->queue ? dma->queue : dma->cur; 200 + 201 + if (!flip) { 202 + spin_unlock_irqrestore(&layer->lock, flags); 203 + return; 204 + } 205 + 206 + /* 207 + * Set LOADED and DONE flags: they'll be cleared if at least one 208 + * memory plane is not LOADED or DONE. 209 + */ 210 + flip_status = ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED | 211 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE; 212 + for (i = 0; i < flip->ngems; i++) { 213 + plane_status = (status >> (8 * i)); 214 + 215 + if (plane_status & 216 + (ATMEL_HLCDC_LAYER_ADD_IRQ | 217 + ATMEL_HLCDC_LAYER_DSCR_IRQ) & 218 + ~flip->dscrs[i]->ctrl) { 219 + flip->dscrs[i]->status |= 220 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED; 221 + flip->dscrs[i]->ctrl |= 222 + ATMEL_HLCDC_LAYER_ADD_IRQ | 223 + ATMEL_HLCDC_LAYER_DSCR_IRQ; 224 + } 225 + 226 + if (plane_status & 227 + ATMEL_HLCDC_LAYER_DONE_IRQ & 228 + ~flip->dscrs[i]->ctrl) { 229 + flip->dscrs[i]->status |= 230 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE; 231 + flip->dscrs[i]->ctrl |= 232 + ATMEL_HLCDC_LAYER_DONE_IRQ; 233 + } 234 + 235 + if (plane_status & ATMEL_HLCDC_LAYER_OVR_IRQ) 236 + flip->dscrs[i]->status |= 237 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN; 238 + 239 + /* 240 + * Clear LOADED and DONE flags if the memory plane is either 241 + * not LOADED or not DONE. 242 + */ 243 + if (!(flip->dscrs[i]->status & 244 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED)) 245 + flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED; 246 + 247 + if (!(flip->dscrs[i]->status & 248 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE)) 249 + flip_status &= ~ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE; 250 + 251 + /* 252 + * An overrun on one memory plane impact the whole framebuffer 253 + * transfer, hence we set the OVERRUN flag as soon as there's 254 + * one memory plane reporting such an overrun. 255 + */ 256 + flip_status |= flip->dscrs[i]->status & 257 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN; 258 + } 259 + 260 + /* Get changed bits */ 261 + flip_status ^= flip->status; 262 + flip->status |= flip_status; 263 + 264 + if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) { 265 + atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur); 266 + dma->cur = dma->queue; 267 + dma->queue = NULL; 268 + } 269 + 270 + if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) { 271 + atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur); 272 + dma->cur = NULL; 273 + } 274 + 275 + if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN) { 276 + regmap_write(regmap, 277 + desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, 278 + ATMEL_HLCDC_LAYER_RST); 279 + if (dma->queue) 280 + atmel_hlcdc_layer_fb_flip_release_queue(layer, 281 + dma->queue); 282 + 283 + if (dma->cur) 284 + atmel_hlcdc_layer_fb_flip_release_queue(layer, 285 + dma->cur); 286 + 287 + dma->cur = NULL; 288 + dma->queue = NULL; 289 + } 290 + 291 + if (!dma->queue) { 292 + atmel_hlcdc_layer_update_apply(layer); 293 + 294 + if (!dma->cur) 295 + dma->status = ATMEL_HLCDC_LAYER_DISABLED; 296 + } 297 + 298 + spin_unlock_irqrestore(&layer->lock, flags); 299 + } 300 + 301 + int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer) 302 + { 303 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 304 + struct atmel_hlcdc_layer_update *upd = &layer->update; 305 + struct regmap *regmap = layer->hlcdc->regmap; 306 + const struct atmel_hlcdc_layer_desc *desc = layer->desc; 307 + unsigned long flags; 308 + unsigned int isr; 309 + 310 + spin_lock_irqsave(&layer->lock, flags); 311 + 312 + /* Disable the layer */ 313 + regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, 314 + ATMEL_HLCDC_LAYER_RST); 315 + 316 + /* Clear all pending interrupts */ 317 + regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr); 318 + 319 + /* Discard current and queued framebuffer transfers. */ 320 + if (dma->cur) { 321 + atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur); 322 + dma->cur = NULL; 323 + } 324 + 325 + if (dma->queue) { 326 + atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->queue); 327 + dma->queue = NULL; 328 + } 329 + 330 + /* 331 + * Then discard the pending update request (if any) to prevent 332 + * DMA irq handler from restarting the DMA channel after it has 333 + * been disabled. 334 + */ 335 + if (upd->pending >= 0) { 336 + atmel_hlcdc_layer_update_reset(layer, upd->pending); 337 + upd->pending = -1; 338 + } 339 + 340 + dma->status = ATMEL_HLCDC_LAYER_DISABLED; 341 + 342 + spin_unlock_irqrestore(&layer->lock, flags); 343 + 344 + return 0; 345 + } 346 + 347 + int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer) 348 + { 349 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 350 + struct atmel_hlcdc_layer_update *upd = &layer->update; 351 + struct regmap *regmap = layer->hlcdc->regmap; 352 + struct atmel_hlcdc_layer_fb_flip *fb_flip; 353 + struct atmel_hlcdc_layer_update_slot *slot; 354 + unsigned long flags; 355 + int i, j = 0; 356 + 357 + fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL); 358 + if (!fb_flip) 359 + return -ENOMEM; 360 + 361 + fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL); 362 + if (!fb_flip->task) { 363 + kfree(fb_flip); 364 + return -ENOMEM; 365 + } 366 + 367 + spin_lock_irqsave(&layer->lock, flags); 368 + 369 + upd->next = upd->pending ? 0 : 1; 370 + 371 + slot = &upd->slots[upd->next]; 372 + 373 + for (i = 0; i < layer->max_planes * 4; i++) { 374 + if (!dma->dscrs[i].status) { 375 + fb_flip->dscrs[j++] = &dma->dscrs[i]; 376 + dma->dscrs[i].status = 377 + ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED; 378 + if (j == layer->max_planes) 379 + break; 380 + } 381 + } 382 + 383 + if (j < layer->max_planes) { 384 + for (i = 0; i < j; i++) 385 + fb_flip->dscrs[i]->status = 0; 386 + } 387 + 388 + if (j < layer->max_planes) { 389 + spin_unlock_irqrestore(&layer->lock, flags); 390 + atmel_hlcdc_layer_fb_flip_destroy(fb_flip); 391 + return -EBUSY; 392 + } 393 + 394 + slot->fb_flip = fb_flip; 395 + 396 + if (upd->pending >= 0) { 397 + memcpy(slot->configs, 398 + upd->slots[upd->pending].configs, 399 + layer->desc->nconfigs * sizeof(u32)); 400 + memcpy(slot->updated_configs, 401 + upd->slots[upd->pending].updated_configs, 402 + DIV_ROUND_UP(layer->desc->nconfigs, 403 + BITS_PER_BYTE * sizeof(unsigned long)) * 404 + sizeof(unsigned long)); 405 + slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb; 406 + if (upd->slots[upd->pending].fb_flip->fb) { 407 + slot->fb_flip->fb = 408 + upd->slots[upd->pending].fb_flip->fb; 409 + slot->fb_flip->ngems = 410 + upd->slots[upd->pending].fb_flip->ngems; 411 + drm_framebuffer_reference(slot->fb_flip->fb); 412 + } 413 + } else { 414 + regmap_bulk_read(regmap, 415 + layer->desc->regs_offset + 416 + ATMEL_HLCDC_LAYER_CFG(layer, 0), 417 + upd->slots[upd->next].configs, 418 + layer->desc->nconfigs); 419 + } 420 + 421 + spin_unlock_irqrestore(&layer->lock, flags); 422 + 423 + return 0; 424 + } 425 + 426 + void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer) 427 + { 428 + struct atmel_hlcdc_layer_update *upd = &layer->update; 429 + 430 + atmel_hlcdc_layer_update_reset(layer, upd->next); 431 + upd->next = -1; 432 + } 433 + 434 + void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer, 435 + struct drm_framebuffer *fb, 436 + unsigned int *offsets) 437 + { 438 + struct atmel_hlcdc_layer_update *upd = &layer->update; 439 + struct atmel_hlcdc_layer_fb_flip *fb_flip; 440 + struct atmel_hlcdc_layer_update_slot *slot; 441 + struct atmel_hlcdc_dma_channel_dscr *dscr; 442 + struct drm_framebuffer *old_fb; 443 + int nplanes = 0; 444 + int i; 445 + 446 + if (upd->next < 0 || upd->next > 1) 447 + return; 448 + 449 + if (fb) 450 + nplanes = drm_format_num_planes(fb->pixel_format); 451 + 452 + if (nplanes > layer->max_planes) 453 + return; 454 + 455 + slot = &upd->slots[upd->next]; 456 + 457 + fb_flip = slot->fb_flip; 458 + old_fb = slot->fb_flip->fb; 459 + 460 + for (i = 0; i < nplanes; i++) { 461 + struct drm_gem_cma_object *gem; 462 + 463 + dscr = slot->fb_flip->dscrs[i]; 464 + gem = drm_fb_cma_get_gem_obj(fb, i); 465 + dscr->addr = gem->paddr + offsets[i]; 466 + } 467 + 468 + fb_flip->ngems = nplanes; 469 + fb_flip->fb = fb; 470 + 471 + if (fb) 472 + drm_framebuffer_reference(fb); 473 + 474 + if (old_fb) 475 + drm_framebuffer_unreference(old_fb); 476 + } 477 + 478 + void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg, 479 + u32 mask, u32 val) 480 + { 481 + struct atmel_hlcdc_layer_update *upd = &layer->update; 482 + struct atmel_hlcdc_layer_update_slot *slot; 483 + 484 + if (upd->next < 0 || upd->next > 1) 485 + return; 486 + 487 + if (cfg >= layer->desc->nconfigs) 488 + return; 489 + 490 + slot = &upd->slots[upd->next]; 491 + slot->configs[cfg] &= ~mask; 492 + slot->configs[cfg] |= (val & mask); 493 + set_bit(cfg, slot->updated_configs); 494 + } 495 + 496 + void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer) 497 + { 498 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 499 + struct atmel_hlcdc_layer_update *upd = &layer->update; 500 + struct atmel_hlcdc_layer_update_slot *slot; 501 + unsigned long flags; 502 + 503 + if (upd->next < 0 || upd->next > 1) 504 + return; 505 + 506 + slot = &upd->slots[upd->next]; 507 + 508 + spin_lock_irqsave(&layer->lock, flags); 509 + 510 + /* 511 + * Release pending update request and replace it by the new one. 512 + */ 513 + if (upd->pending >= 0) 514 + atmel_hlcdc_layer_update_reset(layer, upd->pending); 515 + 516 + upd->pending = upd->next; 517 + upd->next = -1; 518 + 519 + if (!dma->queue) 520 + atmel_hlcdc_layer_update_apply(layer); 521 + 522 + spin_unlock_irqrestore(&layer->lock, flags); 523 + 524 + 525 + upd->next = -1; 526 + } 527 + 528 + static int atmel_hlcdc_layer_dma_init(struct drm_device *dev, 529 + struct atmel_hlcdc_layer *layer) 530 + { 531 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 532 + dma_addr_t dma_addr; 533 + int i; 534 + 535 + dma->dscrs = dma_alloc_coherent(dev->dev, 536 + layer->max_planes * 4 * 537 + sizeof(*dma->dscrs), 538 + &dma_addr, GFP_KERNEL); 539 + if (!dma->dscrs) 540 + return -ENOMEM; 541 + 542 + for (i = 0; i < layer->max_planes * 4; i++) { 543 + struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i]; 544 + 545 + dscr->next = dma_addr + (i * sizeof(*dscr)); 546 + } 547 + 548 + return 0; 549 + } 550 + 551 + static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev, 552 + struct atmel_hlcdc_layer *layer) 553 + { 554 + struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma; 555 + int i; 556 + 557 + for (i = 0; i < layer->max_planes * 4; i++) { 558 + struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i]; 559 + 560 + dscr->status = 0; 561 + } 562 + 563 + dma_free_coherent(dev->dev, layer->max_planes * 4 * 564 + sizeof(*dma->dscrs), dma->dscrs, 565 + dma->dscrs[0].next); 566 + } 567 + 568 + static int atmel_hlcdc_layer_update_init(struct drm_device *dev, 569 + struct atmel_hlcdc_layer *layer, 570 + const struct atmel_hlcdc_layer_desc *desc) 571 + { 572 + struct atmel_hlcdc_layer_update *upd = &layer->update; 573 + int updated_size; 574 + void *buffer; 575 + int i; 576 + 577 + updated_size = DIV_ROUND_UP(desc->nconfigs, 578 + BITS_PER_BYTE * 579 + sizeof(unsigned long)); 580 + 581 + buffer = devm_kzalloc(dev->dev, 582 + ((desc->nconfigs * sizeof(u32)) + 583 + (updated_size * sizeof(unsigned long))) * 2, 584 + GFP_KERNEL); 585 + if (!buffer) 586 + return -ENOMEM; 587 + 588 + for (i = 0; i < 2; i++) { 589 + upd->slots[i].updated_configs = buffer; 590 + buffer += updated_size * sizeof(unsigned long); 591 + upd->slots[i].configs = buffer; 592 + buffer += desc->nconfigs * sizeof(u32); 593 + } 594 + 595 + upd->pending = -1; 596 + upd->next = -1; 597 + 598 + return 0; 599 + } 600 + 601 + int atmel_hlcdc_layer_init(struct drm_device *dev, 602 + struct atmel_hlcdc_layer *layer, 603 + const struct atmel_hlcdc_layer_desc *desc) 604 + { 605 + struct atmel_hlcdc_dc *dc = dev->dev_private; 606 + struct regmap *regmap = dc->hlcdc->regmap; 607 + unsigned int tmp; 608 + int ret; 609 + int i; 610 + 611 + layer->hlcdc = dc->hlcdc; 612 + layer->wq = dc->wq; 613 + layer->desc = desc; 614 + 615 + regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, 616 + ATMEL_HLCDC_LAYER_RST); 617 + for (i = 0; i < desc->formats->nformats; i++) { 618 + int nplanes = drm_format_num_planes(desc->formats->formats[i]); 619 + 620 + if (nplanes > layer->max_planes) 621 + layer->max_planes = nplanes; 622 + } 623 + 624 + spin_lock_init(&layer->lock); 625 + drm_flip_work_init(&layer->gc, desc->name, 626 + atmel_hlcdc_layer_fb_flip_release); 627 + ret = atmel_hlcdc_layer_dma_init(dev, layer); 628 + if (ret) 629 + return ret; 630 + 631 + ret = atmel_hlcdc_layer_update_init(dev, layer, desc); 632 + if (ret) 633 + return ret; 634 + 635 + /* Flush Status Register */ 636 + regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR, 637 + 0xffffffff); 638 + regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, 639 + &tmp); 640 + 641 + tmp = 0; 642 + for (i = 0; i < layer->max_planes; i++) 643 + tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ | 644 + ATMEL_HLCDC_LAYER_DSCR_IRQ | 645 + ATMEL_HLCDC_LAYER_ADD_IRQ | 646 + ATMEL_HLCDC_LAYER_DONE_IRQ | 647 + ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i); 648 + 649 + regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp); 650 + 651 + return 0; 652 + } 653 + 654 + void atmel_hlcdc_layer_cleanup(struct drm_device *dev, 655 + struct atmel_hlcdc_layer *layer) 656 + { 657 + const struct atmel_hlcdc_layer_desc *desc = layer->desc; 658 + struct regmap *regmap = layer->hlcdc->regmap; 659 + 660 + regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR, 661 + 0xffffffff); 662 + regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR, 663 + ATMEL_HLCDC_LAYER_RST); 664 + 665 + atmel_hlcdc_layer_dma_cleanup(dev, layer); 666 + drm_flip_work_cleanup(&layer->gc); 667 + }
+398
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
··· 1 + /* 2 + * Copyright (C) 2014 Free Electrons 3 + * Copyright (C) 2014 Atmel 4 + * 5 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as published by 9 + * the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, but WITHOUT 12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 + * more details. 15 + * 16 + * You should have received a copy of the GNU General Public License along with 17 + * this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #ifndef DRM_ATMEL_HLCDC_LAYER_H 21 + #define DRM_ATMEL_HLCDC_LAYER_H 22 + 23 + #include <linux/mfd/atmel-hlcdc.h> 24 + 25 + #include <drm/drm_crtc.h> 26 + #include <drm/drm_flip_work.h> 27 + #include <drm/drmP.h> 28 + 29 + #define ATMEL_HLCDC_LAYER_CHER 0x0 30 + #define ATMEL_HLCDC_LAYER_CHDR 0x4 31 + #define ATMEL_HLCDC_LAYER_CHSR 0x8 32 + #define ATMEL_HLCDC_LAYER_DMA_CHAN BIT(0) 33 + #define ATMEL_HLCDC_LAYER_UPDATE BIT(1) 34 + #define ATMEL_HLCDC_LAYER_A2Q BIT(2) 35 + #define ATMEL_HLCDC_LAYER_RST BIT(8) 36 + 37 + #define ATMEL_HLCDC_LAYER_IER 0xc 38 + #define ATMEL_HLCDC_LAYER_IDR 0x10 39 + #define ATMEL_HLCDC_LAYER_IMR 0x14 40 + #define ATMEL_HLCDC_LAYER_ISR 0x18 41 + #define ATMEL_HLCDC_LAYER_DFETCH BIT(0) 42 + #define ATMEL_HLCDC_LAYER_LFETCH BIT(1) 43 + #define ATMEL_HLCDC_LAYER_DMA_IRQ BIT(2) 44 + #define ATMEL_HLCDC_LAYER_DSCR_IRQ BIT(3) 45 + #define ATMEL_HLCDC_LAYER_ADD_IRQ BIT(4) 46 + #define ATMEL_HLCDC_LAYER_DONE_IRQ BIT(5) 47 + #define ATMEL_HLCDC_LAYER_OVR_IRQ BIT(6) 48 + 49 + #define ATMEL_HLCDC_LAYER_PLANE_HEAD(n) (((n) * 0x10) + 0x1c) 50 + #define ATMEL_HLCDC_LAYER_PLANE_ADDR(n) (((n) * 0x10) + 0x20) 51 + #define ATMEL_HLCDC_LAYER_PLANE_CTRL(n) (((n) * 0x10) + 0x24) 52 + #define ATMEL_HLCDC_LAYER_PLANE_NEXT(n) (((n) * 0x10) + 0x28) 53 + #define ATMEL_HLCDC_LAYER_CFG(p, c) (((c) * 4) + ((p)->max_planes * 0x10) + 0x1c) 54 + 55 + #define ATMEL_HLCDC_LAYER_DMA_CFG_ID 0 56 + #define ATMEL_HLCDC_LAYER_DMA_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID) 57 + #define ATMEL_HLCDC_LAYER_DMA_SIF BIT(0) 58 + #define ATMEL_HLCDC_LAYER_DMA_BLEN_MASK GENMASK(5, 4) 59 + #define ATMEL_HLCDC_LAYER_DMA_BLEN_SINGLE (0 << 4) 60 + #define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR4 (1 << 4) 61 + #define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR8 (2 << 4) 62 + #define ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 (3 << 4) 63 + #define ATMEL_HLCDC_LAYER_DMA_DLBO BIT(8) 64 + #define ATMEL_HLCDC_LAYER_DMA_ROTDIS BIT(12) 65 + #define ATMEL_HLCDC_LAYER_DMA_LOCKDIS BIT(13) 66 + 67 + #define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID 1 68 + #define ATMEL_HLCDC_LAYER_FORMAT_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID) 69 + #define ATMEL_HLCDC_LAYER_RGB (0 << 0) 70 + #define ATMEL_HLCDC_LAYER_CLUT (1 << 0) 71 + #define ATMEL_HLCDC_LAYER_YUV (2 << 0) 72 + #define ATMEL_HLCDC_RGB_MODE(m) (((m) & 0xf) << 4) 73 + #define ATMEL_HLCDC_CLUT_MODE(m) (((m) & 0x3) << 8) 74 + #define ATMEL_HLCDC_YUV_MODE(m) (((m) & 0xf) << 12) 75 + #define ATMEL_HLCDC_YUV422ROT BIT(16) 76 + #define ATMEL_HLCDC_YUV422SWP BIT(17) 77 + #define ATMEL_HLCDC_DSCALEOPT BIT(20) 78 + 79 + #define ATMEL_HLCDC_XRGB4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0)) 80 + #define ATMEL_HLCDC_ARGB4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1)) 81 + #define ATMEL_HLCDC_RGBA4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2)) 82 + #define ATMEL_HLCDC_RGB565_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3)) 83 + #define ATMEL_HLCDC_ARGB1555_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4)) 84 + #define ATMEL_HLCDC_XRGB8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9)) 85 + #define ATMEL_HLCDC_RGB888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10)) 86 + #define ATMEL_HLCDC_ARGB8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12)) 87 + #define ATMEL_HLCDC_RGBA8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13)) 88 + 89 + #define ATMEL_HLCDC_AYUV_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0)) 90 + #define ATMEL_HLCDC_YUYV_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1)) 91 + #define ATMEL_HLCDC_UYVY_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2)) 92 + #define ATMEL_HLCDC_YVYU_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3)) 93 + #define ATMEL_HLCDC_VYUY_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4)) 94 + #define ATMEL_HLCDC_NV61_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5)) 95 + #define ATMEL_HLCDC_YUV422_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6)) 96 + #define ATMEL_HLCDC_NV21_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7)) 97 + #define ATMEL_HLCDC_YUV420_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8)) 98 + 99 + #define ATMEL_HLCDC_LAYER_POS_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos) 100 + #define ATMEL_HLCDC_LAYER_SIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size) 101 + #define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize) 102 + #define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride) 103 + #define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride) 104 + #define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color) 105 + #define ATMEL_HLCDC_LAYER_CRKEY_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key) 106 + #define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask) 107 + 108 + #define ATMEL_HLCDC_LAYER_GENERAL_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config) 109 + #define ATMEL_HLCDC_LAYER_CRKEY BIT(0) 110 + #define ATMEL_HLCDC_LAYER_INV BIT(1) 111 + #define ATMEL_HLCDC_LAYER_ITER2BL BIT(2) 112 + #define ATMEL_HLCDC_LAYER_ITER BIT(3) 113 + #define ATMEL_HLCDC_LAYER_REVALPHA BIT(4) 114 + #define ATMEL_HLCDC_LAYER_GAEN BIT(5) 115 + #define ATMEL_HLCDC_LAYER_LAEN BIT(6) 116 + #define ATMEL_HLCDC_LAYER_OVR BIT(7) 117 + #define ATMEL_HLCDC_LAYER_DMA BIT(8) 118 + #define ATMEL_HLCDC_LAYER_REP BIT(9) 119 + #define ATMEL_HLCDC_LAYER_DSTKEY BIT(10) 120 + #define ATMEL_HLCDC_LAYER_DISCEN BIT(11) 121 + #define ATMEL_HLCDC_LAYER_GA_SHIFT 16 122 + #define ATMEL_HLCDC_LAYER_GA_MASK GENMASK(23, ATMEL_HLCDC_LAYER_GA_SHIFT) 123 + 124 + #define ATMEL_HLCDC_LAYER_CSC_CFG(p, o) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o) 125 + 126 + #define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos) 127 + 128 + #define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size) 129 + 130 + #define ATMEL_HLCDC_MAX_PLANES 3 131 + 132 + #define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED BIT(0) 133 + #define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED BIT(1) 134 + #define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE BIT(2) 135 + #define ATMEL_HLCDC_DMA_CHANNEL_DSCR_OVERRUN BIT(3) 136 + 137 + /** 138 + * Atmel HLCDC Layer registers layout structure 139 + * 140 + * Each HLCDC layer has its own register organization and a given register 141 + * can be placed differently on 2 different layers depending on its 142 + * capabilities. 143 + * This structure stores common registers layout for a given layer and is 144 + * used by HLCDC layer code to choose the appropriate register to write to 145 + * or to read from. 146 + * 147 + * For all fields, a value of zero means "unsupported". 148 + * 149 + * See Atmel's datasheet for a detailled description of these registers. 150 + * 151 + * @xstride: xstride registers 152 + * @pstride: pstride registers 153 + * @pos: position register 154 + * @size: displayed size register 155 + * @memsize: memory size register 156 + * @default_color: default color register 157 + * @chroma_key: chroma key register 158 + * @chroma_key_mask: chroma key mask register 159 + * @general_config: general layer config register 160 + * @disc_pos: discard area position register 161 + * @disc_size: discard area size register 162 + * @csc: color space conversion register 163 + */ 164 + struct atmel_hlcdc_layer_cfg_layout { 165 + int xstride[ATMEL_HLCDC_MAX_PLANES]; 166 + int pstride[ATMEL_HLCDC_MAX_PLANES]; 167 + int pos; 168 + int size; 169 + int memsize; 170 + int default_color; 171 + int chroma_key; 172 + int chroma_key_mask; 173 + int general_config; 174 + int disc_pos; 175 + int disc_size; 176 + int csc; 177 + }; 178 + 179 + /** 180 + * Atmel HLCDC framebuffer flip structure 181 + * 182 + * This structure is allocated when someone asked for a layer update (most 183 + * likely a DRM plane update, either primary, overlay or cursor plane) and 184 + * released when the layer do not need to reference the framebuffer object 185 + * anymore (i.e. the layer was disabled or updated). 186 + * 187 + * @dscrs: DMA descriptors 188 + * @fb: the referenced framebuffer object 189 + * @ngems: number of GEM objects referenced by the fb element 190 + * @status: fb flip operation status 191 + */ 192 + struct atmel_hlcdc_layer_fb_flip { 193 + struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES]; 194 + struct drm_flip_task *task; 195 + struct drm_framebuffer *fb; 196 + int ngems; 197 + u32 status; 198 + }; 199 + 200 + /** 201 + * Atmel HLCDC DMA descriptor structure 202 + * 203 + * This structure is used by the HLCDC DMA engine to schedule a DMA transfer. 204 + * 205 + * The structure fields must remain in this specific order, because they're 206 + * used by the HLCDC DMA engine, which expect them in this order. 207 + * HLCDC DMA descriptors must be aligned on 64 bits. 208 + * 209 + * @addr: buffer DMA address 210 + * @ctrl: DMA transfer options 211 + * @next: next DMA descriptor to fetch 212 + * @gem_flip: the attached gem_flip operation 213 + */ 214 + struct atmel_hlcdc_dma_channel_dscr { 215 + dma_addr_t addr; 216 + u32 ctrl; 217 + dma_addr_t next; 218 + u32 status; 219 + } __aligned(sizeof(u64)); 220 + 221 + /** 222 + * Atmel HLCDC layer types 223 + */ 224 + enum atmel_hlcdc_layer_type { 225 + ATMEL_HLCDC_BASE_LAYER, 226 + ATMEL_HLCDC_OVERLAY_LAYER, 227 + ATMEL_HLCDC_CURSOR_LAYER, 228 + ATMEL_HLCDC_PP_LAYER, 229 + }; 230 + 231 + /** 232 + * Atmel HLCDC Supported formats structure 233 + * 234 + * This structure list all the formats supported by a given layer. 235 + * 236 + * @nformats: number of supported formats 237 + * @formats: supported formats 238 + */ 239 + struct atmel_hlcdc_formats { 240 + int nformats; 241 + uint32_t *formats; 242 + }; 243 + 244 + /** 245 + * Atmel HLCDC Layer description structure 246 + * 247 + * This structure describe the capabilities provided by a given layer. 248 + * 249 + * @name: layer name 250 + * @type: layer type 251 + * @id: layer id 252 + * @regs_offset: offset of the layer registers from the HLCDC registers base 253 + * @nconfigs: number of config registers provided by this layer 254 + * @formats: supported formats 255 + * @layout: config registers layout 256 + * @max_width: maximum width supported by this layer (0 means unlimited) 257 + * @max_height: maximum height supported by this layer (0 means unlimited) 258 + */ 259 + struct atmel_hlcdc_layer_desc { 260 + const char *name; 261 + enum atmel_hlcdc_layer_type type; 262 + int id; 263 + int regs_offset; 264 + int nconfigs; 265 + struct atmel_hlcdc_formats *formats; 266 + struct atmel_hlcdc_layer_cfg_layout layout; 267 + int max_width; 268 + int max_height; 269 + }; 270 + 271 + /** 272 + * Atmel HLCDC Layer Update Slot structure 273 + * 274 + * This structure stores layer update requests to be applied on next frame. 275 + * This is the base structure behind the atomic layer update infrastructure. 276 + * 277 + * Atomic layer update provides a way to update all layer's parameters 278 + * simultaneously. This is needed to avoid incompatible sequential updates 279 + * like this one: 280 + * 1) update layer format from RGB888 (1 plane/buffer) to YUV422 281 + * (2 planes/buffers) 282 + * 2) the format update is applied but the DMA channel for the second 283 + * plane/buffer is not enabled 284 + * 3) enable the DMA channel for the second plane 285 + * 286 + * @fb_flip: fb_flip object 287 + * @updated_configs: bitmask used to record modified configs 288 + * @configs: new config values 289 + */ 290 + struct atmel_hlcdc_layer_update_slot { 291 + struct atmel_hlcdc_layer_fb_flip *fb_flip; 292 + unsigned long *updated_configs; 293 + u32 *configs; 294 + }; 295 + 296 + /** 297 + * Atmel HLCDC Layer Update structure 298 + * 299 + * This structure provides a way to queue layer update requests. 300 + * 301 + * At a given time there is at most: 302 + * - one pending update request, which means the update request has been 303 + * committed (or validated) and is waiting for the DMA channel(s) to be 304 + * available 305 + * - one request being prepared, which means someone started a layer update 306 + * but has not committed it yet. There cannot be more than one started 307 + * request, because the update lock is taken when starting a layer update 308 + * and release when committing or rolling back the request. 309 + * 310 + * @slots: update slots. One is used for pending request and the other one 311 + * for started update request 312 + * @pending: the pending slot index or -1 if no request is pending 313 + * @next: the started update slot index or -1 no update has been started 314 + */ 315 + struct atmel_hlcdc_layer_update { 316 + struct atmel_hlcdc_layer_update_slot slots[2]; 317 + int pending; 318 + int next; 319 + }; 320 + 321 + enum atmel_hlcdc_layer_dma_channel_status { 322 + ATMEL_HLCDC_LAYER_DISABLED, 323 + ATMEL_HLCDC_LAYER_ENABLED, 324 + ATMEL_HLCDC_LAYER_DISABLING, 325 + }; 326 + 327 + /** 328 + * Atmel HLCDC Layer DMA channel structure 329 + * 330 + * This structure stores information on the DMA channel associated to a 331 + * given layer. 332 + * 333 + * @status: DMA channel status 334 + * @cur: current framebuffer 335 + * @queue: next framebuffer 336 + * @dscrs: allocated DMA descriptors 337 + */ 338 + struct atmel_hlcdc_layer_dma_channel { 339 + enum atmel_hlcdc_layer_dma_channel_status status; 340 + struct atmel_hlcdc_layer_fb_flip *cur; 341 + struct atmel_hlcdc_layer_fb_flip *queue; 342 + struct atmel_hlcdc_dma_channel_dscr *dscrs; 343 + }; 344 + 345 + /** 346 + * Atmel HLCDC Layer structure 347 + * 348 + * This structure stores information on the layer instance. 349 + * 350 + * @desc: layer description 351 + * @max_planes: maximum planes/buffers that can be associated with this layer. 352 + * This depends on the supported formats. 353 + * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device 354 + * @dma: dma channel 355 + * @gc: fb flip garbage collector 356 + * @update: update handler 357 + * @lock: layer lock 358 + */ 359 + struct atmel_hlcdc_layer { 360 + const struct atmel_hlcdc_layer_desc *desc; 361 + int max_planes; 362 + struct atmel_hlcdc *hlcdc; 363 + struct workqueue_struct *wq; 364 + struct drm_flip_work gc; 365 + struct atmel_hlcdc_layer_dma_channel dma; 366 + struct atmel_hlcdc_layer_update update; 367 + spinlock_t lock; 368 + }; 369 + 370 + void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer); 371 + 372 + int atmel_hlcdc_layer_init(struct drm_device *dev, 373 + struct atmel_hlcdc_layer *layer, 374 + const struct atmel_hlcdc_layer_desc *desc); 375 + 376 + void atmel_hlcdc_layer_cleanup(struct drm_device *dev, 377 + struct atmel_hlcdc_layer *layer); 378 + 379 + int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer); 380 + 381 + int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer); 382 + 383 + void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg, 384 + u32 mask, u32 val); 385 + 386 + void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer, 387 + struct drm_framebuffer *fb, 388 + unsigned int *offsets); 389 + 390 + void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer, 391 + void (*finished)(void *data), 392 + void *finished_data); 393 + 394 + void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer); 395 + 396 + void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer); 397 + 398 + #endif /* DRM_ATMEL_HLCDC_LAYER_H */
+319
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
··· 1 + /* 2 + * Copyright (C) 2014 Traphandler 3 + * Copyright (C) 2014 Free Electrons 4 + * Copyright (C) 2014 Atmel 5 + * 6 + * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify it 10 + * under the terms of the GNU General Public License version 2 as published by 11 + * the Free Software Foundation. 12 + * 13 + * This program is distributed in the hope that it will be useful, but WITHOUT 14 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 + * more details. 17 + * 18 + * You should have received a copy of the GNU General Public License along with 19 + * this program. If not, see <http://www.gnu.org/licenses/>. 20 + */ 21 + 22 + #include <linux/of_graph.h> 23 + 24 + #include <drm/drmP.h> 25 + #include <drm/drm_panel.h> 26 + 27 + #include "atmel_hlcdc_dc.h" 28 + 29 + /** 30 + * Atmel HLCDC RGB output mode 31 + */ 32 + enum atmel_hlcdc_connector_rgb_mode { 33 + ATMEL_HLCDC_CONNECTOR_RGB444, 34 + ATMEL_HLCDC_CONNECTOR_RGB565, 35 + ATMEL_HLCDC_CONNECTOR_RGB666, 36 + ATMEL_HLCDC_CONNECTOR_RGB888, 37 + }; 38 + 39 + /** 40 + * Atmel HLCDC RGB connector structure 41 + * 42 + * This structure stores RGB slave device information. 43 + * 44 + * @connector: DRM connector 45 + * @encoder: DRM encoder 46 + * @dc: pointer to the atmel_hlcdc_dc structure 47 + * @dpms: current DPMS mode 48 + */ 49 + struct atmel_hlcdc_rgb_output { 50 + struct drm_connector connector; 51 + struct drm_encoder encoder; 52 + struct atmel_hlcdc_dc *dc; 53 + int dpms; 54 + }; 55 + 56 + static inline struct atmel_hlcdc_rgb_output * 57 + drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector) 58 + { 59 + return container_of(connector, struct atmel_hlcdc_rgb_output, 60 + connector); 61 + } 62 + 63 + static inline struct atmel_hlcdc_rgb_output * 64 + drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder) 65 + { 66 + return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); 67 + } 68 + 69 + /** 70 + * Atmel HLCDC Panel device structure 71 + * 72 + * This structure is specialization of the slave device structure to 73 + * interface with drm panels. 74 + * 75 + * @base: base slave device fields 76 + * @panel: drm panel attached to this slave device 77 + */ 78 + struct atmel_hlcdc_panel { 79 + struct atmel_hlcdc_rgb_output base; 80 + struct drm_panel *panel; 81 + }; 82 + 83 + static inline struct atmel_hlcdc_panel * 84 + atmel_hlcdc_rgb_output_to_panel(struct atmel_hlcdc_rgb_output *output) 85 + { 86 + return container_of(output, struct atmel_hlcdc_panel, base); 87 + } 88 + 89 + static void atmel_hlcdc_panel_encoder_dpms(struct drm_encoder *encoder, 90 + int mode) 91 + { 92 + struct atmel_hlcdc_rgb_output *rgb = 93 + drm_encoder_to_atmel_hlcdc_rgb_output(encoder); 94 + struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb); 95 + 96 + if (mode != DRM_MODE_DPMS_ON) 97 + mode = DRM_MODE_DPMS_OFF; 98 + 99 + if (mode == rgb->dpms) 100 + return; 101 + 102 + if (mode != DRM_MODE_DPMS_ON) 103 + drm_panel_disable(panel->panel); 104 + else 105 + drm_panel_enable(panel->panel); 106 + 107 + rgb->dpms = mode; 108 + } 109 + 110 + static bool 111 + atmel_hlcdc_panel_encoder_mode_fixup(struct drm_encoder *encoder, 112 + const struct drm_display_mode *mode, 113 + struct drm_display_mode *adjusted) 114 + { 115 + return true; 116 + } 117 + 118 + static void atmel_hlcdc_panel_encoder_prepare(struct drm_encoder *encoder) 119 + { 120 + atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); 121 + } 122 + 123 + static void atmel_hlcdc_panel_encoder_commit(struct drm_encoder *encoder) 124 + { 125 + atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON); 126 + } 127 + 128 + static void 129 + atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder, 130 + struct drm_display_mode *mode, 131 + struct drm_display_mode *adjusted) 132 + { 133 + struct atmel_hlcdc_rgb_output *rgb = 134 + drm_encoder_to_atmel_hlcdc_rgb_output(encoder); 135 + struct drm_display_info *info = &rgb->connector.display_info; 136 + unsigned int cfg; 137 + 138 + cfg = 0; 139 + 140 + if (info->num_bus_formats) { 141 + switch (info->bus_formats[0]) { 142 + case MEDIA_BUS_FMT_RGB666_1X18: 143 + cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8; 144 + break; 145 + case MEDIA_BUS_FMT_RGB888_1X24: 146 + cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8; 147 + break; 148 + default: 149 + break; 150 + } 151 + } 152 + 153 + regmap_update_bits(rgb->dc->hlcdc->regmap, ATMEL_HLCDC_CFG(5), 154 + ATMEL_HLCDC_MODE_MASK, 155 + cfg); 156 + } 157 + 158 + static struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = { 159 + .dpms = atmel_hlcdc_panel_encoder_dpms, 160 + .mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup, 161 + .prepare = atmel_hlcdc_panel_encoder_prepare, 162 + .commit = atmel_hlcdc_panel_encoder_commit, 163 + .mode_set = atmel_hlcdc_rgb_encoder_mode_set, 164 + }; 165 + 166 + static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder) 167 + { 168 + drm_encoder_cleanup(encoder); 169 + memset(encoder, 0, sizeof(*encoder)); 170 + } 171 + 172 + static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { 173 + .destroy = atmel_hlcdc_rgb_encoder_destroy, 174 + }; 175 + 176 + static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector) 177 + { 178 + struct atmel_hlcdc_rgb_output *rgb = 179 + drm_connector_to_atmel_hlcdc_rgb_output(connector); 180 + struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb); 181 + 182 + return panel->panel->funcs->get_modes(panel->panel); 183 + } 184 + 185 + static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector, 186 + struct drm_display_mode *mode) 187 + { 188 + struct atmel_hlcdc_rgb_output *rgb = 189 + drm_connector_to_atmel_hlcdc_rgb_output(connector); 190 + 191 + return atmel_hlcdc_dc_mode_valid(rgb->dc, mode); 192 + } 193 + 194 + 195 + 196 + static struct drm_encoder * 197 + atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector) 198 + { 199 + struct atmel_hlcdc_rgb_output *rgb = 200 + drm_connector_to_atmel_hlcdc_rgb_output(connector); 201 + 202 + return &rgb->encoder; 203 + } 204 + 205 + static struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = { 206 + .get_modes = atmel_hlcdc_panel_get_modes, 207 + .mode_valid = atmel_hlcdc_rgb_mode_valid, 208 + .best_encoder = atmel_hlcdc_rgb_best_encoder, 209 + }; 210 + 211 + static enum drm_connector_status 212 + atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force) 213 + { 214 + return connector_status_connected; 215 + } 216 + 217 + static void 218 + atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector) 219 + { 220 + struct atmel_hlcdc_rgb_output *rgb = 221 + drm_connector_to_atmel_hlcdc_rgb_output(connector); 222 + struct atmel_hlcdc_panel *panel = atmel_hlcdc_rgb_output_to_panel(rgb); 223 + 224 + drm_panel_detach(panel->panel); 225 + drm_connector_cleanup(connector); 226 + } 227 + 228 + static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = { 229 + .dpms = drm_helper_connector_dpms, 230 + .detect = atmel_hlcdc_panel_connector_detect, 231 + .fill_modes = drm_helper_probe_single_connector_modes, 232 + .destroy = atmel_hlcdc_panel_connector_destroy, 233 + }; 234 + 235 + static int atmel_hlcdc_create_panel_output(struct drm_device *dev, 236 + struct of_endpoint *ep) 237 + { 238 + struct atmel_hlcdc_dc *dc = dev->dev_private; 239 + struct device_node *np; 240 + struct drm_panel *p = NULL; 241 + struct atmel_hlcdc_panel *panel; 242 + int ret; 243 + 244 + np = of_graph_get_remote_port_parent(ep->local_node); 245 + if (!np) 246 + return -EINVAL; 247 + 248 + p = of_drm_find_panel(np); 249 + of_node_put(np); 250 + 251 + if (!p) 252 + return -EPROBE_DEFER; 253 + 254 + panel = devm_kzalloc(dev->dev, sizeof(*panel), GFP_KERNEL); 255 + if (!panel) 256 + return -EINVAL; 257 + 258 + panel->base.dpms = DRM_MODE_DPMS_OFF; 259 + 260 + panel->base.dc = dc; 261 + 262 + drm_encoder_helper_add(&panel->base.encoder, 263 + &atmel_hlcdc_panel_encoder_helper_funcs); 264 + ret = drm_encoder_init(dev, &panel->base.encoder, 265 + &atmel_hlcdc_panel_encoder_funcs, 266 + DRM_MODE_ENCODER_LVDS); 267 + if (ret) 268 + return ret; 269 + 270 + panel->base.connector.dpms = DRM_MODE_DPMS_OFF; 271 + panel->base.connector.polled = DRM_CONNECTOR_POLL_CONNECT; 272 + drm_connector_helper_add(&panel->base.connector, 273 + &atmel_hlcdc_panel_connector_helper_funcs); 274 + ret = drm_connector_init(dev, &panel->base.connector, 275 + &atmel_hlcdc_panel_connector_funcs, 276 + DRM_MODE_CONNECTOR_LVDS); 277 + if (ret) 278 + goto err_encoder_cleanup; 279 + 280 + drm_mode_connector_attach_encoder(&panel->base.connector, 281 + &panel->base.encoder); 282 + panel->base.encoder.possible_crtcs = 0x1; 283 + 284 + drm_panel_attach(p, &panel->base.connector); 285 + panel->panel = p; 286 + 287 + return 0; 288 + 289 + err_encoder_cleanup: 290 + drm_encoder_cleanup(&panel->base.encoder); 291 + 292 + return ret; 293 + } 294 + 295 + int atmel_hlcdc_create_outputs(struct drm_device *dev) 296 + { 297 + struct device_node *port_np, *np; 298 + struct of_endpoint ep; 299 + int ret; 300 + 301 + port_np = of_get_child_by_name(dev->dev->of_node, "port"); 302 + if (!port_np) 303 + return -EINVAL; 304 + 305 + np = of_get_child_by_name(port_np, "endpoint"); 306 + of_node_put(port_np); 307 + 308 + if (!np) 309 + return -EINVAL; 310 + 311 + ret = of_graph_parse_endpoint(np, &ep); 312 + of_node_put(port_np); 313 + 314 + if (ret) 315 + return ret; 316 + 317 + /* We currently only support panel output */ 318 + return atmel_hlcdc_create_panel_output(dev, &ep); 319 + }
+856
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
··· 1 + /* 2 + * Copyright (C) 2014 Free Electrons 3 + * Copyright (C) 2014 Atmel 4 + * 5 + * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as published by 9 + * the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, but WITHOUT 12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 + * more details. 15 + * 16 + * You should have received a copy of the GNU General Public License along with 17 + * this program. If not, see <http://www.gnu.org/licenses/>. 18 + */ 19 + 20 + #include "atmel_hlcdc_dc.h" 21 + 22 + #define SUBPIXEL_MASK 0xffff 23 + 24 + static uint32_t rgb_formats[] = { 25 + DRM_FORMAT_XRGB4444, 26 + DRM_FORMAT_ARGB4444, 27 + DRM_FORMAT_RGBA4444, 28 + DRM_FORMAT_ARGB1555, 29 + DRM_FORMAT_RGB565, 30 + DRM_FORMAT_RGB888, 31 + DRM_FORMAT_XRGB8888, 32 + DRM_FORMAT_ARGB8888, 33 + DRM_FORMAT_RGBA8888, 34 + }; 35 + 36 + struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = { 37 + .formats = rgb_formats, 38 + .nformats = ARRAY_SIZE(rgb_formats), 39 + }; 40 + 41 + static uint32_t rgb_and_yuv_formats[] = { 42 + DRM_FORMAT_XRGB4444, 43 + DRM_FORMAT_ARGB4444, 44 + DRM_FORMAT_RGBA4444, 45 + DRM_FORMAT_ARGB1555, 46 + DRM_FORMAT_RGB565, 47 + DRM_FORMAT_RGB888, 48 + DRM_FORMAT_XRGB8888, 49 + DRM_FORMAT_ARGB8888, 50 + DRM_FORMAT_RGBA8888, 51 + DRM_FORMAT_AYUV, 52 + DRM_FORMAT_YUYV, 53 + DRM_FORMAT_UYVY, 54 + DRM_FORMAT_YVYU, 55 + DRM_FORMAT_VYUY, 56 + DRM_FORMAT_NV21, 57 + DRM_FORMAT_NV61, 58 + DRM_FORMAT_YUV422, 59 + DRM_FORMAT_YUV420, 60 + }; 61 + 62 + struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = { 63 + .formats = rgb_and_yuv_formats, 64 + .nformats = ARRAY_SIZE(rgb_and_yuv_formats), 65 + }; 66 + 67 + static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) 68 + { 69 + switch (format) { 70 + case DRM_FORMAT_XRGB4444: 71 + *mode = ATMEL_HLCDC_XRGB4444_MODE; 72 + break; 73 + case DRM_FORMAT_ARGB4444: 74 + *mode = ATMEL_HLCDC_ARGB4444_MODE; 75 + break; 76 + case DRM_FORMAT_RGBA4444: 77 + *mode = ATMEL_HLCDC_RGBA4444_MODE; 78 + break; 79 + case DRM_FORMAT_RGB565: 80 + *mode = ATMEL_HLCDC_RGB565_MODE; 81 + break; 82 + case DRM_FORMAT_RGB888: 83 + *mode = ATMEL_HLCDC_RGB888_MODE; 84 + break; 85 + case DRM_FORMAT_ARGB1555: 86 + *mode = ATMEL_HLCDC_ARGB1555_MODE; 87 + break; 88 + case DRM_FORMAT_XRGB8888: 89 + *mode = ATMEL_HLCDC_XRGB8888_MODE; 90 + break; 91 + case DRM_FORMAT_ARGB8888: 92 + *mode = ATMEL_HLCDC_ARGB8888_MODE; 93 + break; 94 + case DRM_FORMAT_RGBA8888: 95 + *mode = ATMEL_HLCDC_RGBA8888_MODE; 96 + break; 97 + case DRM_FORMAT_AYUV: 98 + *mode = ATMEL_HLCDC_AYUV_MODE; 99 + break; 100 + case DRM_FORMAT_YUYV: 101 + *mode = ATMEL_HLCDC_YUYV_MODE; 102 + break; 103 + case DRM_FORMAT_UYVY: 104 + *mode = ATMEL_HLCDC_UYVY_MODE; 105 + break; 106 + case DRM_FORMAT_YVYU: 107 + *mode = ATMEL_HLCDC_YVYU_MODE; 108 + break; 109 + case DRM_FORMAT_VYUY: 110 + *mode = ATMEL_HLCDC_VYUY_MODE; 111 + break; 112 + case DRM_FORMAT_NV21: 113 + *mode = ATMEL_HLCDC_NV21_MODE; 114 + break; 115 + case DRM_FORMAT_NV61: 116 + *mode = ATMEL_HLCDC_NV61_MODE; 117 + break; 118 + case DRM_FORMAT_YUV420: 119 + *mode = ATMEL_HLCDC_YUV420_MODE; 120 + break; 121 + case DRM_FORMAT_YUV422: 122 + *mode = ATMEL_HLCDC_YUV422_MODE; 123 + break; 124 + default: 125 + return -ENOTSUPP; 126 + } 127 + 128 + return 0; 129 + } 130 + 131 + static bool atmel_hlcdc_format_embedds_alpha(u32 format) 132 + { 133 + int i; 134 + 135 + for (i = 0; i < sizeof(format); i++) { 136 + char tmp = (format >> (8 * i)) & 0xff; 137 + 138 + if (tmp == 'A') 139 + return true; 140 + } 141 + 142 + return false; 143 + } 144 + 145 + static u32 heo_downscaling_xcoef[] = { 146 + 0x11343311, 147 + 0x000000f7, 148 + 0x1635300c, 149 + 0x000000f9, 150 + 0x1b362c08, 151 + 0x000000fb, 152 + 0x1f372804, 153 + 0x000000fe, 154 + 0x24382400, 155 + 0x00000000, 156 + 0x28371ffe, 157 + 0x00000004, 158 + 0x2c361bfb, 159 + 0x00000008, 160 + 0x303516f9, 161 + 0x0000000c, 162 + }; 163 + 164 + static u32 heo_downscaling_ycoef[] = { 165 + 0x00123737, 166 + 0x00173732, 167 + 0x001b382d, 168 + 0x001f3928, 169 + 0x00243824, 170 + 0x0028391f, 171 + 0x002d381b, 172 + 0x00323717, 173 + }; 174 + 175 + static u32 heo_upscaling_xcoef[] = { 176 + 0xf74949f7, 177 + 0x00000000, 178 + 0xf55f33fb, 179 + 0x000000fe, 180 + 0xf5701efe, 181 + 0x000000ff, 182 + 0xf87c0dff, 183 + 0x00000000, 184 + 0x00800000, 185 + 0x00000000, 186 + 0x0d7cf800, 187 + 0x000000ff, 188 + 0x1e70f5ff, 189 + 0x000000fe, 190 + 0x335ff5fe, 191 + 0x000000fb, 192 + }; 193 + 194 + static u32 heo_upscaling_ycoef[] = { 195 + 0x00004040, 196 + 0x00075920, 197 + 0x00056f0c, 198 + 0x00027b03, 199 + 0x00008000, 200 + 0x00037b02, 201 + 0x000c6f05, 202 + 0x00205907, 203 + }; 204 + 205 + static void 206 + atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, 207 + struct atmel_hlcdc_plane_update_req *req) 208 + { 209 + const struct atmel_hlcdc_layer_cfg_layout *layout = 210 + &plane->layer.desc->layout; 211 + 212 + if (layout->size) 213 + atmel_hlcdc_layer_update_cfg(&plane->layer, 214 + layout->size, 215 + 0xffffffff, 216 + (req->crtc_w - 1) | 217 + ((req->crtc_h - 1) << 16)); 218 + 219 + if (layout->memsize) 220 + atmel_hlcdc_layer_update_cfg(&plane->layer, 221 + layout->memsize, 222 + 0xffffffff, 223 + (req->src_w - 1) | 224 + ((req->src_h - 1) << 16)); 225 + 226 + if (layout->pos) 227 + atmel_hlcdc_layer_update_cfg(&plane->layer, 228 + layout->pos, 229 + 0xffffffff, 230 + req->crtc_x | 231 + (req->crtc_y << 16)); 232 + 233 + /* TODO: rework the rescaling part */ 234 + if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) { 235 + u32 factor_reg = 0; 236 + 237 + if (req->crtc_w != req->src_w) { 238 + int i; 239 + u32 factor; 240 + u32 *coeff_tab = heo_upscaling_xcoef; 241 + u32 max_memsize; 242 + 243 + if (req->crtc_w < req->src_w) 244 + coeff_tab = heo_downscaling_xcoef; 245 + for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++) 246 + atmel_hlcdc_layer_update_cfg(&plane->layer, 247 + 17 + i, 248 + 0xffffffff, 249 + coeff_tab[i]); 250 + factor = ((8 * 256 * req->src_w) - (256 * 4)) / 251 + req->crtc_w; 252 + factor++; 253 + max_memsize = ((factor * req->crtc_w) + (256 * 4)) / 254 + 2048; 255 + if (max_memsize > req->src_w) 256 + factor--; 257 + factor_reg |= factor | 0x80000000; 258 + } 259 + 260 + if (req->crtc_h != req->src_h) { 261 + int i; 262 + u32 factor; 263 + u32 *coeff_tab = heo_upscaling_ycoef; 264 + u32 max_memsize; 265 + 266 + if (req->crtc_w < req->src_w) 267 + coeff_tab = heo_downscaling_ycoef; 268 + for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++) 269 + atmel_hlcdc_layer_update_cfg(&plane->layer, 270 + 33 + i, 271 + 0xffffffff, 272 + coeff_tab[i]); 273 + factor = ((8 * 256 * req->src_w) - (256 * 4)) / 274 + req->crtc_w; 275 + factor++; 276 + max_memsize = ((factor * req->crtc_w) + (256 * 4)) / 277 + 2048; 278 + if (max_memsize > req->src_w) 279 + factor--; 280 + factor_reg |= (factor << 16) | 0x80000000; 281 + } 282 + 283 + atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff, 284 + factor_reg); 285 + } 286 + } 287 + 288 + static void 289 + atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, 290 + struct atmel_hlcdc_plane_update_req *req) 291 + { 292 + const struct atmel_hlcdc_layer_cfg_layout *layout = 293 + &plane->layer.desc->layout; 294 + unsigned int cfg = ATMEL_HLCDC_LAYER_DMA; 295 + 296 + if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { 297 + cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | 298 + ATMEL_HLCDC_LAYER_ITER; 299 + 300 + if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)) 301 + cfg |= ATMEL_HLCDC_LAYER_LAEN; 302 + else 303 + cfg |= ATMEL_HLCDC_LAYER_GAEN; 304 + } 305 + 306 + atmel_hlcdc_layer_update_cfg(&plane->layer, 307 + ATMEL_HLCDC_LAYER_DMA_CFG_ID, 308 + ATMEL_HLCDC_LAYER_DMA_BLEN_MASK, 309 + ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16); 310 + 311 + atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config, 312 + ATMEL_HLCDC_LAYER_ITER2BL | 313 + ATMEL_HLCDC_LAYER_ITER | 314 + ATMEL_HLCDC_LAYER_GAEN | 315 + ATMEL_HLCDC_LAYER_LAEN | 316 + ATMEL_HLCDC_LAYER_OVR | 317 + ATMEL_HLCDC_LAYER_DMA, cfg); 318 + } 319 + 320 + static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, 321 + struct atmel_hlcdc_plane_update_req *req) 322 + { 323 + u32 cfg; 324 + int ret; 325 + 326 + ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &cfg); 327 + if (ret) 328 + return; 329 + 330 + if ((req->fb->pixel_format == DRM_FORMAT_YUV422 || 331 + req->fb->pixel_format == DRM_FORMAT_NV61) && 332 + (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270)))) 333 + cfg |= ATMEL_HLCDC_YUV422ROT; 334 + 335 + atmel_hlcdc_layer_update_cfg(&plane->layer, 336 + ATMEL_HLCDC_LAYER_FORMAT_CFG_ID, 337 + 0xffffffff, 338 + cfg); 339 + 340 + /* 341 + * Rotation optimization is not working on RGB888 (rotation is still 342 + * working but without any optimization). 343 + */ 344 + if (req->fb->pixel_format == DRM_FORMAT_RGB888) 345 + cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS; 346 + else 347 + cfg = 0; 348 + 349 + atmel_hlcdc_layer_update_cfg(&plane->layer, 350 + ATMEL_HLCDC_LAYER_DMA_CFG_ID, 351 + ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg); 352 + } 353 + 354 + static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, 355 + struct atmel_hlcdc_plane_update_req *req) 356 + { 357 + struct atmel_hlcdc_layer *layer = &plane->layer; 358 + const struct atmel_hlcdc_layer_cfg_layout *layout = 359 + &layer->desc->layout; 360 + int i; 361 + 362 + atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets); 363 + 364 + for (i = 0; i < req->nplanes; i++) { 365 + if (layout->xstride[i]) { 366 + atmel_hlcdc_layer_update_cfg(&plane->layer, 367 + layout->xstride[i], 368 + 0xffffffff, 369 + req->xstride[i]); 370 + } 371 + 372 + if (layout->pstride[i]) { 373 + atmel_hlcdc_layer_update_cfg(&plane->layer, 374 + layout->pstride[i], 375 + 0xffffffff, 376 + req->pstride[i]); 377 + } 378 + } 379 + } 380 + 381 + static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p, 382 + struct atmel_hlcdc_plane_update_req *req, 383 + const struct drm_display_mode *mode) 384 + { 385 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 386 + const struct atmel_hlcdc_layer_cfg_layout *layout = 387 + &plane->layer.desc->layout; 388 + 389 + if (!layout->size && 390 + (mode->hdisplay != req->crtc_w || 391 + mode->vdisplay != req->crtc_h)) 392 + return -EINVAL; 393 + 394 + if (plane->layer.desc->max_height && 395 + req->crtc_h > plane->layer.desc->max_height) 396 + return -EINVAL; 397 + 398 + if (plane->layer.desc->max_width && 399 + req->crtc_w > plane->layer.desc->max_width) 400 + return -EINVAL; 401 + 402 + if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) && 403 + (!layout->memsize || 404 + atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))) 405 + return -EINVAL; 406 + 407 + if (req->crtc_x < 0 || req->crtc_y < 0) 408 + return -EINVAL; 409 + 410 + if (req->crtc_w + req->crtc_x > mode->hdisplay || 411 + req->crtc_h + req->crtc_y > mode->vdisplay) 412 + return -EINVAL; 413 + 414 + return 0; 415 + } 416 + 417 + int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p, 418 + struct atmel_hlcdc_plane_update_req *req, 419 + const struct drm_display_mode *mode) 420 + { 421 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 422 + unsigned int patched_crtc_w; 423 + unsigned int patched_crtc_h; 424 + unsigned int patched_src_w; 425 + unsigned int patched_src_h; 426 + unsigned int tmp; 427 + int x_offset = 0; 428 + int y_offset = 0; 429 + int hsub = 1; 430 + int vsub = 1; 431 + int i; 432 + 433 + if ((req->src_x | req->src_y | req->src_w | req->src_h) & 434 + SUBPIXEL_MASK) 435 + return -EINVAL; 436 + 437 + req->src_x >>= 16; 438 + req->src_y >>= 16; 439 + req->src_w >>= 16; 440 + req->src_h >>= 16; 441 + 442 + req->nplanes = drm_format_num_planes(req->fb->pixel_format); 443 + if (req->nplanes > ATMEL_HLCDC_MAX_PLANES) 444 + return -EINVAL; 445 + 446 + /* 447 + * Swap width and size in case of 90 or 270 degrees rotation 448 + */ 449 + if (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { 450 + tmp = req->crtc_w; 451 + req->crtc_w = req->crtc_h; 452 + req->crtc_h = tmp; 453 + tmp = req->src_w; 454 + req->src_w = req->src_h; 455 + req->src_h = tmp; 456 + } 457 + 458 + if (req->crtc_x + req->crtc_w > mode->hdisplay) 459 + patched_crtc_w = mode->hdisplay - req->crtc_x; 460 + else 461 + patched_crtc_w = req->crtc_w; 462 + 463 + if (req->crtc_x < 0) { 464 + patched_crtc_w += req->crtc_x; 465 + x_offset = -req->crtc_x; 466 + req->crtc_x = 0; 467 + } 468 + 469 + if (req->crtc_y + req->crtc_h > mode->vdisplay) 470 + patched_crtc_h = mode->vdisplay - req->crtc_y; 471 + else 472 + patched_crtc_h = req->crtc_h; 473 + 474 + if (req->crtc_y < 0) { 475 + patched_crtc_h += req->crtc_y; 476 + y_offset = -req->crtc_y; 477 + req->crtc_y = 0; 478 + } 479 + 480 + patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w, 481 + req->crtc_w); 482 + patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h, 483 + req->crtc_h); 484 + 485 + hsub = drm_format_horz_chroma_subsampling(req->fb->pixel_format); 486 + vsub = drm_format_vert_chroma_subsampling(req->fb->pixel_format); 487 + 488 + for (i = 0; i < req->nplanes; i++) { 489 + unsigned int offset = 0; 490 + int xdiv = i ? hsub : 1; 491 + int ydiv = i ? vsub : 1; 492 + 493 + req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i); 494 + if (!req->bpp[i]) 495 + return -EINVAL; 496 + 497 + switch (plane->rotation & 0xf) { 498 + case BIT(DRM_ROTATE_90): 499 + offset = ((y_offset + req->src_y + patched_src_w - 1) / 500 + ydiv) * req->fb->pitches[i]; 501 + offset += ((x_offset + req->src_x) / xdiv) * 502 + req->bpp[i]; 503 + req->xstride[i] = ((patched_src_w - 1) / ydiv) * 504 + req->fb->pitches[i]; 505 + req->pstride[i] = -req->fb->pitches[i] - req->bpp[i]; 506 + break; 507 + case BIT(DRM_ROTATE_180): 508 + offset = ((y_offset + req->src_y + patched_src_h - 1) / 509 + ydiv) * req->fb->pitches[i]; 510 + offset += ((x_offset + req->src_x + patched_src_w - 1) / 511 + xdiv) * req->bpp[i]; 512 + req->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) * 513 + req->bpp[i]) - req->fb->pitches[i]; 514 + req->pstride[i] = -2 * req->bpp[i]; 515 + break; 516 + case BIT(DRM_ROTATE_270): 517 + offset = ((y_offset + req->src_y) / ydiv) * 518 + req->fb->pitches[i]; 519 + offset += ((x_offset + req->src_x + patched_src_h - 1) / 520 + xdiv) * req->bpp[i]; 521 + req->xstride[i] = -(((patched_src_w - 1) / ydiv) * 522 + req->fb->pitches[i]) - 523 + (2 * req->bpp[i]); 524 + req->pstride[i] = req->fb->pitches[i] - req->bpp[i]; 525 + break; 526 + case BIT(DRM_ROTATE_0): 527 + default: 528 + offset = ((y_offset + req->src_y) / ydiv) * 529 + req->fb->pitches[i]; 530 + offset += ((x_offset + req->src_x) / xdiv) * 531 + req->bpp[i]; 532 + req->xstride[i] = req->fb->pitches[i] - 533 + ((patched_src_w / xdiv) * 534 + req->bpp[i]); 535 + req->pstride[i] = 0; 536 + break; 537 + } 538 + 539 + req->offsets[i] = offset + req->fb->offsets[i]; 540 + } 541 + 542 + req->src_w = patched_src_w; 543 + req->src_h = patched_src_h; 544 + req->crtc_w = patched_crtc_w; 545 + req->crtc_h = patched_crtc_h; 546 + 547 + return atmel_hlcdc_plane_check_update_req(p, req, mode); 548 + } 549 + 550 + int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p, 551 + struct atmel_hlcdc_plane_update_req *req) 552 + { 553 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 554 + int ret; 555 + 556 + ret = atmel_hlcdc_layer_update_start(&plane->layer); 557 + if (ret) 558 + return ret; 559 + 560 + atmel_hlcdc_plane_update_pos_and_size(plane, req); 561 + atmel_hlcdc_plane_update_general_settings(plane, req); 562 + atmel_hlcdc_plane_update_format(plane, req); 563 + atmel_hlcdc_plane_update_buffers(plane, req); 564 + 565 + atmel_hlcdc_layer_update_commit(&plane->layer); 566 + 567 + return 0; 568 + } 569 + 570 + int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p, 571 + struct drm_crtc *crtc, 572 + struct drm_framebuffer *fb, 573 + int crtc_x, int crtc_y, 574 + unsigned int crtc_w, 575 + unsigned int crtc_h, 576 + uint32_t src_x, uint32_t src_y, 577 + uint32_t src_w, uint32_t src_h, 578 + const struct drm_display_mode *mode) 579 + { 580 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 581 + struct atmel_hlcdc_plane_update_req req; 582 + int ret = 0; 583 + 584 + memset(&req, 0, sizeof(req)); 585 + req.crtc_x = crtc_x; 586 + req.crtc_y = crtc_y; 587 + req.crtc_w = crtc_w; 588 + req.crtc_h = crtc_h; 589 + req.src_x = src_x; 590 + req.src_y = src_y; 591 + req.src_w = src_w; 592 + req.src_h = src_h; 593 + req.fb = fb; 594 + 595 + ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req, mode); 596 + if (ret) 597 + return ret; 598 + 599 + if (!req.crtc_h || !req.crtc_w) 600 + return atmel_hlcdc_layer_disable(&plane->layer); 601 + 602 + return atmel_hlcdc_plane_apply_update_req(&plane->base, &req); 603 + } 604 + 605 + static int atmel_hlcdc_plane_update(struct drm_plane *p, 606 + struct drm_crtc *crtc, 607 + struct drm_framebuffer *fb, 608 + int crtc_x, int crtc_y, 609 + unsigned int crtc_w, unsigned int crtc_h, 610 + uint32_t src_x, uint32_t src_y, 611 + uint32_t src_w, uint32_t src_h) 612 + { 613 + return atmel_hlcdc_plane_update_with_mode(p, crtc, fb, crtc_x, crtc_y, 614 + crtc_w, crtc_h, src_x, src_y, 615 + src_w, src_h, &crtc->hwmode); 616 + } 617 + 618 + static int atmel_hlcdc_plane_disable(struct drm_plane *p) 619 + { 620 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 621 + 622 + return atmel_hlcdc_layer_disable(&plane->layer); 623 + } 624 + 625 + static void atmel_hlcdc_plane_destroy(struct drm_plane *p) 626 + { 627 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 628 + 629 + if (plane->base.fb) 630 + drm_framebuffer_unreference(plane->base.fb); 631 + 632 + atmel_hlcdc_layer_cleanup(p->dev, &plane->layer); 633 + 634 + drm_plane_cleanup(p); 635 + devm_kfree(p->dev->dev, plane); 636 + } 637 + 638 + static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane, 639 + u8 alpha) 640 + { 641 + atmel_hlcdc_layer_update_start(&plane->layer); 642 + atmel_hlcdc_layer_update_cfg(&plane->layer, 643 + plane->layer.desc->layout.general_config, 644 + ATMEL_HLCDC_LAYER_GA_MASK, 645 + alpha << ATMEL_HLCDC_LAYER_GA_SHIFT); 646 + atmel_hlcdc_layer_update_commit(&plane->layer); 647 + 648 + return 0; 649 + } 650 + 651 + static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane, 652 + unsigned int rotation) 653 + { 654 + plane->rotation = rotation; 655 + 656 + return 0; 657 + } 658 + 659 + static int atmel_hlcdc_plane_set_property(struct drm_plane *p, 660 + struct drm_property *property, 661 + uint64_t value) 662 + { 663 + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 664 + struct atmel_hlcdc_plane_properties *props = plane->properties; 665 + 666 + if (property == props->alpha) 667 + atmel_hlcdc_plane_set_alpha(plane, value); 668 + else if (property == props->rotation) 669 + atmel_hlcdc_plane_set_rotation(plane, value); 670 + else 671 + return -EINVAL; 672 + 673 + return 0; 674 + } 675 + 676 + static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, 677 + const struct atmel_hlcdc_layer_desc *desc, 678 + struct atmel_hlcdc_plane_properties *props) 679 + { 680 + struct regmap *regmap = plane->layer.hlcdc->regmap; 681 + 682 + if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || 683 + desc->type == ATMEL_HLCDC_CURSOR_LAYER) { 684 + drm_object_attach_property(&plane->base.base, 685 + props->alpha, 255); 686 + 687 + /* Set default alpha value */ 688 + regmap_update_bits(regmap, 689 + desc->regs_offset + 690 + ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer), 691 + ATMEL_HLCDC_LAYER_GA_MASK, 692 + ATMEL_HLCDC_LAYER_GA_MASK); 693 + } 694 + 695 + if (desc->layout.xstride && desc->layout.pstride) 696 + drm_object_attach_property(&plane->base.base, 697 + props->rotation, 698 + BIT(DRM_ROTATE_0)); 699 + 700 + if (desc->layout.csc) { 701 + /* 702 + * TODO: decare a "yuv-to-rgb-conv-factors" property to let 703 + * userspace modify these factors (using a BLOB property ?). 704 + */ 705 + regmap_write(regmap, 706 + desc->regs_offset + 707 + ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0), 708 + 0x4c900091); 709 + regmap_write(regmap, 710 + desc->regs_offset + 711 + ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1), 712 + 0x7a5f5090); 713 + regmap_write(regmap, 714 + desc->regs_offset + 715 + ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2), 716 + 0x40040890); 717 + } 718 + } 719 + 720 + static struct drm_plane_funcs layer_plane_funcs = { 721 + .update_plane = atmel_hlcdc_plane_update, 722 + .disable_plane = atmel_hlcdc_plane_disable, 723 + .set_property = atmel_hlcdc_plane_set_property, 724 + .destroy = atmel_hlcdc_plane_destroy, 725 + }; 726 + 727 + static struct atmel_hlcdc_plane * 728 + atmel_hlcdc_plane_create(struct drm_device *dev, 729 + const struct atmel_hlcdc_layer_desc *desc, 730 + struct atmel_hlcdc_plane_properties *props) 731 + { 732 + struct atmel_hlcdc_plane *plane; 733 + enum drm_plane_type type; 734 + int ret; 735 + 736 + plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); 737 + if (!plane) 738 + return ERR_PTR(-ENOMEM); 739 + 740 + ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc); 741 + if (ret) 742 + return ERR_PTR(ret); 743 + 744 + if (desc->type == ATMEL_HLCDC_BASE_LAYER) 745 + type = DRM_PLANE_TYPE_PRIMARY; 746 + else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER) 747 + type = DRM_PLANE_TYPE_CURSOR; 748 + else 749 + type = DRM_PLANE_TYPE_OVERLAY; 750 + 751 + ret = drm_universal_plane_init(dev, &plane->base, 0, 752 + &layer_plane_funcs, 753 + desc->formats->formats, 754 + desc->formats->nformats, type); 755 + if (ret) 756 + return ERR_PTR(ret); 757 + 758 + /* Set default property values*/ 759 + atmel_hlcdc_plane_init_properties(plane, desc, props); 760 + 761 + return plane; 762 + } 763 + 764 + static struct atmel_hlcdc_plane_properties * 765 + atmel_hlcdc_plane_create_properties(struct drm_device *dev) 766 + { 767 + struct atmel_hlcdc_plane_properties *props; 768 + 769 + props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL); 770 + if (!props) 771 + return ERR_PTR(-ENOMEM); 772 + 773 + props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255); 774 + if (!props->alpha) 775 + return ERR_PTR(-ENOMEM); 776 + 777 + props->rotation = drm_mode_create_rotation_property(dev, 778 + BIT(DRM_ROTATE_0) | 779 + BIT(DRM_ROTATE_90) | 780 + BIT(DRM_ROTATE_180) | 781 + BIT(DRM_ROTATE_270)); 782 + if (!props->rotation) 783 + return ERR_PTR(-ENOMEM); 784 + 785 + return props; 786 + } 787 + 788 + struct atmel_hlcdc_planes * 789 + atmel_hlcdc_create_planes(struct drm_device *dev) 790 + { 791 + struct atmel_hlcdc_dc *dc = dev->dev_private; 792 + struct atmel_hlcdc_plane_properties *props; 793 + struct atmel_hlcdc_planes *planes; 794 + const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; 795 + int nlayers = dc->desc->nlayers; 796 + int i; 797 + 798 + planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL); 799 + if (!planes) 800 + return ERR_PTR(-ENOMEM); 801 + 802 + for (i = 0; i < nlayers; i++) { 803 + if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER) 804 + planes->noverlays++; 805 + } 806 + 807 + if (planes->noverlays) { 808 + planes->overlays = devm_kzalloc(dev->dev, 809 + planes->noverlays * 810 + sizeof(*planes->overlays), 811 + GFP_KERNEL); 812 + if (!planes->overlays) 813 + return ERR_PTR(-ENOMEM); 814 + } 815 + 816 + props = atmel_hlcdc_plane_create_properties(dev); 817 + if (IS_ERR(props)) 818 + return ERR_CAST(props); 819 + 820 + planes->noverlays = 0; 821 + for (i = 0; i < nlayers; i++) { 822 + struct atmel_hlcdc_plane *plane; 823 + 824 + if (descs[i].type == ATMEL_HLCDC_PP_LAYER) 825 + continue; 826 + 827 + plane = atmel_hlcdc_plane_create(dev, &descs[i], props); 828 + if (IS_ERR(plane)) 829 + return ERR_CAST(plane); 830 + 831 + plane->properties = props; 832 + 833 + switch (descs[i].type) { 834 + case ATMEL_HLCDC_BASE_LAYER: 835 + if (planes->primary) 836 + return ERR_PTR(-EINVAL); 837 + planes->primary = plane; 838 + break; 839 + 840 + case ATMEL_HLCDC_OVERLAY_LAYER: 841 + planes->overlays[planes->noverlays++] = plane; 842 + break; 843 + 844 + case ATMEL_HLCDC_CURSOR_LAYER: 845 + if (planes->cursor) 846 + return ERR_PTR(-EINVAL); 847 + planes->cursor = plane; 848 + break; 849 + 850 + default: 851 + break; 852 + } 853 + } 854 + 855 + return planes; 856 + }