1/* 2 * Copyright (c) 2018 Loongson Technology Co., Ltd. 3 * Authors: 4 * Chen Zhu <zhuchen@loongson.cn> 5 * Yaling Fang <fangyaling@loongson.cn> 6 * Dandan Zhang <zhangdandan@loongson.cn> 7 * Huacai Chen <chenhc@lemote.com> 8 * Jiaxun Yang <jiaxun.yang@flygoat.com> 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 15#include <linux/delay.h> 16#include <drm/drm_atomic_helper.h> 17#include <drm/drm_plane.h> 18#include <drm/drm_crtc_helper.h> 19#include <drm/drm_plane_helper.h> 20#include <drm/drm_fb_cma_helper.h> 21#include <drm/drm_fourcc.h> 22#include <drm/drm_gem_cma_helper.h> 23#include "loongson_drv.h" 24 25/** 26 * This file contains setup code for the CRTC 27 */ 28 29DEFINE_SPINLOCK(loongson_crtc_lock); 30 31static int loongson_crtc_enable_vblank(struct drm_crtc *crtc) 32{ 33 struct loongson_crtc *lcrtc = to_loongson_crtc(crtc); 34 struct loongson_drm_device *ldev = lcrtc->ldev; 35 36 if(lcrtc->crtc_id == 0) { 37 ldev->int_reg |= (BIT(INT_DVO0_FB_END) << 16); 38 } else { 39 ldev->int_reg |= (BIT(INT_DVO1_FB_END) << 16); 40 } 41 42 spin_lock(&loongson_reglock); 43 writel(ldev->int_reg, ldev->mmio + FB_INT_REG); 44 spin_unlock(&loongson_reglock); 45 46 return 0; 47} 48 49static void loongson_crtc_disable_vblank(struct drm_crtc *crtc) 50{ 51 struct loongson_crtc *lcrtc = to_loongson_crtc(crtc); 52 struct loongson_drm_device *ldev = lcrtc->ldev; 53 54 55 if(lcrtc->crtc_id == 0) { 56 ldev->int_reg &= (~BIT(INT_DVO0_FB_END) << 16); 57 } else { 58 ldev->int_reg &= (~BIT(INT_DVO1_FB_END) << 16); 59 } 60 61 spin_lock(&loongson_reglock); 62 writel(ldev->int_reg, ldev->mmio + FB_INT_REG); 63 spin_unlock(&loongson_reglock); 64} 65 66#define PLL_REF_CLK_MHZ 100 67#define PCLK_PRECISION_INDICATOR 10000 68 69/** 70 * cal_freq 71 * 72 * @pixclock: unsigned int 73 * @pll_config: point to the pix_pll structure 74 * 75 * Calculate frequency 76 */ 77static unsigned int cal_freq(unsigned int pixclock, struct pix_pll *pll_config) 78{ 79 int i, j, loopc_offset; 80 unsigned int refc_set[] = {4, 5, 3}; 81 unsigned int prec_set[] = {1, 5, 10, 50, 100}; /*in 1/PCLK_PRECISION_INDICATOR*/ 82 unsigned int pstdiv, loopc, refc; 83 unsigned int precision_req, precision; 84 unsigned int loopc_min, loopc_max, loopc_mid; 85 unsigned long long real_dvo, req_dvo; 86 87 /*try precision from high to low*/ 88 for (j = 0; j < sizeof(prec_set)/sizeof(int); j++){ 89 precision_req = prec_set[j]; 90 91 /*try each refc*/ 92 for (i = 0; i < sizeof(refc_set)/sizeof(int); i++) { 93 refc = refc_set[i]; 94 loopc_min = (1200 / PLL_REF_CLK_MHZ) * refc; /*1200 / (PLL_REF_CLK_MHZ / refc)*/ 95 loopc_max = (3200 / PLL_REF_CLK_MHZ) * refc; /*3200 / (PLL_REF_CLK_MHZ / refc)*/ 96 loopc_mid = (2200 / PLL_REF_CLK_MHZ) * refc; /*(loopc_min + loopc_max) / 2;*/ 97 loopc_offset = -1; 98 99 /*try each loopc*/ 100 for (loopc = loopc_mid; (loopc <= loopc_max) && (loopc >= loopc_min); loopc += loopc_offset) { 101 if(loopc_offset < 0) 102 loopc_offset = -loopc_offset; 103 else 104 loopc_offset = -(loopc_offset+1); 105 106 pstdiv = loopc * PLL_REF_CLK_MHZ * 1000 / refc / pixclock; 107 if((pstdiv > 127) || (pstdiv < 1)) 108 continue; 109 110 /*real_freq is float type which is not available, but read_freq * pstdiv is available.*/ 111 req_dvo = (pixclock * pstdiv); 112 real_dvo = (loopc * PLL_REF_CLK_MHZ * 1000 / refc); 113 precision = abs(real_dvo * PCLK_PRECISION_INDICATOR / req_dvo - PCLK_PRECISION_INDICATOR); 114 115 if(precision < precision_req){ 116 pll_config->l2_div = pstdiv; 117 pll_config->l1_loopc = loopc; 118 pll_config->l1_frefc = refc; 119 if(j > 1) 120 printk("Warning: PIX clock precision degraded to %d / %d\n", precision_req, PCLK_PRECISION_INDICATOR); 121 return 1; 122 } 123 } 124 } 125 } 126 return 0; 127} 128 129/** 130 * config_pll 131 * 132 * @pll_base: represent a long type 133 * @pll_cfg: point to the pix_pll srtucture 134 * 135 * Config pll apply to ls7a 136 */ 137static void config_pll(void *pll_base, struct pix_pll *pll_cfg) 138{ 139 unsigned long val; 140 141 /* clear sel_pll_out0 */ 142 val = readl(pll_base + 0x4); 143 val &= ~(1UL << 8); 144 writel(val, pll_base + 0x4); 145 /* set pll_pd */ 146 val = readl(pll_base + 0x4); 147 val |= (1UL << 13); 148 writel(val, pll_base + 0x4); 149 /* clear set_pll_param */ 150 val = readl(pll_base + 0x4); 151 val &= ~(1UL << 11); 152 writel(val, pll_base + 0x4); 153 /* clear old value & config new value */ 154 val = readl(pll_base + 0x4); 155 val &= ~(0x7fUL << 0); 156 val |= (pll_cfg->l1_frefc << 0); /* refc */ 157 writel(val, pll_base + 0x4); 158 val = readl(pll_base + 0x0); 159 val &= ~(0x7fUL << 0); 160 val |= (pll_cfg->l2_div << 0); /* div */ 161 val &= ~(0x1ffUL << 21); 162 val |= (pll_cfg->l1_loopc << 21);/* loopc */ 163 writel(val, pll_base + 0x0); 164 /* set set_pll_param */ 165 val = readl(pll_base + 0x4); 166 val |= (1UL << 11); 167 writel(val, pll_base + 0x4); 168 /* clear pll_pd */ 169 val = readl(pll_base + 0x4); 170 val &= ~(1UL << 13); 171 writel(val, pll_base + 0x4); 172 /* wait pll lock */ 173 while(!(readl(pll_base + 0x4) & 0x80)) 174 cpu_relax(); 175 /* set sel_pll_out0 */ 176 val = readl(pll_base + 0x4); 177 val |= (1UL << 8); 178 writel(val, pll_base + 0x4); 179} 180 181static void loongson_config_pll(int id, unsigned int pix_freq) 182{ 183 unsigned int out; 184 struct pix_pll pll_cfg; 185 186 out = cal_freq(pix_freq, &pll_cfg); 187 if (id == 0) 188 config_pll(LS7A_PIX0_PLL, &pll_cfg); 189 else 190 config_pll(LS7A_PIX1_PLL, &pll_cfg); 191} 192 193/** 194 * These provide the minimum set of functions required to handle a CRTC 195 * Each driver is responsible for filling out this structure at startup time 196 * 197 * The drm_crtc_funcs structure is the central CRTC management structure 198 * in the DRM. Each CRTC controls one or more connectors 199 */ 200static const struct drm_crtc_funcs loongson_swcursor_crtc_funcs = { 201 .destroy = drm_crtc_cleanup, 202 .set_config = drm_atomic_helper_set_config, 203 .page_flip = drm_atomic_helper_page_flip, 204 .reset = drm_atomic_helper_crtc_reset, 205 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 206 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 207 .enable_vblank = loongson_crtc_enable_vblank, 208 .disable_vblank = loongson_crtc_disable_vblank, 209}; 210 211static const struct drm_crtc_funcs loongson_hwcursor_crtc_funcs = { 212 .cursor_set2 = loongson_crtc_cursor_set2, 213 .cursor_move = loongson_crtc_cursor_move, 214 .destroy = drm_crtc_cleanup, 215 .set_config = drm_atomic_helper_set_config, 216 .page_flip = drm_atomic_helper_page_flip, 217 .reset = drm_atomic_helper_crtc_reset, 218 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 219 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 220 .enable_vblank = loongson_crtc_enable_vblank, 221 .disable_vblank = loongson_crtc_disable_vblank, 222}; 223 224static const uint32_t loongson_formats[] = { 225 DRM_FORMAT_RGB565, 226 DRM_FORMAT_RGB888, 227 DRM_FORMAT_XRGB8888, 228 DRM_FORMAT_ARGB8888, 229}; 230 231static const uint64_t loongson_format_modifiers[] = { 232 DRM_FORMAT_MOD_LINEAR, 233 DRM_FORMAT_MOD_INVALID 234}; 235 236static enum drm_mode_status loongson_crtc_mode_valid(struct drm_crtc *crtc, 237 const struct drm_display_mode *mode) 238{ 239 int id = crtc->index; 240 struct drm_device *dev = crtc->dev; 241 struct loongson_drm_device *ldev = (struct loongson_drm_device*)dev->dev_private; 242 243 if (mode->hdisplay > get_crtc_max_width(ldev, id)) 244 return MODE_BAD; 245 if (mode->vdisplay > get_crtc_max_height(ldev, id)) 246 return MODE_BAD; 247 if (ldev->num_crtc == 1) { 248 if (mode->hdisplay % 16) 249 return MODE_BAD; 250 } else { 251 if (mode->hdisplay % 64) 252 return MODE_BAD; 253 } 254 255 return MODE_OK; 256} 257 258u32 crtc_read(struct loongson_crtc *lcrtc, u32 offset) 259{ 260 struct loongson_drm_device *ldev = lcrtc->ldev; 261 return readl(ldev->mmio + offset + (lcrtc->crtc_id * CRTC_REG_OFFSET)); 262} 263 264void crtc_write(struct loongson_crtc *lcrtc, u32 offset, u32 val) 265{ 266 struct loongson_drm_device *ldev = lcrtc->ldev; 267 writel(val, ldev->mmio + offset + (lcrtc->crtc_id * CRTC_REG_OFFSET)); 268} 269 270static void loongson_crtc_mode_set_nofb(struct drm_crtc *crtc) 271{ 272 unsigned int hr, hss, hse, hfl; 273 unsigned int vr, vss, vse, vfl; 274 unsigned int pix_freq; 275 unsigned long flags; 276 struct loongson_crtc *lcrtc = to_loongson_crtc(crtc); 277 struct drm_display_mode *mode = &crtc->state->adjusted_mode; 278 279 hr = mode->hdisplay; 280 hss = mode->hsync_start; 281 hse = mode->hsync_end; 282 hfl = mode->htotal; 283 284 vr = mode->vdisplay; 285 vss = mode->vsync_start; 286 vse = mode->vsync_end; 287 vfl = mode->vtotal; 288 289 pix_freq = mode->clock; 290 291 DRM_DEBUG("crtc_id = %d, hr = %d, hss = %d, hse = %d, hfl = %d, vr = %d, vss = %d, vse = %d, vfl = %d, pix_freq = %d,\n", 292 lcrtc->crtc_id, hr, hss, hse, hfl, vr, vss, vse, vfl, pix_freq); 293 294 spin_lock_irqsave(&loongson_reglock, flags); 295 crtc_write(lcrtc, FB_DITCFG_DVO_REG, 0); 296 crtc_write(lcrtc, FB_DITTAB_LO_DVO_REG, 0); 297 crtc_write(lcrtc, FB_DITTAB_HI_DVO_REG, 0); 298 crtc_write(lcrtc, FB_PANCFG_DVO_REG, 0x80001311); 299 crtc_write(lcrtc, FB_PANTIM_DVO_REG, 0); 300 301 crtc_write(lcrtc, FB_HDISPLAY_DVO_REG, (mode->crtc_htotal << 16) | mode->crtc_hdisplay); 302 crtc_write(lcrtc, FB_HSYNC_DVO_REG, 0x40000000 | (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start); 303 304 crtc_write(lcrtc, FB_VDISPLAY_DVO_REG, (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); 305 crtc_write(lcrtc, FB_VSYNC_DVO_REG, 0x40000000 | (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start); 306 307 crtc_write(lcrtc, FB_STRI_DVO_REG, (crtc->primary->state->fb->pitches[0] + 255) & ~255); 308 309 DRM_DEBUG("Stride: %x\n",(crtc->primary->state->fb->pitches[0] + 255) & ~255); 310 311 switch (crtc->primary->state->fb->format->format) { 312 case DRM_FORMAT_RGB565: 313 lcrtc->cfg_reg |= 0x3; 314 crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg); 315 break; 316 case DRM_FORMAT_RGB888: 317 default: 318 lcrtc->cfg_reg |= 0x4; 319 crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg); 320 break; 321 } 322 spin_unlock_irqrestore(&loongson_reglock, flags); 323 324 loongson_config_pll(lcrtc->crtc_id, mode->clock); 325} 326 327static void loongson_crtc_atomic_enable(struct drm_crtc *crtc, 328 struct drm_crtc_state *old_state) 329{ 330 unsigned long flags; 331 struct loongson_crtc *lcrtc = to_loongson_crtc(crtc); 332 333 if (lcrtc->cfg_reg & CFG_ENABLE) 334 goto vblank_on; 335 336 lcrtc->cfg_reg |= CFG_ENABLE; 337 spin_lock_irqsave(&loongson_reglock, flags); 338 crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg); 339 spin_unlock_irqrestore(&loongson_reglock, flags); 340 341vblank_on: 342 drm_crtc_vblank_on(crtc); 343} 344 345static void loongson_crtc_atomic_disable(struct drm_crtc *crtc, 346 struct drm_crtc_state *old_state) 347{ 348 unsigned long flags; 349 struct loongson_crtc *lcrtc = to_loongson_crtc(crtc); 350 351 lcrtc->cfg_reg &= ~CFG_ENABLE; 352 spin_lock_irqsave(&loongson_reglock, flags); 353 crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg); 354 spin_unlock_irqrestore(&loongson_reglock, flags); 355 356 spin_lock_irq(&crtc->dev->event_lock); 357 if (crtc->state->event) { 358 drm_crtc_send_vblank_event(crtc, crtc->state->event); 359 crtc->state->event = NULL; 360 } 361 spin_unlock_irq(&crtc->dev->event_lock); 362 363 drm_crtc_vblank_off(crtc); 364} 365 366static void loongson_crtc_atomic_flush(struct drm_crtc *crtc, 367 struct drm_crtc_state *old_crtc_state) 368{ 369 struct drm_pending_vblank_event *event = crtc->state->event; 370 371 if (event) { 372 crtc->state->event = NULL; 373 374 spin_lock_irq(&crtc->dev->event_lock); 375 if (drm_crtc_vblank_get(crtc) == 0) 376 drm_crtc_arm_vblank_event(crtc, event); 377 else 378 drm_crtc_send_vblank_event(crtc, event); 379 spin_unlock_irq(&crtc->dev->event_lock); 380 } 381} 382 383static void loongson_plane_atomic_update(struct drm_plane *plane, 384 struct drm_plane_state *old_state) 385{ 386 int id, clonemode; 387 unsigned int pitch; 388 unsigned long flags; 389 struct loongson_crtc *lcrtc; 390 struct loongson_drm_device *ldev; 391 struct drm_plane_state *state = plane->state; 392 393 if (!state->crtc || !state->fb) 394 return; 395 396 pitch = state->fb->pitches[0]; 397 lcrtc = to_loongson_crtc(state->crtc); 398 ldev = lcrtc->ldev; 399 id = lcrtc->crtc_id; 400 clonemode = clone_mode(ldev); 401 402 /* CRTC1 cloned from CRTC0 in clone mode */ 403 if (clonemode) 404 ldev->lcrtc[1].cfg_reg |= CFG_PANELSWITCH; 405 else 406 ldev->lcrtc[1].cfg_reg &= ~CFG_PANELSWITCH; 407 408 spin_lock_irqsave(&loongson_reglock, flags); 409 crtc_write(lcrtc, FB_STRI_DVO_REG, (pitch + 255) & ~255); 410 if (crtc_read(lcrtc, FB_CFG_DVO_REG) & CFG_FBNUM) 411 crtc_write(lcrtc, FB_ADDR0_DVO_REG, drm_fb_cma_get_gem_addr(state->fb, state, 0)); 412 else 413 crtc_write(lcrtc, FB_ADDR1_DVO_REG, drm_fb_cma_get_gem_addr(state->fb, state, 0)); 414 415 lcrtc->cfg_reg |= CFG_ENABLE; 416 crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg | CFG_FBSWITCH); 417 if (clonemode) { 418 if (id) 419 crtc_write(&ldev->lcrtc[0], FB_CFG_DVO_REG, ldev->lcrtc[0].cfg_reg | CFG_ENABLE); 420 else 421 crtc_write(&ldev->lcrtc[1], FB_CFG_DVO_REG, ldev->lcrtc[1].cfg_reg | CFG_ENABLE); 422 } 423 spin_unlock_irqrestore(&loongson_reglock, flags); 424 425 udelay(2500); 426} 427 428/** 429 * These provide the minimum set of functions required to handle a CRTC 430 * 431 * The drm_crtc_helper_funcs is a helper operations for CRTC 432 */ 433static const struct drm_crtc_helper_funcs loongson_crtc_helper_funcs = { 434 .mode_valid = loongson_crtc_mode_valid, 435 .mode_set_nofb = loongson_crtc_mode_set_nofb, 436 .atomic_enable = loongson_crtc_atomic_enable, 437 .atomic_disable = loongson_crtc_atomic_disable, 438 .atomic_flush = loongson_crtc_atomic_flush, 439}; 440 441static void loongson_plane_destroy(struct drm_plane *plane) 442{ 443 drm_plane_cleanup(plane); 444} 445 446static bool loongson_format_mod_supported(struct drm_plane *plane, 447 uint32_t format, uint64_t modifier) 448{ 449 return (modifier == DRM_FORMAT_MOD_LINEAR); 450} 451 452static const struct drm_plane_funcs loongson_plane_funcs = { 453 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 454 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 455 .destroy = loongson_plane_destroy, 456 .disable_plane = drm_atomic_helper_disable_plane, 457 .reset = drm_atomic_helper_plane_reset, 458 .update_plane = drm_atomic_helper_update_plane, 459 .format_mod_supported = loongson_format_mod_supported, 460}; 461 462static const struct drm_plane_helper_funcs loongson_plane_helper_funcs = { 463 .atomic_update = loongson_plane_atomic_update, 464}; 465 466/** 467 * loongosn_crtc_init 468 * 469 * @ldev: point to the loongson_drm_device structure 470 * 471 * Init CRTC 472 */ 473int loongson_crtc_init(struct loongson_drm_device *ldev) 474{ 475 int i, ret; 476 extern bool hw_cursor; 477 478 for(i=0;i<ldev->num_crtc;i++){ 479 ldev->lcrtc[i].ldev = ldev; 480 ldev->lcrtc[i].crtc_id = i; 481 482 ldev->lcrtc[i].cfg_reg = CFG_RESET; 483 ldev->lcrtc[i].primary = devm_kzalloc(ldev->dev->dev, sizeof(*ldev->lcrtc[i].primary), GFP_KERNEL); 484 if (!ldev->lcrtc[i].primary) 485 return -ENOMEM; 486 487 ret = drm_universal_plane_init(ldev->dev, ldev->lcrtc[i].primary, BIT(i), &loongson_plane_funcs, 488 loongson_formats, ARRAY_SIZE(loongson_formats), 489 loongson_format_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL); 490 if (ret) 491 return ret; 492 493 drm_plane_helper_add(ldev->lcrtc[i].primary, &loongson_plane_helper_funcs); 494 495 if (hw_cursor) 496 ret = drm_crtc_init_with_planes(ldev->dev, &ldev->lcrtc[i].base,ldev->lcrtc[i].primary, 497 NULL, &loongson_hwcursor_crtc_funcs, NULL); 498 else 499 ret = drm_crtc_init_with_planes(ldev->dev, &ldev->lcrtc[i].base,ldev->lcrtc[i].primary, 500 NULL, &loongson_swcursor_crtc_funcs, NULL); 501 if (ret) { 502 loongson_plane_destroy(ldev->lcrtc[i].primary); 503 return ret; 504 } 505 drm_crtc_helper_add(&ldev->lcrtc[i].base, &loongson_crtc_helper_funcs); 506 } 507 508 return 0; 509} 510