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