| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* Hisilicon Hibmc SoC drm driver |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Based on the bochs drm driver. |
|---|
| .. | .. |
|---|
| 8 | 9 | * Rongrong Zou <zourongrong@huawei.com> |
|---|
| 9 | 10 | * Rongrong Zou <zourongrong@gmail.com> |
|---|
| 10 | 11 | * Jianhua Li <lijianhua@huawei.com> |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 13 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 14 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 15 | | - * (at your option) any later version. |
|---|
| 16 | | - * |
|---|
| 17 | 12 | */ |
|---|
| 13 | + |
|---|
| 14 | +#include <linux/delay.h> |
|---|
| 18 | 15 | |
|---|
| 19 | 16 | #include <drm/drm_atomic.h> |
|---|
| 20 | 17 | #include <drm/drm_atomic_helper.h> |
|---|
| 21 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 22 | | -#include <drm/drm_plane_helper.h> |
|---|
| 18 | +#include <drm/drm_fourcc.h> |
|---|
| 19 | +#include <drm/drm_gem_vram_helper.h> |
|---|
| 20 | +#include <drm/drm_vblank.h> |
|---|
| 23 | 21 | |
|---|
| 24 | 22 | #include "hibmc_drm_drv.h" |
|---|
| 25 | 23 | #include "hibmc_drm_regs.h" |
|---|
| .. | .. |
|---|
| 39 | 37 | }; |
|---|
| 40 | 38 | |
|---|
| 41 | 39 | static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { |
|---|
| 40 | + {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, |
|---|
| 42 | 41 | {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, |
|---|
| 43 | 42 | {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, |
|---|
| 44 | 43 | {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, |
|---|
| .. | .. |
|---|
| 46 | 45 | {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, |
|---|
| 47 | 46 | {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, |
|---|
| 48 | 47 | {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, |
|---|
| 48 | + {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, |
|---|
| 49 | + {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, |
|---|
| 49 | 50 | {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, |
|---|
| 50 | 51 | {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, |
|---|
| 51 | 52 | {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, |
|---|
| .. | .. |
|---|
| 70 | 71 | return PTR_ERR(crtc_state); |
|---|
| 71 | 72 | |
|---|
| 72 | 73 | if (src_w != state->crtc_w || src_h != state->crtc_h) { |
|---|
| 73 | | - DRM_DEBUG_ATOMIC("scale not support\n"); |
|---|
| 74 | + drm_dbg_atomic(plane->dev, "scale not support\n"); |
|---|
| 74 | 75 | return -EINVAL; |
|---|
| 75 | 76 | } |
|---|
| 76 | 77 | |
|---|
| 77 | 78 | if (state->crtc_x < 0 || state->crtc_y < 0) { |
|---|
| 78 | | - DRM_DEBUG_ATOMIC("crtc_x/y of drm_plane state is invalid\n"); |
|---|
| 79 | + drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n"); |
|---|
| 79 | 80 | return -EINVAL; |
|---|
| 80 | 81 | } |
|---|
| 82 | + |
|---|
| 83 | + if (!crtc_state->enable) |
|---|
| 84 | + return 0; |
|---|
| 81 | 85 | |
|---|
| 82 | 86 | if (state->crtc_x + state->crtc_w > |
|---|
| 83 | 87 | crtc_state->adjusted_mode.hdisplay || |
|---|
| 84 | 88 | state->crtc_y + state->crtc_h > |
|---|
| 85 | 89 | crtc_state->adjusted_mode.vdisplay) { |
|---|
| 86 | | - DRM_DEBUG_ATOMIC("visible portion of plane is invalid\n"); |
|---|
| 90 | + drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n"); |
|---|
| 87 | 91 | return -EINVAL; |
|---|
| 88 | 92 | } |
|---|
| 89 | 93 | |
|---|
| 94 | + if (state->fb->pitches[0] % 128 != 0) { |
|---|
| 95 | + drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n"); |
|---|
| 96 | + return -EINVAL; |
|---|
| 97 | + } |
|---|
| 90 | 98 | return 0; |
|---|
| 91 | 99 | } |
|---|
| 92 | 100 | |
|---|
| .. | .. |
|---|
| 95 | 103 | { |
|---|
| 96 | 104 | struct drm_plane_state *state = plane->state; |
|---|
| 97 | 105 | u32 reg; |
|---|
| 98 | | - int ret; |
|---|
| 99 | | - u64 gpu_addr = 0; |
|---|
| 106 | + s64 gpu_addr = 0; |
|---|
| 100 | 107 | unsigned int line_l; |
|---|
| 101 | 108 | struct hibmc_drm_private *priv = plane->dev->dev_private; |
|---|
| 102 | | - struct hibmc_framebuffer *hibmc_fb; |
|---|
| 103 | | - struct hibmc_bo *bo; |
|---|
| 109 | + struct drm_gem_vram_object *gbo; |
|---|
| 104 | 110 | |
|---|
| 105 | 111 | if (!state->fb) |
|---|
| 106 | 112 | return; |
|---|
| 107 | 113 | |
|---|
| 108 | | - hibmc_fb = to_hibmc_framebuffer(state->fb); |
|---|
| 109 | | - bo = gem_to_hibmc_bo(hibmc_fb->obj); |
|---|
| 110 | | - ret = ttm_bo_reserve(&bo->bo, true, false, NULL); |
|---|
| 111 | | - if (ret) { |
|---|
| 112 | | - DRM_ERROR("failed to reserve ttm_bo: %d", ret); |
|---|
| 113 | | - return; |
|---|
| 114 | | - } |
|---|
| 114 | + gbo = drm_gem_vram_of_gem(state->fb->obj[0]); |
|---|
| 115 | 115 | |
|---|
| 116 | | - ret = hibmc_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); |
|---|
| 117 | | - ttm_bo_unreserve(&bo->bo); |
|---|
| 118 | | - if (ret) { |
|---|
| 119 | | - DRM_ERROR("failed to pin hibmc_bo: %d", ret); |
|---|
| 120 | | - return; |
|---|
| 121 | | - } |
|---|
| 116 | + gpu_addr = drm_gem_vram_offset(gbo); |
|---|
| 117 | + if (WARN_ON_ONCE(gpu_addr < 0)) |
|---|
| 118 | + return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */ |
|---|
| 122 | 119 | |
|---|
| 123 | 120 | writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS); |
|---|
| 124 | 121 | |
|---|
| 125 | 122 | reg = state->fb->width * (state->fb->format->cpp[0]); |
|---|
| 126 | | - /* now line_pad is 16 */ |
|---|
| 127 | | - reg = PADDING(16, reg); |
|---|
| 128 | 123 | |
|---|
| 129 | | - line_l = state->fb->width * state->fb->format->cpp[0]; |
|---|
| 130 | | - line_l = PADDING(16, line_l); |
|---|
| 124 | + line_l = state->fb->pitches[0]; |
|---|
| 131 | 125 | writel(HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_WIDTH, reg) | |
|---|
| 132 | 126 | HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_OFFS, line_l), |
|---|
| 133 | 127 | priv->mmio + HIBMC_CRT_FB_WIDTH); |
|---|
| .. | .. |
|---|
| 157 | 151 | }; |
|---|
| 158 | 152 | |
|---|
| 159 | 153 | static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = { |
|---|
| 154 | + .prepare_fb = drm_gem_vram_plane_helper_prepare_fb, |
|---|
| 155 | + .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb, |
|---|
| 160 | 156 | .atomic_check = hibmc_plane_atomic_check, |
|---|
| 161 | 157 | .atomic_update = hibmc_plane_atomic_update, |
|---|
| 162 | 158 | }; |
|---|
| 163 | 159 | |
|---|
| 164 | | -static struct drm_plane *hibmc_plane_init(struct hibmc_drm_private *priv) |
|---|
| 160 | +static void hibmc_crtc_dpms(struct drm_crtc *crtc, int dpms) |
|---|
| 165 | 161 | { |
|---|
| 166 | | - struct drm_device *dev = priv->dev; |
|---|
| 167 | | - struct drm_plane *plane; |
|---|
| 168 | | - int ret = 0; |
|---|
| 162 | + struct hibmc_drm_private *priv = crtc->dev->dev_private; |
|---|
| 163 | + unsigned int reg; |
|---|
| 169 | 164 | |
|---|
| 170 | | - plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); |
|---|
| 171 | | - if (!plane) { |
|---|
| 172 | | - DRM_ERROR("failed to alloc memory when init plane\n"); |
|---|
| 173 | | - return ERR_PTR(-ENOMEM); |
|---|
| 174 | | - } |
|---|
| 175 | | - /* |
|---|
| 176 | | - * plane init |
|---|
| 177 | | - * TODO: Now only support primary plane, overlay planes |
|---|
| 178 | | - * need to do. |
|---|
| 179 | | - */ |
|---|
| 180 | | - ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs, |
|---|
| 181 | | - channel_formats1, |
|---|
| 182 | | - ARRAY_SIZE(channel_formats1), |
|---|
| 183 | | - NULL, |
|---|
| 184 | | - DRM_PLANE_TYPE_PRIMARY, |
|---|
| 185 | | - NULL); |
|---|
| 186 | | - if (ret) { |
|---|
| 187 | | - DRM_ERROR("failed to init plane: %d\n", ret); |
|---|
| 188 | | - return ERR_PTR(ret); |
|---|
| 189 | | - } |
|---|
| 190 | | - |
|---|
| 191 | | - drm_plane_helper_add(plane, &hibmc_plane_helper_funcs); |
|---|
| 192 | | - return plane; |
|---|
| 165 | + reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); |
|---|
| 166 | + reg &= ~HIBMC_CRT_DISP_CTL_DPMS_MASK; |
|---|
| 167 | + reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_DPMS, dpms); |
|---|
| 168 | + reg &= ~HIBMC_CRT_DISP_CTL_TIMING_MASK; |
|---|
| 169 | + if (dpms == HIBMC_CRT_DPMS_ON) |
|---|
| 170 | + reg |= HIBMC_CRT_DISP_CTL_TIMING(1); |
|---|
| 171 | + writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); |
|---|
| 193 | 172 | } |
|---|
| 194 | 173 | |
|---|
| 195 | 174 | static void hibmc_crtc_atomic_enable(struct drm_crtc *crtc, |
|---|
| .. | .. |
|---|
| 208 | 187 | reg |= HIBMC_CURR_GATE_DISPLAY(1); |
|---|
| 209 | 188 | hibmc_set_current_gate(priv, reg); |
|---|
| 210 | 189 | drm_crtc_vblank_on(crtc); |
|---|
| 190 | + hibmc_crtc_dpms(crtc, HIBMC_CRT_DPMS_ON); |
|---|
| 211 | 191 | } |
|---|
| 212 | 192 | |
|---|
| 213 | 193 | static void hibmc_crtc_atomic_disable(struct drm_crtc *crtc, |
|---|
| .. | .. |
|---|
| 216 | 196 | unsigned int reg; |
|---|
| 217 | 197 | struct hibmc_drm_private *priv = crtc->dev->dev_private; |
|---|
| 218 | 198 | |
|---|
| 199 | + hibmc_crtc_dpms(crtc, HIBMC_CRT_DPMS_OFF); |
|---|
| 219 | 200 | drm_crtc_vblank_off(crtc); |
|---|
| 220 | 201 | |
|---|
| 221 | 202 | hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_SLEEP); |
|---|
| .. | .. |
|---|
| 227 | 208 | reg |= HIBMC_CURR_GATE_LOCALMEM(0); |
|---|
| 228 | 209 | reg |= HIBMC_CURR_GATE_DISPLAY(0); |
|---|
| 229 | 210 | hibmc_set_current_gate(priv, reg); |
|---|
| 211 | +} |
|---|
| 212 | + |
|---|
| 213 | +static enum drm_mode_status |
|---|
| 214 | +hibmc_crtc_mode_valid(struct drm_crtc *crtc, |
|---|
| 215 | + const struct drm_display_mode *mode) |
|---|
| 216 | +{ |
|---|
| 217 | + int i = 0; |
|---|
| 218 | + int vrefresh = drm_mode_vrefresh(mode); |
|---|
| 219 | + |
|---|
| 220 | + if (vrefresh < 59 || vrefresh > 61) |
|---|
| 221 | + return MODE_NOCLOCK; |
|---|
| 222 | + |
|---|
| 223 | + for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { |
|---|
| 224 | + if (hibmc_pll_table[i].hdisplay == mode->hdisplay && |
|---|
| 225 | + hibmc_pll_table[i].vdisplay == mode->vdisplay) |
|---|
| 226 | + return MODE_OK; |
|---|
| 227 | + } |
|---|
| 228 | + |
|---|
| 229 | + return MODE_BAD; |
|---|
| 230 | 230 | } |
|---|
| 231 | 231 | |
|---|
| 232 | 232 | static unsigned int format_pll_reg(void) |
|---|
| .. | .. |
|---|
| 443 | 443 | priv->mmio + HIBMC_RAW_INTERRUPT_EN); |
|---|
| 444 | 444 | } |
|---|
| 445 | 445 | |
|---|
| 446 | +static void hibmc_crtc_load_lut(struct drm_crtc *crtc) |
|---|
| 447 | +{ |
|---|
| 448 | + struct hibmc_drm_private *priv = crtc->dev->dev_private; |
|---|
| 449 | + void __iomem *mmio = priv->mmio; |
|---|
| 450 | + u16 *r, *g, *b; |
|---|
| 451 | + unsigned int reg; |
|---|
| 452 | + int i; |
|---|
| 453 | + |
|---|
| 454 | + r = crtc->gamma_store; |
|---|
| 455 | + g = r + crtc->gamma_size; |
|---|
| 456 | + b = g + crtc->gamma_size; |
|---|
| 457 | + |
|---|
| 458 | + for (i = 0; i < crtc->gamma_size; i++) { |
|---|
| 459 | + unsigned int offset = i << 2; |
|---|
| 460 | + u8 red = *r++ >> 8; |
|---|
| 461 | + u8 green = *g++ >> 8; |
|---|
| 462 | + u8 blue = *b++ >> 8; |
|---|
| 463 | + u32 rgb = (red << 16) | (green << 8) | blue; |
|---|
| 464 | + |
|---|
| 465 | + writel(rgb, mmio + HIBMC_CRT_PALETTE + offset); |
|---|
| 466 | + } |
|---|
| 467 | + |
|---|
| 468 | + reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); |
|---|
| 469 | + reg |= HIBMC_FIELD(HIBMC_CTL_DISP_CTL_GAMMA, 1); |
|---|
| 470 | + writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); |
|---|
| 471 | +} |
|---|
| 472 | + |
|---|
| 473 | +static int hibmc_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, |
|---|
| 474 | + u16 *blue, uint32_t size, |
|---|
| 475 | + struct drm_modeset_acquire_ctx *ctx) |
|---|
| 476 | +{ |
|---|
| 477 | + hibmc_crtc_load_lut(crtc); |
|---|
| 478 | + |
|---|
| 479 | + return 0; |
|---|
| 480 | +} |
|---|
| 481 | + |
|---|
| 446 | 482 | static const struct drm_crtc_funcs hibmc_crtc_funcs = { |
|---|
| 447 | 483 | .page_flip = drm_atomic_helper_page_flip, |
|---|
| 448 | 484 | .set_config = drm_atomic_helper_set_config, |
|---|
| .. | .. |
|---|
| 452 | 488 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
|---|
| 453 | 489 | .enable_vblank = hibmc_crtc_enable_vblank, |
|---|
| 454 | 490 | .disable_vblank = hibmc_crtc_disable_vblank, |
|---|
| 491 | + .gamma_set = hibmc_crtc_gamma_set, |
|---|
| 455 | 492 | }; |
|---|
| 456 | 493 | |
|---|
| 457 | 494 | static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = { |
|---|
| .. | .. |
|---|
| 460 | 497 | .atomic_flush = hibmc_crtc_atomic_flush, |
|---|
| 461 | 498 | .atomic_enable = hibmc_crtc_atomic_enable, |
|---|
| 462 | 499 | .atomic_disable = hibmc_crtc_atomic_disable, |
|---|
| 500 | + .mode_valid = hibmc_crtc_mode_valid, |
|---|
| 463 | 501 | }; |
|---|
| 464 | 502 | |
|---|
| 465 | 503 | int hibmc_de_init(struct hibmc_drm_private *priv) |
|---|
| 466 | 504 | { |
|---|
| 467 | 505 | struct drm_device *dev = priv->dev; |
|---|
| 468 | | - struct drm_crtc *crtc; |
|---|
| 469 | | - struct drm_plane *plane; |
|---|
| 506 | + struct drm_crtc *crtc = &priv->crtc; |
|---|
| 507 | + struct drm_plane *plane = &priv->primary_plane; |
|---|
| 470 | 508 | int ret; |
|---|
| 471 | 509 | |
|---|
| 472 | | - plane = hibmc_plane_init(priv); |
|---|
| 473 | | - if (IS_ERR(plane)) { |
|---|
| 474 | | - DRM_ERROR("failed to create plane: %ld\n", PTR_ERR(plane)); |
|---|
| 475 | | - return PTR_ERR(plane); |
|---|
| 510 | + ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs, |
|---|
| 511 | + channel_formats1, |
|---|
| 512 | + ARRAY_SIZE(channel_formats1), |
|---|
| 513 | + NULL, |
|---|
| 514 | + DRM_PLANE_TYPE_PRIMARY, |
|---|
| 515 | + NULL); |
|---|
| 516 | + |
|---|
| 517 | + if (ret) { |
|---|
| 518 | + drm_err(dev, "failed to init plane: %d\n", ret); |
|---|
| 519 | + return ret; |
|---|
| 476 | 520 | } |
|---|
| 477 | 521 | |
|---|
| 478 | | - crtc = devm_kzalloc(dev->dev, sizeof(*crtc), GFP_KERNEL); |
|---|
| 479 | | - if (!crtc) { |
|---|
| 480 | | - DRM_ERROR("failed to alloc memory when init crtc\n"); |
|---|
| 481 | | - return -ENOMEM; |
|---|
| 482 | | - } |
|---|
| 522 | + drm_plane_helper_add(plane, &hibmc_plane_helper_funcs); |
|---|
| 483 | 523 | |
|---|
| 484 | 524 | ret = drm_crtc_init_with_planes(dev, crtc, plane, |
|---|
| 485 | 525 | NULL, &hibmc_crtc_funcs, NULL); |
|---|
| 486 | 526 | if (ret) { |
|---|
| 487 | | - DRM_ERROR("failed to init crtc: %d\n", ret); |
|---|
| 527 | + drm_err(dev, "failed to init crtc: %d\n", ret); |
|---|
| 488 | 528 | return ret; |
|---|
| 489 | 529 | } |
|---|
| 490 | 530 | |
|---|
| 491 | 531 | ret = drm_mode_crtc_set_gamma_size(crtc, 256); |
|---|
| 492 | 532 | if (ret) { |
|---|
| 493 | | - DRM_ERROR("failed to set gamma size: %d\n", ret); |
|---|
| 533 | + drm_err(dev, "failed to set gamma size: %d\n", ret); |
|---|
| 494 | 534 | return ret; |
|---|
| 495 | 535 | } |
|---|
| 496 | 536 | drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs); |
|---|