1 /* 2 * Copyright (C) STMicroelectronics SA 2014 3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 4 * Fabien Dessenne <fabien.dessenne@st.com> 5 * for STMicroelectronics. 6 * License terms: GNU General Public License (GPL), version 2 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/dma-mapping.h> 11 12 #include <drm/drm_fb_cma_helper.h> 13 #include <drm/drm_gem_cma_helper.h> 14 15 #include "sti_compositor.h" 16 #include "sti_gdp.h" 17 #include "sti_plane.h" 18 #include "sti_vtg.h" 19 20 #define ALPHASWITCH BIT(6) 21 #define ENA_COLOR_FILL BIT(8) 22 #define BIGNOTLITTLE BIT(23) 23 #define WAIT_NEXT_VSYNC BIT(31) 24 25 /* GDP color formats */ 26 #define GDP_RGB565 0x00 27 #define GDP_RGB888 0x01 28 #define GDP_RGB888_32 0x02 29 #define GDP_XBGR8888 (GDP_RGB888_32 | BIGNOTLITTLE | ALPHASWITCH) 30 #define GDP_ARGB8565 0x04 31 #define GDP_ARGB8888 0x05 32 #define GDP_ABGR8888 (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) 33 #define GDP_ARGB1555 0x06 34 #define GDP_ARGB4444 0x07 35 #define GDP_CLUT8 0x0B 36 #define GDP_YCBR888 0x10 37 #define GDP_YCBR422R 0x12 38 #define GDP_AYCBR8888 0x15 39 40 #define GAM_GDP_CTL_OFFSET 0x00 41 #define GAM_GDP_AGC_OFFSET 0x04 42 #define GAM_GDP_VPO_OFFSET 0x0C 43 #define GAM_GDP_VPS_OFFSET 0x10 44 #define GAM_GDP_PML_OFFSET 0x14 45 #define GAM_GDP_PMP_OFFSET 0x18 46 #define GAM_GDP_SIZE_OFFSET 0x1C 47 #define GAM_GDP_NVN_OFFSET 0x24 48 #define GAM_GDP_KEY1_OFFSET 0x28 49 #define GAM_GDP_KEY2_OFFSET 0x2C 50 #define GAM_GDP_PPT_OFFSET 0x34 51 #define GAM_GDP_CML_OFFSET 0x3C 52 #define GAM_GDP_MST_OFFSET 0x68 53 54 #define GAM_GDP_ALPHARANGE_255 BIT(5) 55 #define GAM_GDP_AGC_FULL_RANGE 0x00808080 56 #define GAM_GDP_PPT_IGNORE (BIT(1) | BIT(0)) 57 #define GAM_GDP_SIZE_MAX 0x7FF 58 59 #define GDP_NODE_NB_BANK 2 60 #define GDP_NODE_PER_FIELD 2 61 62 struct sti_gdp_node { 63 u32 gam_gdp_ctl; 64 u32 gam_gdp_agc; 65 u32 reserved1; 66 u32 gam_gdp_vpo; 67 u32 gam_gdp_vps; 68 u32 gam_gdp_pml; 69 u32 gam_gdp_pmp; 70 u32 gam_gdp_size; 71 u32 reserved2; 72 u32 gam_gdp_nvn; 73 u32 gam_gdp_key1; 74 u32 gam_gdp_key2; 75 u32 reserved3; 76 u32 gam_gdp_ppt; 77 u32 reserved4; 78 u32 gam_gdp_cml; 79 }; 80 81 struct sti_gdp_node_list { 82 struct sti_gdp_node *top_field; 83 dma_addr_t top_field_paddr; 84 struct sti_gdp_node *btm_field; 85 dma_addr_t btm_field_paddr; 86 }; 87 88 /** 89 * STI GDP structure 90 * 91 * @sti_plane: sti_plane structure 92 * @dev: driver device 93 * @regs: gdp registers 94 * @clk_pix: pixel clock for the current gdp 95 * @clk_main_parent: gdp parent clock if main path used 96 * @clk_aux_parent: gdp parent clock if aux path used 97 * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification 98 * @is_curr_top: true if the current node processed is the top field 99 * @node_list: array of node list 100 */ 101 struct sti_gdp { 102 struct sti_plane plane; 103 struct device *dev; 104 void __iomem *regs; 105 struct clk *clk_pix; 106 struct clk *clk_main_parent; 107 struct clk *clk_aux_parent; 108 struct notifier_block vtg_field_nb; 109 bool is_curr_top; 110 struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; 111 }; 112 113 #define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) 114 115 static const uint32_t gdp_supported_formats[] = { 116 DRM_FORMAT_XRGB8888, 117 DRM_FORMAT_XBGR8888, 118 DRM_FORMAT_ARGB8888, 119 DRM_FORMAT_ABGR8888, 120 DRM_FORMAT_ARGB4444, 121 DRM_FORMAT_ARGB1555, 122 DRM_FORMAT_RGB565, 123 DRM_FORMAT_RGB888, 124 DRM_FORMAT_AYUV, 125 DRM_FORMAT_YUV444, 126 DRM_FORMAT_VYUY, 127 DRM_FORMAT_C8, 128 }; 129 130 static int sti_gdp_fourcc2format(int fourcc) 131 { 132 switch (fourcc) { 133 case DRM_FORMAT_XRGB8888: 134 return GDP_RGB888_32; 135 case DRM_FORMAT_XBGR8888: 136 return GDP_XBGR8888; 137 case DRM_FORMAT_ARGB8888: 138 return GDP_ARGB8888; 139 case DRM_FORMAT_ABGR8888: 140 return GDP_ABGR8888; 141 case DRM_FORMAT_ARGB4444: 142 return GDP_ARGB4444; 143 case DRM_FORMAT_ARGB1555: 144 return GDP_ARGB1555; 145 case DRM_FORMAT_RGB565: 146 return GDP_RGB565; 147 case DRM_FORMAT_RGB888: 148 return GDP_RGB888; 149 case DRM_FORMAT_AYUV: 150 return GDP_AYCBR8888; 151 case DRM_FORMAT_YUV444: 152 return GDP_YCBR888; 153 case DRM_FORMAT_VYUY: 154 return GDP_YCBR422R; 155 case DRM_FORMAT_C8: 156 return GDP_CLUT8; 157 } 158 return -1; 159 } 160 161 static int sti_gdp_get_alpharange(int format) 162 { 163 switch (format) { 164 case GDP_ARGB8565: 165 case GDP_ARGB8888: 166 case GDP_AYCBR8888: 167 case GDP_ABGR8888: 168 return GAM_GDP_ALPHARANGE_255; 169 } 170 return 0; 171 } 172 173 /** 174 * sti_gdp_get_free_nodes 175 * @gdp: gdp pointer 176 * 177 * Look for a GDP node list that is not currently read by the HW. 178 * 179 * RETURNS: 180 * Pointer to the free GDP node list 181 */ 182 static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_gdp *gdp) 183 { 184 int hw_nvn; 185 unsigned int i; 186 187 hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET); 188 if (!hw_nvn) 189 goto end; 190 191 for (i = 0; i < GDP_NODE_NB_BANK; i++) 192 if ((hw_nvn != gdp->node_list[i].btm_field_paddr) && 193 (hw_nvn != gdp->node_list[i].top_field_paddr)) 194 return &gdp->node_list[i]; 195 196 /* in hazardious cases restart with the first node */ 197 DRM_ERROR("inconsistent NVN for %s: 0x%08X\n", 198 sti_plane_to_str(&gdp->plane), hw_nvn); 199 200 end: 201 return &gdp->node_list[0]; 202 } 203 204 /** 205 * sti_gdp_get_current_nodes 206 * @gdp: gdp pointer 207 * 208 * Look for GDP nodes that are currently read by the HW. 209 * 210 * RETURNS: 211 * Pointer to the current GDP node list 212 */ 213 static 214 struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_gdp *gdp) 215 { 216 int hw_nvn; 217 unsigned int i; 218 219 hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET); 220 if (!hw_nvn) 221 goto end; 222 223 for (i = 0; i < GDP_NODE_NB_BANK; i++) 224 if ((hw_nvn == gdp->node_list[i].btm_field_paddr) || 225 (hw_nvn == gdp->node_list[i].top_field_paddr)) 226 return &gdp->node_list[i]; 227 228 end: 229 DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n", 230 hw_nvn, sti_plane_to_str(&gdp->plane)); 231 232 return NULL; 233 } 234 235 /** 236 * sti_gdp_disable 237 * @gdp: gdp pointer 238 * 239 * Disable a GDP. 240 */ 241 static void sti_gdp_disable(struct sti_gdp *gdp) 242 { 243 struct drm_plane *drm_plane = &gdp->plane.drm_plane; 244 struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); 245 struct sti_compositor *compo = dev_get_drvdata(gdp->dev); 246 unsigned int i; 247 248 DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane)); 249 250 /* Set the nodes as 'to be ignored on mixer' */ 251 for (i = 0; i < GDP_NODE_NB_BANK; i++) { 252 gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; 253 gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; 254 } 255 256 if (sti_vtg_unregister_client(mixer->id == STI_MIXER_MAIN ? 257 compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb)) 258 DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); 259 260 if (gdp->clk_pix) 261 clk_disable_unprepare(gdp->clk_pix); 262 263 gdp->plane.status = STI_PLANE_DISABLED; 264 } 265 266 /** 267 * sti_gdp_field_cb 268 * @nb: notifier block 269 * @event: event message 270 * @data: private data 271 * 272 * Handle VTG top field and bottom field event. 273 * 274 * RETURNS: 275 * 0 on success. 276 */ 277 int sti_gdp_field_cb(struct notifier_block *nb, 278 unsigned long event, void *data) 279 { 280 struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb); 281 282 if (gdp->plane.status == STI_PLANE_FLUSHING) { 283 /* disable need to be synchronize on vsync event */ 284 DRM_DEBUG_DRIVER("Vsync event received => disable %s\n", 285 sti_plane_to_str(&gdp->plane)); 286 287 sti_gdp_disable(gdp); 288 } 289 290 switch (event) { 291 case VTG_TOP_FIELD_EVENT: 292 gdp->is_curr_top = true; 293 break; 294 case VTG_BOTTOM_FIELD_EVENT: 295 gdp->is_curr_top = false; 296 break; 297 default: 298 DRM_ERROR("unsupported event: %lu\n", event); 299 break; 300 } 301 302 return 0; 303 } 304 305 static void sti_gdp_init(struct sti_gdp *gdp) 306 { 307 struct device_node *np = gdp->dev->of_node; 308 dma_addr_t dma_addr; 309 void *base; 310 unsigned int i, size; 311 312 /* Allocate all the nodes within a single memory page */ 313 size = sizeof(struct sti_gdp_node) * 314 GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; 315 base = dma_alloc_wc(gdp->dev, size, &dma_addr, GFP_KERNEL | GFP_DMA); 316 317 if (!base) { 318 DRM_ERROR("Failed to allocate memory for GDP node\n"); 319 return; 320 } 321 memset(base, 0, size); 322 323 for (i = 0; i < GDP_NODE_NB_BANK; i++) { 324 if (dma_addr & 0xF) { 325 DRM_ERROR("Mem alignment failed\n"); 326 return; 327 } 328 gdp->node_list[i].top_field = base; 329 gdp->node_list[i].top_field_paddr = dma_addr; 330 331 DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base); 332 base += sizeof(struct sti_gdp_node); 333 dma_addr += sizeof(struct sti_gdp_node); 334 335 if (dma_addr & 0xF) { 336 DRM_ERROR("Mem alignment failed\n"); 337 return; 338 } 339 gdp->node_list[i].btm_field = base; 340 gdp->node_list[i].btm_field_paddr = dma_addr; 341 DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base); 342 base += sizeof(struct sti_gdp_node); 343 dma_addr += sizeof(struct sti_gdp_node); 344 } 345 346 if (of_device_is_compatible(np, "st,stih407-compositor")) { 347 /* GDP of STiH407 chip have its own pixel clock */ 348 char *clk_name; 349 350 switch (gdp->plane.desc) { 351 case STI_GDP_0: 352 clk_name = "pix_gdp1"; 353 break; 354 case STI_GDP_1: 355 clk_name = "pix_gdp2"; 356 break; 357 case STI_GDP_2: 358 clk_name = "pix_gdp3"; 359 break; 360 case STI_GDP_3: 361 clk_name = "pix_gdp4"; 362 break; 363 default: 364 DRM_ERROR("GDP id not recognized\n"); 365 return; 366 } 367 368 gdp->clk_pix = devm_clk_get(gdp->dev, clk_name); 369 if (IS_ERR(gdp->clk_pix)) 370 DRM_ERROR("Cannot get %s clock\n", clk_name); 371 372 gdp->clk_main_parent = devm_clk_get(gdp->dev, "main_parent"); 373 if (IS_ERR(gdp->clk_main_parent)) 374 DRM_ERROR("Cannot get main_parent clock\n"); 375 376 gdp->clk_aux_parent = devm_clk_get(gdp->dev, "aux_parent"); 377 if (IS_ERR(gdp->clk_aux_parent)) 378 DRM_ERROR("Cannot get aux_parent clock\n"); 379 } 380 } 381 382 static void sti_gdp_atomic_update(struct drm_plane *drm_plane, 383 struct drm_plane_state *oldstate) 384 { 385 struct drm_plane_state *state = drm_plane->state; 386 struct sti_plane *plane = to_sti_plane(drm_plane); 387 struct sti_gdp *gdp = to_sti_gdp(plane); 388 struct drm_crtc *crtc = state->crtc; 389 struct sti_compositor *compo = dev_get_drvdata(gdp->dev); 390 struct drm_framebuffer *fb = state->fb; 391 bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false; 392 struct sti_mixer *mixer; 393 struct drm_display_mode *mode; 394 int dst_x, dst_y, dst_w, dst_h; 395 int src_x, src_y, src_w, src_h; 396 struct drm_gem_cma_object *cma_obj; 397 struct sti_gdp_node_list *list; 398 struct sti_gdp_node_list *curr_list; 399 struct sti_gdp_node *top_field, *btm_field; 400 u32 dma_updated_top; 401 u32 dma_updated_btm; 402 int format; 403 unsigned int depth, bpp; 404 u32 ydo, xdo, yds, xds; 405 int res; 406 407 /* Manage the case where crtc is null (disabled) */ 408 if (!crtc) 409 return; 410 411 mixer = to_sti_mixer(crtc); 412 mode = &crtc->mode; 413 dst_x = state->crtc_x; 414 dst_y = state->crtc_y; 415 dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x); 416 dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y); 417 /* src_x are in 16.16 format */ 418 src_x = state->src_x >> 16; 419 src_y = state->src_y >> 16; 420 src_w = state->src_w >> 16; 421 src_h = state->src_h >> 16; 422 423 DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", 424 crtc->base.id, sti_mixer_to_str(mixer), 425 drm_plane->base.id, sti_plane_to_str(plane)); 426 DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", 427 sti_plane_to_str(plane), 428 dst_w, dst_h, dst_x, dst_y, 429 src_w, src_h, src_x, src_y); 430 431 list = sti_gdp_get_free_nodes(gdp); 432 top_field = list->top_field; 433 btm_field = list->btm_field; 434 435 dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__, 436 sti_plane_to_str(plane), top_field, btm_field); 437 438 /* build the top field */ 439 top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; 440 top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; 441 format = sti_gdp_fourcc2format(fb->pixel_format); 442 if (format == -1) { 443 DRM_ERROR("Format not supported by GDP %.4s\n", 444 (char *)&fb->pixel_format); 445 return; 446 } 447 top_field->gam_gdp_ctl |= format; 448 top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); 449 top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; 450 451 cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 452 if (!cma_obj) { 453 DRM_ERROR("Can't get CMA GEM object for fb\n"); 454 return; 455 } 456 457 DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, 458 (char *)&fb->pixel_format, 459 (unsigned long)cma_obj->paddr); 460 461 /* pixel memory location */ 462 drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); 463 top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0]; 464 top_field->gam_gdp_pml += src_x * (bpp >> 3); 465 top_field->gam_gdp_pml += src_y * fb->pitches[0]; 466 467 /* input parameters */ 468 top_field->gam_gdp_pmp = fb->pitches[0]; 469 top_field->gam_gdp_size = clamp_val(src_h, 0, GAM_GDP_SIZE_MAX) << 16 | 470 clamp_val(src_w, 0, GAM_GDP_SIZE_MAX); 471 472 /* output parameters */ 473 ydo = sti_vtg_get_line_number(*mode, dst_y); 474 yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1); 475 xdo = sti_vtg_get_pixel_number(*mode, dst_x); 476 xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1); 477 top_field->gam_gdp_vpo = (ydo << 16) | xdo; 478 top_field->gam_gdp_vps = (yds << 16) | xds; 479 480 /* Same content and chained together */ 481 memcpy(btm_field, top_field, sizeof(*btm_field)); 482 top_field->gam_gdp_nvn = list->btm_field_paddr; 483 btm_field->gam_gdp_nvn = list->top_field_paddr; 484 485 /* Interlaced mode */ 486 if (mode->flags & DRM_MODE_FLAG_INTERLACE) 487 btm_field->gam_gdp_pml = top_field->gam_gdp_pml + 488 fb->pitches[0]; 489 490 if (first_prepare) { 491 /* Register gdp callback */ 492 if (sti_vtg_register_client(mixer->id == STI_MIXER_MAIN ? 493 compo->vtg_main : compo->vtg_aux, 494 &gdp->vtg_field_nb, crtc)) { 495 DRM_ERROR("Cannot register VTG notifier\n"); 496 return; 497 } 498 499 /* Set and enable gdp clock */ 500 if (gdp->clk_pix) { 501 struct clk *clkp; 502 int rate = mode->clock * 1000; 503 504 /* According to the mixer used, the gdp pixel clock 505 * should have a different parent clock. */ 506 if (mixer->id == STI_MIXER_MAIN) 507 clkp = gdp->clk_main_parent; 508 else 509 clkp = gdp->clk_aux_parent; 510 511 if (clkp) 512 clk_set_parent(gdp->clk_pix, clkp); 513 514 res = clk_set_rate(gdp->clk_pix, rate); 515 if (res < 0) { 516 DRM_ERROR("Cannot set rate (%dHz) for gdp\n", 517 rate); 518 return; 519 } 520 521 if (clk_prepare_enable(gdp->clk_pix)) { 522 DRM_ERROR("Failed to prepare/enable gdp\n"); 523 return; 524 } 525 } 526 } 527 528 /* Update the NVN field of the 'right' field of the current GDP node 529 * (being used by the HW) with the address of the updated ('free') top 530 * field GDP node. 531 * - In interlaced mode the 'right' field is the bottom field as we 532 * update frames starting from their top field 533 * - In progressive mode, we update both bottom and top fields which 534 * are equal nodes. 535 * At the next VSYNC, the updated node list will be used by the HW. 536 */ 537 curr_list = sti_gdp_get_current_nodes(gdp); 538 dma_updated_top = list->top_field_paddr; 539 dma_updated_btm = list->btm_field_paddr; 540 541 dev_dbg(gdp->dev, "Current NVN:0x%X\n", 542 readl(gdp->regs + GAM_GDP_NVN_OFFSET)); 543 dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n", 544 (unsigned long)cma_obj->paddr, 545 readl(gdp->regs + GAM_GDP_PML_OFFSET)); 546 547 if (!curr_list) { 548 /* First update or invalid node should directly write in the 549 * hw register */ 550 DRM_DEBUG_DRIVER("%s first update (or invalid node)", 551 sti_plane_to_str(plane)); 552 553 writel(gdp->is_curr_top ? 554 dma_updated_btm : dma_updated_top, 555 gdp->regs + GAM_GDP_NVN_OFFSET); 556 goto end; 557 } 558 559 if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 560 if (gdp->is_curr_top) { 561 /* Do not update in the middle of the frame, but 562 * postpone the update after the bottom field has 563 * been displayed */ 564 curr_list->btm_field->gam_gdp_nvn = dma_updated_top; 565 } else { 566 /* Direct update to avoid one frame delay */ 567 writel(dma_updated_top, 568 gdp->regs + GAM_GDP_NVN_OFFSET); 569 } 570 } else { 571 /* Direct update for progressive to avoid one frame delay */ 572 writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET); 573 } 574 575 end: 576 plane->status = STI_PLANE_UPDATED; 577 } 578 579 static void sti_gdp_atomic_disable(struct drm_plane *drm_plane, 580 struct drm_plane_state *oldstate) 581 { 582 struct sti_plane *plane = to_sti_plane(drm_plane); 583 struct sti_mixer *mixer = to_sti_mixer(drm_plane->crtc); 584 585 if (!drm_plane->crtc) { 586 DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", 587 drm_plane->base.id); 588 return; 589 } 590 591 DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", 592 drm_plane->crtc->base.id, sti_mixer_to_str(mixer), 593 drm_plane->base.id, sti_plane_to_str(plane)); 594 595 plane->status = STI_PLANE_DISABLING; 596 } 597 598 static const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { 599 .atomic_update = sti_gdp_atomic_update, 600 .atomic_disable = sti_gdp_atomic_disable, 601 }; 602 603 struct drm_plane *sti_gdp_create(struct drm_device *drm_dev, 604 struct device *dev, int desc, 605 void __iomem *baseaddr, 606 unsigned int possible_crtcs, 607 enum drm_plane_type type) 608 { 609 struct sti_gdp *gdp; 610 int res; 611 612 gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL); 613 if (!gdp) { 614 DRM_ERROR("Failed to allocate memory for GDP\n"); 615 return NULL; 616 } 617 618 gdp->dev = dev; 619 gdp->regs = baseaddr; 620 gdp->plane.desc = desc; 621 gdp->plane.status = STI_PLANE_DISABLED; 622 623 gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb; 624 625 sti_gdp_init(gdp); 626 627 res = drm_universal_plane_init(drm_dev, &gdp->plane.drm_plane, 628 possible_crtcs, 629 &sti_plane_helpers_funcs, 630 gdp_supported_formats, 631 ARRAY_SIZE(gdp_supported_formats), 632 type, NULL); 633 if (res) { 634 DRM_ERROR("Failed to initialize universal plane\n"); 635 goto err; 636 } 637 638 drm_plane_helper_add(&gdp->plane.drm_plane, &sti_gdp_helpers_funcs); 639 640 sti_plane_init_property(&gdp->plane, type); 641 642 return &gdp->plane.drm_plane; 643 644 err: 645 devm_kfree(dev, gdp); 646 return NULL; 647 } 648