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

drm/ingenic: Add support for OSD mode

All Ingenic SoCs starting from the JZ4725B support OSD mode.

In this mode, two separate planes can be used. They can have different
positions and sizes, and one can be overlayed on top of the other.

v2: Use fallthrough; instead of /* fall-through */

v3: - Add custom atomic_tail function to handle case where HW gives no
VBLANK
- Use regmap_set_bits() / regmap_clear_bits() when possible
- Use dma_hwdesc_f{0,1} fields in priv structure instead of array
- Use dmam_alloc_coherent() instead of dma_alloc_coherent()
- Use more meaningful 0xf0 / 0xf1 values as DMA descriptors IDs
- Add a bit more code comments

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-9-paul@crapouillou.net

+288 -50
+253 -50
drivers/gpu/drm/ingenic/ingenic-drm-drv.c
··· 43 43 44 44 struct jz_soc_info { 45 45 bool needs_dev_clk; 46 + bool has_osd; 46 47 unsigned int max_width, max_height; 47 48 }; 48 49 49 50 struct ingenic_drm { 50 51 struct drm_device drm; 51 - struct drm_plane primary; 52 + /* 53 + * f1 (aka. foreground1) is our primary plane, on top of which 54 + * f0 (aka. foreground0) can be overlayed. Z-order is fixed in 55 + * hardware and cannot be changed. 56 + */ 57 + struct drm_plane f0, f1; 52 58 struct drm_crtc crtc; 53 59 struct drm_encoder encoder; 54 60 ··· 63 57 struct clk *lcd_clk, *pix_clk; 64 58 const struct jz_soc_info *soc_info; 65 59 66 - struct ingenic_dma_hwdesc *dma_hwdesc; 67 - dma_addr_t dma_hwdesc_phys; 60 + struct ingenic_dma_hwdesc *dma_hwdesc_f0, *dma_hwdesc_f1; 61 + dma_addr_t dma_hwdesc_phys_f0, dma_hwdesc_phys_f1; 68 62 69 63 bool panel_is_sharp; 64 + bool no_vblank; 70 65 }; 71 66 72 67 static const u32 ingenic_drm_primary_formats[] = { ··· 97 90 .val_bits = 32, 98 91 .reg_stride = 4, 99 92 100 - .max_register = JZ_REG_LCD_CMD1, 93 + .max_register = JZ_REG_LCD_SIZE1, 101 94 .writeable_reg = ingenic_drm_writeable_reg, 102 95 }; 103 96 ··· 115 108 drm_encoder_get_priv(struct drm_encoder *encoder) 116 109 { 117 110 return container_of(encoder, struct ingenic_drm, encoder); 118 - } 119 - 120 - static inline struct ingenic_drm *drm_plane_get_priv(struct drm_plane *plane) 121 - { 122 - return container_of(plane, struct ingenic_drm, primary); 123 111 } 124 112 125 113 static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, ··· 187 185 regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1)); 188 186 regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16); 189 187 } 190 - } 191 188 192 - static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv, 193 - const struct drm_format_info *finfo) 194 - { 195 - unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; 196 - 197 - switch (finfo->format) { 198 - case DRM_FORMAT_XRGB1555: 199 - ctrl |= JZ_LCD_CTRL_RGB555; 200 - /* fall-through */ 201 - case DRM_FORMAT_RGB565: 202 - ctrl |= JZ_LCD_CTRL_BPP_15_16; 203 - break; 204 - case DRM_FORMAT_XRGB8888: 205 - ctrl |= JZ_LCD_CTRL_BPP_18_24; 206 - break; 207 - } 208 - 209 - regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, 210 - JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 | 211 - JZ_LCD_CTRL_BPP_MASK, ctrl); 189 + regmap_set_bits(priv->map, JZ_REG_LCD_CTRL, 190 + JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16); 212 191 } 213 192 214 193 static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, 215 194 struct drm_crtc_state *state) 216 195 { 217 196 struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 197 + struct drm_plane_state *f1_state, *f0_state; 218 198 long rate; 219 199 220 200 if (!drm_atomic_crtc_needs_modeset(state)) ··· 211 227 if (rate < 0) 212 228 return rate; 213 229 230 + if (priv->soc_info->has_osd) { 231 + f1_state = drm_atomic_get_plane_state(state->state, &priv->f1); 232 + f0_state = drm_atomic_get_plane_state(state->state, &priv->f0); 233 + 234 + /* If all the planes are disabled, we won't get a VBLANK IRQ */ 235 + priv->no_vblank = !f1_state->fb && !f0_state->fb; 236 + } 237 + 214 238 return 0; 215 239 } 216 240 ··· 228 236 struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 229 237 struct drm_crtc_state *state = crtc->state; 230 238 struct drm_pending_vblank_event *event = state->event; 231 - struct drm_framebuffer *drm_fb = crtc->primary->state->fb; 232 - const struct drm_format_info *finfo; 233 239 234 240 if (drm_atomic_crtc_needs_modeset(state)) { 235 - finfo = drm_format_info(drm_fb->format->format); 236 - 237 241 ingenic_drm_crtc_update_timings(priv, &state->mode); 238 - ingenic_drm_crtc_update_ctrl(priv, finfo); 239 242 240 243 clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000); 241 244 } ··· 247 260 } 248 261 } 249 262 263 + static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, 264 + struct drm_plane_state *state) 265 + { 266 + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); 267 + struct drm_crtc_state *crtc_state; 268 + struct drm_crtc *crtc = state->crtc ?: plane->state->crtc; 269 + int ret; 270 + 271 + if (!crtc) 272 + return 0; 273 + 274 + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); 275 + if (WARN_ON(!crtc_state)) 276 + return -EINVAL; 277 + 278 + ret = drm_atomic_helper_check_plane_state(state, crtc_state, 279 + DRM_PLANE_HELPER_NO_SCALING, 280 + DRM_PLANE_HELPER_NO_SCALING, 281 + priv->soc_info->has_osd, 282 + true); 283 + if (ret) 284 + return ret; 285 + 286 + /* 287 + * If OSD is not available, check that the width/height match. 288 + * Note that state->src_* are in 16.16 fixed-point format. 289 + */ 290 + if (!priv->soc_info->has_osd && 291 + (state->src_x != 0 || 292 + (state->src_w >> 16) != state->crtc_w || 293 + (state->src_h >> 16) != state->crtc_h)) 294 + return -EINVAL; 295 + 296 + /* 297 + * Require full modeset if enabling or disabling a plane, or changing 298 + * its position, size or depth. 299 + */ 300 + if (priv->soc_info->has_osd && 301 + (!plane->state->fb || !state->fb || 302 + plane->state->crtc_x != state->crtc_x || 303 + plane->state->crtc_y != state->crtc_y || 304 + plane->state->crtc_w != state->crtc_w || 305 + plane->state->crtc_h != state->crtc_h || 306 + plane->state->fb->format->format != state->fb->format->format)) 307 + crtc_state->mode_changed = true; 308 + 309 + return 0; 310 + } 311 + 312 + static void ingenic_drm_plane_enable(struct ingenic_drm *priv, 313 + struct drm_plane *plane) 314 + { 315 + unsigned int en_bit; 316 + 317 + if (priv->soc_info->has_osd) { 318 + if (plane->type == DRM_PLANE_TYPE_PRIMARY) 319 + en_bit = JZ_LCD_OSDC_F1EN; 320 + else 321 + en_bit = JZ_LCD_OSDC_F0EN; 322 + 323 + regmap_set_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); 324 + } 325 + } 326 + 327 + static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, 328 + struct drm_plane_state *old_state) 329 + { 330 + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); 331 + unsigned int en_bit; 332 + 333 + if (priv->soc_info->has_osd) { 334 + if (plane->type == DRM_PLANE_TYPE_PRIMARY) 335 + en_bit = JZ_LCD_OSDC_F1EN; 336 + else 337 + en_bit = JZ_LCD_OSDC_F0EN; 338 + 339 + regmap_clear_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); 340 + } 341 + } 342 + 343 + static void ingenic_drm_plane_config(struct ingenic_drm *priv, 344 + struct drm_plane *plane, u32 fourcc) 345 + { 346 + struct drm_plane_state *state = plane->state; 347 + unsigned int xy_reg, size_reg; 348 + unsigned int ctrl = 0; 349 + 350 + ingenic_drm_plane_enable(priv, plane); 351 + 352 + if (priv->soc_info->has_osd && 353 + plane->type == DRM_PLANE_TYPE_PRIMARY) { 354 + switch (fourcc) { 355 + case DRM_FORMAT_XRGB1555: 356 + ctrl |= JZ_LCD_OSDCTRL_RGB555; 357 + fallthrough; 358 + case DRM_FORMAT_RGB565: 359 + ctrl |= JZ_LCD_OSDCTRL_BPP_15_16; 360 + break; 361 + case DRM_FORMAT_XRGB8888: 362 + ctrl |= JZ_LCD_OSDCTRL_BPP_18_24; 363 + break; 364 + } 365 + 366 + regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, 367 + JZ_LCD_OSDCTRL_BPP_MASK, ctrl); 368 + } else { 369 + switch (fourcc) { 370 + case DRM_FORMAT_XRGB1555: 371 + ctrl |= JZ_LCD_CTRL_RGB555; 372 + fallthrough; 373 + case DRM_FORMAT_RGB565: 374 + ctrl |= JZ_LCD_CTRL_BPP_15_16; 375 + break; 376 + case DRM_FORMAT_XRGB8888: 377 + ctrl |= JZ_LCD_CTRL_BPP_18_24; 378 + break; 379 + } 380 + 381 + regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, 382 + JZ_LCD_CTRL_BPP_MASK, ctrl); 383 + } 384 + 385 + if (priv->soc_info->has_osd) { 386 + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { 387 + xy_reg = JZ_REG_LCD_XYP1; 388 + size_reg = JZ_REG_LCD_SIZE1; 389 + } else { 390 + xy_reg = JZ_REG_LCD_XYP0; 391 + size_reg = JZ_REG_LCD_SIZE0; 392 + } 393 + 394 + regmap_write(priv->map, xy_reg, 395 + state->crtc_x << JZ_LCD_XYP01_XPOS_LSB | 396 + state->crtc_y << JZ_LCD_XYP01_YPOS_LSB); 397 + regmap_write(priv->map, size_reg, 398 + state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB | 399 + state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB); 400 + } 401 + } 402 + 250 403 static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, 251 404 struct drm_plane_state *oldstate) 252 405 { 253 - struct ingenic_drm *priv = drm_plane_get_priv(plane); 406 + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); 254 407 struct drm_plane_state *state = plane->state; 408 + struct ingenic_dma_hwdesc *hwdesc; 255 409 unsigned int width, height, cpp; 256 410 dma_addr_t addr; 257 411 ··· 402 274 height = state->src_h >> 16; 403 275 cpp = state->fb->format->cpp[0]; 404 276 405 - priv->dma_hwdesc->addr = addr; 406 - priv->dma_hwdesc->cmd = width * height * cpp / 4; 407 - priv->dma_hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ; 277 + if (priv->soc_info->has_osd && plane->type == DRM_PLANE_TYPE_OVERLAY) 278 + hwdesc = priv->dma_hwdesc_f0; 279 + else 280 + hwdesc = priv->dma_hwdesc_f1; 281 + 282 + hwdesc->addr = addr; 283 + hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); 284 + 285 + if (drm_atomic_crtc_needs_modeset(state->crtc->state)) 286 + ingenic_drm_plane_config(priv, plane, 287 + state->fb->format->format); 408 288 } 409 289 } 410 290 ··· 496 360 } 497 361 } 498 362 363 + static void ingenic_drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) 364 + { 365 + /* 366 + * Just your regular drm_atomic_helper_commit_tail(), but only calls 367 + * drm_atomic_helper_wait_for_vblanks() if priv->no_vblank. 368 + */ 369 + struct drm_device *dev = old_state->dev; 370 + struct ingenic_drm *priv = drm_device_get_priv(dev); 371 + 372 + drm_atomic_helper_commit_modeset_disables(dev, old_state); 373 + 374 + drm_atomic_helper_commit_planes(dev, old_state, 0); 375 + 376 + drm_atomic_helper_commit_modeset_enables(dev, old_state); 377 + 378 + drm_atomic_helper_commit_hw_done(old_state); 379 + 380 + if (!priv->no_vblank) 381 + drm_atomic_helper_wait_for_vblanks(dev, old_state); 382 + 383 + drm_atomic_helper_cleanup_planes(dev, old_state); 384 + } 385 + 499 386 static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) 500 387 { 501 388 struct ingenic_drm *priv = drm_device_get_priv(arg); ··· 596 437 597 438 static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = { 598 439 .atomic_update = ingenic_drm_plane_atomic_update, 440 + .atomic_check = ingenic_drm_plane_atomic_check, 441 + .atomic_disable = ingenic_drm_plane_atomic_disable, 599 442 .prepare_fb = drm_gem_fb_prepare_fb, 600 443 }; 601 444 ··· 618 457 .output_poll_changed = drm_fb_helper_output_poll_changed, 619 458 .atomic_check = drm_atomic_helper_check, 620 459 .atomic_commit = drm_atomic_helper_commit, 460 + }; 461 + 462 + static struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = { 463 + .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail, 621 464 }; 622 465 623 466 static int ingenic_drm_probe(struct platform_device *pdev) ··· 663 498 drm->mode_config.max_width = soc_info->max_width; 664 499 drm->mode_config.max_height = 4095; 665 500 drm->mode_config.funcs = &ingenic_drm_mode_config_funcs; 501 + drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers; 666 502 667 503 base = devm_platform_ioremap_resource(pdev, 0); 668 504 if (IS_ERR(base)) { ··· 707 541 bridge = devm_drm_panel_bridge_add_typed(dev, panel, 708 542 DRM_MODE_CONNECTOR_DPI); 709 543 710 - priv->dma_hwdesc = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc), 711 - &priv->dma_hwdesc_phys, 712 - GFP_KERNEL); 713 - if (!priv->dma_hwdesc) 544 + priv->dma_hwdesc_f1 = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc_f1), 545 + &priv->dma_hwdesc_phys_f1, 546 + GFP_KERNEL); 547 + if (!priv->dma_hwdesc_f1) 714 548 return -ENOMEM; 715 549 716 - priv->dma_hwdesc->next = priv->dma_hwdesc_phys; 717 - priv->dma_hwdesc->id = 0xdeafbead; 550 + priv->dma_hwdesc_f1->next = priv->dma_hwdesc_phys_f1; 551 + priv->dma_hwdesc_f1->id = 0xf1; 718 552 719 - drm_plane_helper_add(&priv->primary, &ingenic_drm_plane_helper_funcs); 553 + if (priv->soc_info->has_osd) { 554 + priv->dma_hwdesc_f0 = dmam_alloc_coherent(dev, 555 + sizeof(*priv->dma_hwdesc_f0), 556 + &priv->dma_hwdesc_phys_f0, 557 + GFP_KERNEL); 558 + if (!priv->dma_hwdesc_f0) 559 + return -ENOMEM; 720 560 721 - ret = drm_universal_plane_init(drm, &priv->primary, 722 - 0, &ingenic_drm_primary_plane_funcs, 561 + priv->dma_hwdesc_f0->next = priv->dma_hwdesc_phys_f0; 562 + priv->dma_hwdesc_f0->id = 0xf0; 563 + } 564 + 565 + drm_plane_helper_add(&priv->f1, &ingenic_drm_plane_helper_funcs); 566 + 567 + ret = drm_universal_plane_init(drm, &priv->f1, 1, 568 + &ingenic_drm_primary_plane_funcs, 723 569 ingenic_drm_primary_formats, 724 570 ARRAY_SIZE(ingenic_drm_primary_formats), 725 571 NULL, DRM_PLANE_TYPE_PRIMARY, NULL); ··· 742 564 743 565 drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs); 744 566 745 - ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->primary, 567 + ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->f1, 746 568 NULL, &ingenic_drm_crtc_funcs, NULL); 747 569 if (ret) { 748 570 dev_err(dev, "Failed to init CRTC: %i\n", ret); 749 571 return ret; 572 + } 573 + 574 + if (soc_info->has_osd) { 575 + drm_plane_helper_add(&priv->f0, 576 + &ingenic_drm_plane_helper_funcs); 577 + 578 + ret = drm_universal_plane_init(drm, &priv->f0, 1, 579 + &ingenic_drm_primary_plane_funcs, 580 + ingenic_drm_primary_formats, 581 + ARRAY_SIZE(ingenic_drm_primary_formats), 582 + NULL, DRM_PLANE_TYPE_OVERLAY, 583 + NULL); 584 + if (ret) { 585 + dev_err(dev, "Failed to register overlay plane: %i\n", 586 + ret); 587 + return ret; 588 + } 750 589 } 751 590 752 591 priv->encoder.possible_crtcs = 1; ··· 827 632 } 828 633 829 634 /* Set address of our DMA descriptor chain */ 830 - regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys); 635 + regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys_f0); 636 + regmap_write(priv->map, JZ_REG_LCD_DA1, priv->dma_hwdesc_phys_f1); 637 + 638 + /* Enable OSD if available */ 639 + if (soc_info->has_osd) 640 + regmap_write(priv->map, JZ_REG_LCD_OSDC, JZ_LCD_OSDC_OSDEN); 831 641 832 642 ret = drm_dev_register(drm, 0); 833 643 if (ret) { ··· 868 668 869 669 static const struct jz_soc_info jz4740_soc_info = { 870 670 .needs_dev_clk = true, 671 + .has_osd = false, 871 672 .max_width = 800, 872 673 .max_height = 600, 873 674 }; 874 675 875 676 static const struct jz_soc_info jz4725b_soc_info = { 876 677 .needs_dev_clk = false, 678 + .has_osd = true, 877 679 .max_width = 800, 878 680 .max_height = 600, 879 681 }; 880 682 881 683 static const struct jz_soc_info jz4770_soc_info = { 882 684 .needs_dev_clk = false, 685 + .has_osd = true, 883 686 .max_width = 1280, 884 687 .max_height = 720, 885 688 };
+35
drivers/gpu/drm/ingenic/ingenic-drm.h
··· 30 30 #define JZ_REG_LCD_SA1 0x54 31 31 #define JZ_REG_LCD_FID1 0x58 32 32 #define JZ_REG_LCD_CMD1 0x5C 33 + #define JZ_REG_LCD_OSDC 0x100 34 + #define JZ_REG_LCD_OSDCTRL 0x104 35 + #define JZ_REG_LCD_OSDS 0x108 36 + #define JZ_REG_LCD_BGC 0x10c 37 + #define JZ_REG_LCD_KEY0 0x110 38 + #define JZ_REG_LCD_KEY1 0x114 39 + #define JZ_REG_LCD_ALPHA 0x118 40 + #define JZ_REG_LCD_IPUR 0x11c 41 + #define JZ_REG_LCD_XYP0 0x120 42 + #define JZ_REG_LCD_XYP1 0x124 43 + #define JZ_REG_LCD_SIZE0 0x128 44 + #define JZ_REG_LCD_SIZE1 0x12c 33 45 34 46 #define JZ_LCD_CFG_SLCD BIT(31) 35 47 #define JZ_LCD_CFG_PS_DISABLE BIT(23) ··· 134 122 #define JZ_LCD_STATE_EOF_IRQ BIT(5) 135 123 #define JZ_LCD_STATE_SOF_IRQ BIT(4) 136 124 #define JZ_LCD_STATE_DISABLED BIT(0) 125 + 126 + #define JZ_LCD_OSDC_OSDEN BIT(0) 127 + #define JZ_LCD_OSDC_F0EN BIT(3) 128 + #define JZ_LCD_OSDC_F1EN BIT(4) 129 + 130 + #define JZ_LCD_OSDCTRL_IPU BIT(15) 131 + #define JZ_LCD_OSDCTRL_RGB555 BIT(4) 132 + #define JZ_LCD_OSDCTRL_CHANGE BIT(3) 133 + #define JZ_LCD_OSDCTRL_BPP_15_16 0x4 134 + #define JZ_LCD_OSDCTRL_BPP_18_24 0x5 135 + #define JZ_LCD_OSDCTRL_BPP_30 0x7 136 + #define JZ_LCD_OSDCTRL_BPP_MASK (JZ_LCD_OSDCTRL_RGB555 | 0x7) 137 + 138 + #define JZ_LCD_OSDS_READY BIT(0) 139 + 140 + #define JZ_LCD_IPUR_IPUREN BIT(31) 141 + #define JZ_LCD_IPUR_IPUR_LSB 0 142 + 143 + #define JZ_LCD_XYP01_XPOS_LSB 0 144 + #define JZ_LCD_XYP01_YPOS_LSB 16 145 + 146 + #define JZ_LCD_SIZE01_WIDTH_LSB 0 147 + #define JZ_LCD_SIZE01_HEIGHT_LSB 16 137 148 138 149 #endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */