1026 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2023 Loongson Technology Corporation Limited4 */5 6#include <linux/debugfs.h>7#include <linux/delay.h>8 9#include <drm/drm_atomic.h>10#include <drm/drm_atomic_helper.h>11#include <drm/drm_debugfs.h>12#include <drm/drm_vblank.h>13 14#include "lsdc_drv.h"15 16/*17 * After the CRTC soft reset, the vblank counter would be reset to zero.18 * But the address and other settings in the CRTC register remain the same19 * as before.20 */21 22static void lsdc_crtc0_soft_reset(struct lsdc_crtc *lcrtc)23{24 struct lsdc_device *ldev = lcrtc->ldev;25 u32 val;26 27 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);28 29 val &= CFG_VALID_BITS_MASK;30 31 /* Soft reset bit, active low */32 val &= ~CFG_RESET_N;33 34 val &= ~CFG_PIX_FMT_MASK;35 36 lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);37 38 udelay(1);39 40 val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE;41 42 lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);43 44 /* Wait about a vblank time */45 mdelay(20);46}47 48static void lsdc_crtc1_soft_reset(struct lsdc_crtc *lcrtc)49{50 struct lsdc_device *ldev = lcrtc->ldev;51 u32 val;52 53 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);54 55 val &= CFG_VALID_BITS_MASK;56 57 /* Soft reset bit, active low */58 val &= ~CFG_RESET_N;59 60 val &= ~CFG_PIX_FMT_MASK;61 62 lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);63 64 udelay(1);65 66 val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE;67 68 lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);69 70 /* Wait about a vblank time */71 msleep(20);72}73 74static void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc)75{76 struct lsdc_device *ldev = lcrtc->ldev;77 u32 val;78 79 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);80 81 /*82 * This may happen in extremely rare cases, but a soft reset can83 * bring it back to normal. We add a warning here, hoping to catch84 * something if it happens.85 */86 if (val & CRTC_ANCHORED) {87 drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);88 return lsdc_crtc0_soft_reset(lcrtc);89 }90 91 lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val | CFG_OUTPUT_ENABLE);92}93 94static void lsdc_crtc0_disable(struct lsdc_crtc *lcrtc)95{96 struct lsdc_device *ldev = lcrtc->ldev;97 98 lsdc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, CFG_OUTPUT_ENABLE);99 100 udelay(9);101}102 103static void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc)104{105 struct lsdc_device *ldev = lcrtc->ldev;106 u32 val;107 108 /*109 * This may happen in extremely rare cases, but a soft reset can110 * bring it back to normal. We add a warning here, hoping to catch111 * something if it happens.112 */113 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);114 if (val & CRTC_ANCHORED) {115 drm_warn(&ldev->base, "%s stall\n", lcrtc->base.name);116 return lsdc_crtc1_soft_reset(lcrtc);117 }118 119 lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val | CFG_OUTPUT_ENABLE);120}121 122static void lsdc_crtc1_disable(struct lsdc_crtc *lcrtc)123{124 struct lsdc_device *ldev = lcrtc->ldev;125 126 lsdc_ureg32_clr(ldev, LSDC_CRTC1_CFG_REG, CFG_OUTPUT_ENABLE);127 128 udelay(9);129}130 131/* All Loongson display controllers have hardware scanout position recoders */132 133static void lsdc_crtc0_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos)134{135 struct lsdc_device *ldev = lcrtc->ldev;136 u32 val;137 138 val = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG);139 140 *hpos = val >> 16;141 *vpos = val & 0xffff;142}143 144static void lsdc_crtc1_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos)145{146 struct lsdc_device *ldev = lcrtc->ldev;147 u32 val;148 149 val = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG);150 151 *hpos = val >> 16;152 *vpos = val & 0xffff;153}154 155static void lsdc_crtc0_enable_vblank(struct lsdc_crtc *lcrtc)156{157 struct lsdc_device *ldev = lcrtc->ldev;158 159 lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN);160}161 162static void lsdc_crtc0_disable_vblank(struct lsdc_crtc *lcrtc)163{164 struct lsdc_device *ldev = lcrtc->ldev;165 166 lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN);167}168 169static void lsdc_crtc1_enable_vblank(struct lsdc_crtc *lcrtc)170{171 struct lsdc_device *ldev = lcrtc->ldev;172 173 lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN);174}175 176static void lsdc_crtc1_disable_vblank(struct lsdc_crtc *lcrtc)177{178 struct lsdc_device *ldev = lcrtc->ldev;179 180 lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN);181}182 183static void lsdc_crtc0_flip(struct lsdc_crtc *lcrtc)184{185 struct lsdc_device *ldev = lcrtc->ldev;186 187 lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_PAGE_FLIP);188}189 190static void lsdc_crtc1_flip(struct lsdc_crtc *lcrtc)191{192 struct lsdc_device *ldev = lcrtc->ldev;193 194 lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_PAGE_FLIP);195}196 197/*198 * CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic199 * This may be useful for custom cloning (TWIN) applications. Saving the200 * bandwidth compared with the clone (mirroring) display mode provided by201 * drm core.202 */203 204static void lsdc_crtc0_clone(struct lsdc_crtc *lcrtc)205{206 struct lsdc_device *ldev = lcrtc->ldev;207 208 lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_HW_CLONE);209}210 211static void lsdc_crtc1_clone(struct lsdc_crtc *lcrtc)212{213 struct lsdc_device *ldev = lcrtc->ldev;214 215 lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_HW_CLONE);216}217 218static void lsdc_crtc0_set_mode(struct lsdc_crtc *lcrtc,219 const struct drm_display_mode *mode)220{221 struct lsdc_device *ldev = lcrtc->ldev;222 223 lsdc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG,224 (mode->crtc_htotal << 16) | mode->crtc_hdisplay);225 226 lsdc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG,227 (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);228 229 lsdc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG,230 (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN);231 232 lsdc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG,233 (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN);234}235 236static void lsdc_crtc1_set_mode(struct lsdc_crtc *lcrtc,237 const struct drm_display_mode *mode)238{239 struct lsdc_device *ldev = lcrtc->ldev;240 241 lsdc_wreg32(ldev, LSDC_CRTC1_HDISPLAY_REG,242 (mode->crtc_htotal << 16) | mode->crtc_hdisplay);243 244 lsdc_wreg32(ldev, LSDC_CRTC1_VDISPLAY_REG,245 (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);246 247 lsdc_wreg32(ldev, LSDC_CRTC1_HSYNC_REG,248 (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN);249 250 lsdc_wreg32(ldev, LSDC_CRTC1_VSYNC_REG,251 (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN);252}253 254/*255 * This is required for S3 support.256 * After resuming from suspend, LSDC_CRTCx_CFG_REG (x = 0 or 1) is filled257 * with garbage value, which causes the CRTC hang there.258 *259 * This function provides minimal settings for the affected registers.260 * This overrides the firmware's settings on startup, making the CRTC work261 * on our own, similar to the functional of GPU POST (Power On Self Test).262 * Only touch CRTC hardware-related parts.263 */264 265static void lsdc_crtc0_reset(struct lsdc_crtc *lcrtc)266{267 struct lsdc_device *ldev = lcrtc->ldev;268 269 lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888);270}271 272static void lsdc_crtc1_reset(struct lsdc_crtc *lcrtc)273{274 struct lsdc_device *ldev = lcrtc->ldev;275 276 lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888);277}278 279static const struct lsdc_crtc_hw_ops ls7a1000_crtc_hw_ops[2] = {280 {281 .enable = lsdc_crtc0_enable,282 .disable = lsdc_crtc0_disable,283 .enable_vblank = lsdc_crtc0_enable_vblank,284 .disable_vblank = lsdc_crtc0_disable_vblank,285 .flip = lsdc_crtc0_flip,286 .clone = lsdc_crtc0_clone,287 .set_mode = lsdc_crtc0_set_mode,288 .get_scan_pos = lsdc_crtc0_scan_pos,289 .soft_reset = lsdc_crtc0_soft_reset,290 .reset = lsdc_crtc0_reset,291 },292 {293 .enable = lsdc_crtc1_enable,294 .disable = lsdc_crtc1_disable,295 .enable_vblank = lsdc_crtc1_enable_vblank,296 .disable_vblank = lsdc_crtc1_disable_vblank,297 .flip = lsdc_crtc1_flip,298 .clone = lsdc_crtc1_clone,299 .set_mode = lsdc_crtc1_set_mode,300 .get_scan_pos = lsdc_crtc1_scan_pos,301 .soft_reset = lsdc_crtc1_soft_reset,302 .reset = lsdc_crtc1_reset,303 },304};305 306/*307 * The 32-bit hardware vblank counter has been available since LS7A2000308 * and LS2K2000. The counter increases even though the CRTC is disabled,309 * it will be reset only if the CRTC is being soft reset.310 * Those registers are also readable for ls7a1000, but its value does not311 * change.312 */313 314static u32 lsdc_crtc0_get_vblank_count(struct lsdc_crtc *lcrtc)315{316 struct lsdc_device *ldev = lcrtc->ldev;317 318 return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG);319}320 321static u32 lsdc_crtc1_get_vblank_count(struct lsdc_crtc *lcrtc)322{323 struct lsdc_device *ldev = lcrtc->ldev;324 325 return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG);326}327 328/*329 * The DMA step bit fields are available since LS7A2000/LS2K2000, for330 * supporting odd resolutions. But a large DMA step save the bandwidth.331 * The larger, the better. Behavior of writing those bits on LS7A1000332 * or LS2K1000 is underfined.333 */334 335static void lsdc_crtc0_set_dma_step(struct lsdc_crtc *lcrtc,336 enum lsdc_dma_steps dma_step)337{338 struct lsdc_device *ldev = lcrtc->ldev;339 u32 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);340 341 val &= ~CFG_DMA_STEP_MASK;342 val |= dma_step << CFG_DMA_STEP_SHIFT;343 344 lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val);345}346 347static void lsdc_crtc1_set_dma_step(struct lsdc_crtc *lcrtc,348 enum lsdc_dma_steps dma_step)349{350 struct lsdc_device *ldev = lcrtc->ldev;351 u32 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);352 353 val &= ~CFG_DMA_STEP_MASK;354 val |= dma_step << CFG_DMA_STEP_SHIFT;355 356 lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val);357}358 359static const struct lsdc_crtc_hw_ops ls7a2000_crtc_hw_ops[2] = {360 {361 .enable = lsdc_crtc0_enable,362 .disable = lsdc_crtc0_disable,363 .enable_vblank = lsdc_crtc0_enable_vblank,364 .disable_vblank = lsdc_crtc0_disable_vblank,365 .flip = lsdc_crtc0_flip,366 .clone = lsdc_crtc0_clone,367 .set_mode = lsdc_crtc0_set_mode,368 .soft_reset = lsdc_crtc0_soft_reset,369 .get_scan_pos = lsdc_crtc0_scan_pos,370 .set_dma_step = lsdc_crtc0_set_dma_step,371 .get_vblank_counter = lsdc_crtc0_get_vblank_count,372 .reset = lsdc_crtc0_reset,373 },374 {375 .enable = lsdc_crtc1_enable,376 .disable = lsdc_crtc1_disable,377 .enable_vblank = lsdc_crtc1_enable_vblank,378 .disable_vblank = lsdc_crtc1_disable_vblank,379 .flip = lsdc_crtc1_flip,380 .clone = lsdc_crtc1_clone,381 .set_mode = lsdc_crtc1_set_mode,382 .get_scan_pos = lsdc_crtc1_scan_pos,383 .soft_reset = lsdc_crtc1_soft_reset,384 .set_dma_step = lsdc_crtc1_set_dma_step,385 .get_vblank_counter = lsdc_crtc1_get_vblank_count,386 .reset = lsdc_crtc1_reset,387 },388};389 390static void lsdc_crtc_reset(struct drm_crtc *crtc)391{392 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);393 const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;394 struct lsdc_crtc_state *priv_crtc_state;395 396 if (crtc->state)397 crtc->funcs->atomic_destroy_state(crtc, crtc->state);398 399 priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL);400 401 if (!priv_crtc_state)402 __drm_atomic_helper_crtc_reset(crtc, NULL);403 else404 __drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base);405 406 /* Reset the CRTC hardware, this is required for S3 support */407 ops->reset(lcrtc);408}409 410static void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc,411 struct drm_crtc_state *state)412{413 struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);414 415 __drm_atomic_helper_crtc_destroy_state(&priv_state->base);416 417 kfree(priv_state);418}419 420static struct drm_crtc_state *421lsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc)422{423 struct lsdc_crtc_state *new_priv_state;424 struct lsdc_crtc_state *old_priv_state;425 426 new_priv_state = kzalloc(sizeof(*new_priv_state), GFP_KERNEL);427 if (!new_priv_state)428 return NULL;429 430 __drm_atomic_helper_crtc_duplicate_state(crtc, &new_priv_state->base);431 432 old_priv_state = to_lsdc_crtc_state(crtc->state);433 434 memcpy(&new_priv_state->pparms, &old_priv_state->pparms,435 sizeof(new_priv_state->pparms));436 437 return &new_priv_state->base;438}439 440static u32 lsdc_crtc_get_vblank_counter(struct drm_crtc *crtc)441{442 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);443 444 /* 32-bit hardware vblank counter */445 return lcrtc->hw_ops->get_vblank_counter(lcrtc);446}447 448static int lsdc_crtc_enable_vblank(struct drm_crtc *crtc)449{450 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);451 452 if (!lcrtc->has_vblank)453 return -EINVAL;454 455 lcrtc->hw_ops->enable_vblank(lcrtc);456 457 return 0;458}459 460static void lsdc_crtc_disable_vblank(struct drm_crtc *crtc)461{462 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);463 464 if (!lcrtc->has_vblank)465 return;466 467 lcrtc->hw_ops->disable_vblank(lcrtc);468}469 470/*471 * CRTC related debugfs472 * Primary planes and cursor planes belong to the CRTC as well.473 * For the sake of convenience, plane-related registers are also add here.474 */475 476#define REG_DEF(reg) { \477 .name = __stringify_1(LSDC_##reg##_REG), \478 .offset = LSDC_##reg##_REG, \479}480 481static const struct lsdc_reg32 lsdc_crtc_regs_array[2][21] = {482 [0] = {483 REG_DEF(CRTC0_CFG),484 REG_DEF(CRTC0_FB_ORIGIN),485 REG_DEF(CRTC0_DVO_CONF),486 REG_DEF(CRTC0_HDISPLAY),487 REG_DEF(CRTC0_HSYNC),488 REG_DEF(CRTC0_VDISPLAY),489 REG_DEF(CRTC0_VSYNC),490 REG_DEF(CRTC0_GAMMA_INDEX),491 REG_DEF(CRTC0_GAMMA_DATA),492 REG_DEF(CRTC0_SYNC_DEVIATION),493 REG_DEF(CRTC0_VSYNC_COUNTER),494 REG_DEF(CRTC0_SCAN_POS),495 REG_DEF(CRTC0_STRIDE),496 REG_DEF(CRTC0_FB1_ADDR_HI),497 REG_DEF(CRTC0_FB1_ADDR_LO),498 REG_DEF(CRTC0_FB0_ADDR_HI),499 REG_DEF(CRTC0_FB0_ADDR_LO),500 REG_DEF(CURSOR0_CFG),501 REG_DEF(CURSOR0_POSITION),502 REG_DEF(CURSOR0_BG_COLOR),503 REG_DEF(CURSOR0_FG_COLOR),504 },505 [1] = {506 REG_DEF(CRTC1_CFG),507 REG_DEF(CRTC1_FB_ORIGIN),508 REG_DEF(CRTC1_DVO_CONF),509 REG_DEF(CRTC1_HDISPLAY),510 REG_DEF(CRTC1_HSYNC),511 REG_DEF(CRTC1_VDISPLAY),512 REG_DEF(CRTC1_VSYNC),513 REG_DEF(CRTC1_GAMMA_INDEX),514 REG_DEF(CRTC1_GAMMA_DATA),515 REG_DEF(CRTC1_SYNC_DEVIATION),516 REG_DEF(CRTC1_VSYNC_COUNTER),517 REG_DEF(CRTC1_SCAN_POS),518 REG_DEF(CRTC1_STRIDE),519 REG_DEF(CRTC1_FB1_ADDR_HI),520 REG_DEF(CRTC1_FB1_ADDR_LO),521 REG_DEF(CRTC1_FB0_ADDR_HI),522 REG_DEF(CRTC1_FB0_ADDR_LO),523 REG_DEF(CURSOR1_CFG),524 REG_DEF(CURSOR1_POSITION),525 REG_DEF(CURSOR1_BG_COLOR),526 REG_DEF(CURSOR1_FG_COLOR),527 },528};529 530static int lsdc_crtc_show_regs(struct seq_file *m, void *arg)531{532 struct drm_info_node *node = (struct drm_info_node *)m->private;533 struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;534 struct lsdc_device *ldev = lcrtc->ldev;535 unsigned int i;536 537 for (i = 0; i < lcrtc->nreg; i++) {538 const struct lsdc_reg32 *preg = &lcrtc->preg[i];539 u32 offset = preg->offset;540 541 seq_printf(m, "%s (0x%04x): 0x%08x\n",542 preg->name, offset, lsdc_rreg32(ldev, offset));543 }544 545 return 0;546}547 548static int lsdc_crtc_show_scan_position(struct seq_file *m, void *arg)549{550 struct drm_info_node *node = (struct drm_info_node *)m->private;551 struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;552 int x, y;553 554 lcrtc->hw_ops->get_scan_pos(lcrtc, &x, &y);555 seq_printf(m, "Scanout position: x: %08u, y: %08u\n", x, y);556 557 return 0;558}559 560static int lsdc_crtc_show_vblank_counter(struct seq_file *m, void *arg)561{562 struct drm_info_node *node = (struct drm_info_node *)m->private;563 struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;564 565 if (lcrtc->hw_ops->get_vblank_counter)566 seq_printf(m, "%s vblank counter: %08u\n\n", lcrtc->base.name,567 lcrtc->hw_ops->get_vblank_counter(lcrtc));568 569 return 0;570}571 572static int lsdc_pixpll_show_clock(struct seq_file *m, void *arg)573{574 struct drm_info_node *node = (struct drm_info_node *)m->private;575 struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data;576 struct lsdc_pixpll *pixpll = &lcrtc->pixpll;577 const struct lsdc_pixpll_funcs *funcs = pixpll->funcs;578 struct drm_crtc *crtc = &lcrtc->base;579 struct drm_display_mode *mode = &crtc->state->mode;580 struct drm_printer printer = drm_seq_file_printer(m);581 unsigned int out_khz;582 583 out_khz = funcs->get_rate(pixpll);584 585 seq_printf(m, "%s: %dx%d@%d\n", crtc->name,586 mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode));587 588 seq_printf(m, "Pixel clock required: %d kHz\n", mode->clock);589 seq_printf(m, "Actual frequency output: %u kHz\n", out_khz);590 seq_printf(m, "Diff: %d kHz\n", out_khz - mode->clock);591 592 funcs->print(pixpll, &printer);593 594 return 0;595}596 597static struct drm_info_list lsdc_crtc_debugfs_list[2][4] = {598 [0] = {599 { "regs", lsdc_crtc_show_regs, 0, NULL },600 { "pixclk", lsdc_pixpll_show_clock, 0, NULL },601 { "scanpos", lsdc_crtc_show_scan_position, 0, NULL },602 { "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL },603 },604 [1] = {605 { "regs", lsdc_crtc_show_regs, 0, NULL },606 { "pixclk", lsdc_pixpll_show_clock, 0, NULL },607 { "scanpos", lsdc_crtc_show_scan_position, 0, NULL },608 { "vblanks", lsdc_crtc_show_vblank_counter, 0, NULL },609 },610};611 612/* operate manually */613 614static int lsdc_crtc_man_op_show(struct seq_file *m, void *data)615{616 seq_puts(m, "soft_reset: soft reset this CRTC\n");617 seq_puts(m, "enable: enable this CRTC\n");618 seq_puts(m, "disable: disable this CRTC\n");619 seq_puts(m, "flip: trigger the page flip\n");620 seq_puts(m, "clone: clone the another crtc with hardware logic\n");621 622 return 0;623}624 625static int lsdc_crtc_man_op_open(struct inode *inode, struct file *file)626{627 struct drm_crtc *crtc = inode->i_private;628 629 return single_open(file, lsdc_crtc_man_op_show, crtc);630}631 632static ssize_t lsdc_crtc_man_op_write(struct file *file,633 const char __user *ubuf,634 size_t len,635 loff_t *offp)636{637 struct seq_file *m = file->private_data;638 struct lsdc_crtc *lcrtc = m->private;639 const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;640 char buf[16];641 642 if (len > sizeof(buf) - 1)643 return -EINVAL;644 645 if (copy_from_user(buf, ubuf, len))646 return -EFAULT;647 648 buf[len] = '\0';649 650 if (sysfs_streq(buf, "soft_reset"))651 ops->soft_reset(lcrtc);652 else if (sysfs_streq(buf, "enable"))653 ops->enable(lcrtc);654 else if (sysfs_streq(buf, "disable"))655 ops->disable(lcrtc);656 else if (sysfs_streq(buf, "flip"))657 ops->flip(lcrtc);658 else if (sysfs_streq(buf, "clone"))659 ops->clone(lcrtc);660 661 return len;662}663 664static const struct file_operations lsdc_crtc_man_op_fops = {665 .owner = THIS_MODULE,666 .open = lsdc_crtc_man_op_open,667 .read = seq_read,668 .llseek = seq_lseek,669 .release = single_release,670 .write = lsdc_crtc_man_op_write,671};672 673static int lsdc_crtc_late_register(struct drm_crtc *crtc)674{675 struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc);676 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);677 struct drm_minor *minor = crtc->dev->primary;678 unsigned int index = dispipe->index;679 unsigned int i;680 681 lcrtc->preg = lsdc_crtc_regs_array[index];682 lcrtc->nreg = ARRAY_SIZE(lsdc_crtc_regs_array[index]);683 lcrtc->p_info_list = lsdc_crtc_debugfs_list[index];684 lcrtc->n_info_list = ARRAY_SIZE(lsdc_crtc_debugfs_list[index]);685 686 for (i = 0; i < lcrtc->n_info_list; ++i)687 lcrtc->p_info_list[i].data = lcrtc;688 689 drm_debugfs_create_files(lcrtc->p_info_list, lcrtc->n_info_list,690 crtc->debugfs_entry, minor);691 692 /* Manual operations supported */693 debugfs_create_file("ops", 0644, crtc->debugfs_entry, lcrtc,694 &lsdc_crtc_man_op_fops);695 696 return 0;697}698 699static void lsdc_crtc_atomic_print_state(struct drm_printer *p,700 const struct drm_crtc_state *state)701{702 const struct lsdc_crtc_state *priv_state;703 const struct lsdc_pixpll_parms *pparms;704 705 priv_state = container_of_const(state, struct lsdc_crtc_state, base);706 pparms = &priv_state->pparms;707 708 drm_printf(p, "\tInput clock divider = %u\n", pparms->div_ref);709 drm_printf(p, "\tMedium clock multiplier = %u\n", pparms->loopc);710 drm_printf(p, "\tOutput clock divider = %u\n", pparms->div_out);711}712 713static const struct drm_crtc_funcs ls7a1000_crtc_funcs = {714 .reset = lsdc_crtc_reset,715 .destroy = drm_crtc_cleanup,716 .set_config = drm_atomic_helper_set_config,717 .page_flip = drm_atomic_helper_page_flip,718 .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state,719 .atomic_destroy_state = lsdc_crtc_atomic_destroy_state,720 .late_register = lsdc_crtc_late_register,721 .enable_vblank = lsdc_crtc_enable_vblank,722 .disable_vblank = lsdc_crtc_disable_vblank,723 .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,724 .atomic_print_state = lsdc_crtc_atomic_print_state,725};726 727static const struct drm_crtc_funcs ls7a2000_crtc_funcs = {728 .reset = lsdc_crtc_reset,729 .destroy = drm_crtc_cleanup,730 .set_config = drm_atomic_helper_set_config,731 .page_flip = drm_atomic_helper_page_flip,732 .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state,733 .atomic_destroy_state = lsdc_crtc_atomic_destroy_state,734 .late_register = lsdc_crtc_late_register,735 .get_vblank_counter = lsdc_crtc_get_vblank_counter,736 .enable_vblank = lsdc_crtc_enable_vblank,737 .disable_vblank = lsdc_crtc_disable_vblank,738 .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,739 .atomic_print_state = lsdc_crtc_atomic_print_state,740};741 742static enum drm_mode_status743lsdc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)744{745 struct drm_device *ddev = crtc->dev;746 struct lsdc_device *ldev = to_lsdc(ddev);747 const struct lsdc_desc *descp = ldev->descp;748 unsigned int pitch;749 750 if (mode->hdisplay > descp->max_width)751 return MODE_BAD_HVALUE;752 753 if (mode->vdisplay > descp->max_height)754 return MODE_BAD_VVALUE;755 756 if (mode->clock > descp->max_pixel_clk) {757 drm_dbg_kms(ddev, "mode %dx%d, pixel clock=%d is too high\n",758 mode->hdisplay, mode->vdisplay, mode->clock);759 return MODE_CLOCK_HIGH;760 }761 762 /* 4 for DRM_FORMAT_XRGB8888 */763 pitch = mode->hdisplay * 4;764 765 if (pitch % descp->pitch_align) {766 drm_dbg_kms(ddev, "align to %u bytes is required: %u\n",767 descp->pitch_align, pitch);768 return MODE_BAD_WIDTH;769 }770 771 return MODE_OK;772}773 774static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc,775 struct drm_crtc_state *state)776{777 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);778 struct lsdc_pixpll *pixpll = &lcrtc->pixpll;779 const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs;780 struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);781 unsigned int clock = state->mode.clock;782 int ret;783 784 ret = pfuncs->compute(pixpll, clock, &priv_state->pparms);785 if (ret) {786 drm_warn(crtc->dev, "Failed to find PLL params for %ukHz\n",787 clock);788 return -EINVAL;789 }790 791 return 0;792}793 794static int lsdc_crtc_helper_atomic_check(struct drm_crtc *crtc,795 struct drm_atomic_state *state)796{797 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);798 799 if (!crtc_state->enable)800 return 0;801 802 return lsdc_pixpll_atomic_check(crtc, crtc_state);803}804 805static void lsdc_crtc_mode_set_nofb(struct drm_crtc *crtc)806{807 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);808 const struct lsdc_crtc_hw_ops *crtc_hw_ops = lcrtc->hw_ops;809 struct lsdc_pixpll *pixpll = &lcrtc->pixpll;810 const struct lsdc_pixpll_funcs *pixpll_funcs = pixpll->funcs;811 struct drm_crtc_state *state = crtc->state;812 struct drm_display_mode *mode = &state->mode;813 struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state);814 815 pixpll_funcs->update(pixpll, &priv_state->pparms);816 817 if (crtc_hw_ops->set_dma_step) {818 unsigned int width_in_bytes = mode->hdisplay * 4;819 enum lsdc_dma_steps dma_step;820 821 /*822 * Using DMA step as large as possible, for improving823 * hardware DMA efficiency.824 */825 if (width_in_bytes % 256 == 0)826 dma_step = LSDC_DMA_STEP_256_BYTES;827 else if (width_in_bytes % 128 == 0)828 dma_step = LSDC_DMA_STEP_128_BYTES;829 else if (width_in_bytes % 64 == 0)830 dma_step = LSDC_DMA_STEP_64_BYTES;831 else /* width_in_bytes % 32 == 0 */832 dma_step = LSDC_DMA_STEP_32_BYTES;833 834 crtc_hw_ops->set_dma_step(lcrtc, dma_step);835 }836 837 crtc_hw_ops->set_mode(lcrtc, mode);838}839 840static void lsdc_crtc_send_vblank(struct drm_crtc *crtc)841{842 struct drm_device *ddev = crtc->dev;843 unsigned long flags;844 845 if (!crtc->state || !crtc->state->event)846 return;847 848 drm_dbg(ddev, "Send vblank manually\n");849 850 spin_lock_irqsave(&ddev->event_lock, flags);851 drm_crtc_send_vblank_event(crtc, crtc->state->event);852 crtc->state->event = NULL;853 spin_unlock_irqrestore(&ddev->event_lock, flags);854}855 856static void lsdc_crtc_atomic_enable(struct drm_crtc *crtc,857 struct drm_atomic_state *state)858{859 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);860 861 if (lcrtc->has_vblank)862 drm_crtc_vblank_on(crtc);863 864 lcrtc->hw_ops->enable(lcrtc);865}866 867static void lsdc_crtc_atomic_disable(struct drm_crtc *crtc,868 struct drm_atomic_state *state)869{870 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);871 872 if (lcrtc->has_vblank)873 drm_crtc_vblank_off(crtc);874 875 lcrtc->hw_ops->disable(lcrtc);876 877 /*878 * Make sure we issue a vblank event after disabling the CRTC if879 * someone was waiting it.880 */881 lsdc_crtc_send_vblank(crtc);882}883 884static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc,885 struct drm_atomic_state *state)886{887 spin_lock_irq(&crtc->dev->event_lock);888 if (crtc->state->event) {889 if (drm_crtc_vblank_get(crtc) == 0)890 drm_crtc_arm_vblank_event(crtc, crtc->state->event);891 else892 drm_crtc_send_vblank_event(crtc, crtc->state->event);893 crtc->state->event = NULL;894 }895 spin_unlock_irq(&crtc->dev->event_lock);896}897 898static bool lsdc_crtc_get_scanout_position(struct drm_crtc *crtc,899 bool in_vblank_irq,900 int *vpos,901 int *hpos,902 ktime_t *stime,903 ktime_t *etime,904 const struct drm_display_mode *mode)905{906 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);907 const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops;908 int vsw, vbp, vactive_start, vactive_end, vfp_end;909 int x, y;910 911 vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;912 vbp = mode->crtc_vtotal - mode->crtc_vsync_end;913 914 vactive_start = vsw + vbp + 1;915 vactive_end = vactive_start + mode->crtc_vdisplay;916 917 /* last scan line before VSYNC */918 vfp_end = mode->crtc_vtotal;919 920 if (stime)921 *stime = ktime_get();922 923 ops->get_scan_pos(lcrtc, &x, &y);924 925 if (y > vactive_end)926 y = y - vfp_end - vactive_start;927 else928 y -= vactive_start;929 930 *vpos = y;931 *hpos = 0;932 933 if (etime)934 *etime = ktime_get();935 936 return true;937}938 939static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = {940 .mode_valid = lsdc_crtc_mode_valid,941 .mode_set_nofb = lsdc_crtc_mode_set_nofb,942 .atomic_enable = lsdc_crtc_atomic_enable,943 .atomic_disable = lsdc_crtc_atomic_disable,944 .atomic_check = lsdc_crtc_helper_atomic_check,945 .atomic_flush = lsdc_crtc_atomic_flush,946 .get_scanout_position = lsdc_crtc_get_scanout_position,947};948 949int ls7a1000_crtc_init(struct drm_device *ddev,950 struct drm_crtc *crtc,951 struct drm_plane *primary,952 struct drm_plane *cursor,953 unsigned int index,954 bool has_vblank)955{956 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);957 int ret;958 959 ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index);960 if (ret) {961 drm_err(ddev, "pixel pll init failed: %d\n", ret);962 return ret;963 }964 965 lcrtc->ldev = to_lsdc(ddev);966 lcrtc->has_vblank = has_vblank;967 lcrtc->hw_ops = &ls7a1000_crtc_hw_ops[index];968 969 ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor,970 &ls7a1000_crtc_funcs,971 "LS-CRTC-%d", index);972 if (ret) {973 drm_err(ddev, "crtc init with planes failed: %d\n", ret);974 return ret;975 }976 977 drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);978 979 ret = drm_mode_crtc_set_gamma_size(crtc, 256);980 if (ret)981 return ret;982 983 drm_crtc_enable_color_mgmt(crtc, 0, false, 256);984 985 return 0;986}987 988int ls7a2000_crtc_init(struct drm_device *ddev,989 struct drm_crtc *crtc,990 struct drm_plane *primary,991 struct drm_plane *cursor,992 unsigned int index,993 bool has_vblank)994{995 struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc);996 int ret;997 998 ret = lsdc_pixpll_init(&lcrtc->pixpll, ddev, index);999 if (ret) {1000 drm_err(ddev, "crtc init with pll failed: %d\n", ret);1001 return ret;1002 }1003 1004 lcrtc->ldev = to_lsdc(ddev);1005 lcrtc->has_vblank = has_vblank;1006 lcrtc->hw_ops = &ls7a2000_crtc_hw_ops[index];1007 1008 ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor,1009 &ls7a2000_crtc_funcs,1010 "LS-CRTC-%u", index);1011 if (ret) {1012 drm_err(ddev, "crtc init with planes failed: %d\n", ret);1013 return ret;1014 }1015 1016 drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs);1017 1018 ret = drm_mode_crtc_set_gamma_size(crtc, 256);1019 if (ret)1020 return ret;1021 1022 drm_crtc_enable_color_mgmt(crtc, 0, false, 256);1023 1024 return 0;1025}1026