1*dbf21777SIcenowy Zheng // SPDX-License-Identifier: GPL-2.0-only 2*dbf21777SIcenowy Zheng /* 3*dbf21777SIcenowy Zheng * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me> 4*dbf21777SIcenowy Zheng */ 5*dbf21777SIcenowy Zheng 6*dbf21777SIcenowy Zheng #include <linux/clk.h> 7*dbf21777SIcenowy Zheng #include <linux/regmap.h> 8*dbf21777SIcenowy Zheng #include <linux/units.h> 9*dbf21777SIcenowy Zheng 10*dbf21777SIcenowy Zheng #include <drm/drm_atomic.h> 11*dbf21777SIcenowy Zheng #include <drm/drm_atomic_helper.h> 12*dbf21777SIcenowy Zheng #include <drm/drm_print.h> 13*dbf21777SIcenowy Zheng #include <drm/drm_managed.h> 14*dbf21777SIcenowy Zheng #include <drm/drm_vblank_helper.h> 15*dbf21777SIcenowy Zheng 16*dbf21777SIcenowy Zheng #include "vs_crtc_regs.h" 17*dbf21777SIcenowy Zheng #include "vs_crtc.h" 18*dbf21777SIcenowy Zheng #include "vs_dc.h" 19*dbf21777SIcenowy Zheng #include "vs_dc_top_regs.h" 20*dbf21777SIcenowy Zheng #include "vs_drm.h" 21*dbf21777SIcenowy Zheng #include "vs_plane.h" 22*dbf21777SIcenowy Zheng 23*dbf21777SIcenowy Zheng static void vs_crtc_atomic_disable(struct drm_crtc *crtc, 24*dbf21777SIcenowy Zheng struct drm_atomic_state *state) 25*dbf21777SIcenowy Zheng { 26*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 27*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 28*dbf21777SIcenowy Zheng unsigned int output = vcrtc->id; 29*dbf21777SIcenowy Zheng 30*dbf21777SIcenowy Zheng drm_crtc_vblank_off(crtc); 31*dbf21777SIcenowy Zheng 32*dbf21777SIcenowy Zheng clk_disable_unprepare(dc->pix_clk[output]); 33*dbf21777SIcenowy Zheng } 34*dbf21777SIcenowy Zheng 35*dbf21777SIcenowy Zheng static void vs_crtc_atomic_enable(struct drm_crtc *crtc, 36*dbf21777SIcenowy Zheng struct drm_atomic_state *state) 37*dbf21777SIcenowy Zheng { 38*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 39*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 40*dbf21777SIcenowy Zheng unsigned int output = vcrtc->id; 41*dbf21777SIcenowy Zheng 42*dbf21777SIcenowy Zheng drm_WARN_ON(&dc->drm_dev->base, 43*dbf21777SIcenowy Zheng clk_prepare_enable(dc->pix_clk[output])); 44*dbf21777SIcenowy Zheng 45*dbf21777SIcenowy Zheng drm_crtc_vblank_on(crtc); 46*dbf21777SIcenowy Zheng } 47*dbf21777SIcenowy Zheng 48*dbf21777SIcenowy Zheng static void vs_crtc_mode_set_nofb(struct drm_crtc *crtc) 49*dbf21777SIcenowy Zheng { 50*dbf21777SIcenowy Zheng struct drm_display_mode *mode = &crtc->state->adjusted_mode; 51*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 52*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 53*dbf21777SIcenowy Zheng unsigned int output = vcrtc->id; 54*dbf21777SIcenowy Zheng 55*dbf21777SIcenowy Zheng regmap_write(dc->regs, VSDC_DISP_HSIZE(output), 56*dbf21777SIcenowy Zheng VSDC_DISP_HSIZE_DISP(mode->hdisplay) | 57*dbf21777SIcenowy Zheng VSDC_DISP_HSIZE_TOTAL(mode->htotal)); 58*dbf21777SIcenowy Zheng regmap_write(dc->regs, VSDC_DISP_VSIZE(output), 59*dbf21777SIcenowy Zheng VSDC_DISP_VSIZE_DISP(mode->vdisplay) | 60*dbf21777SIcenowy Zheng VSDC_DISP_VSIZE_TOTAL(mode->vtotal)); 61*dbf21777SIcenowy Zheng regmap_write(dc->regs, VSDC_DISP_HSYNC(output), 62*dbf21777SIcenowy Zheng VSDC_DISP_HSYNC_START(mode->hsync_start) | 63*dbf21777SIcenowy Zheng VSDC_DISP_HSYNC_END(mode->hsync_end) | 64*dbf21777SIcenowy Zheng VSDC_DISP_HSYNC_EN); 65*dbf21777SIcenowy Zheng if (!(mode->flags & DRM_MODE_FLAG_PHSYNC)) 66*dbf21777SIcenowy Zheng regmap_set_bits(dc->regs, VSDC_DISP_HSYNC(output), 67*dbf21777SIcenowy Zheng VSDC_DISP_HSYNC_POL); 68*dbf21777SIcenowy Zheng regmap_write(dc->regs, VSDC_DISP_VSYNC(output), 69*dbf21777SIcenowy Zheng VSDC_DISP_VSYNC_START(mode->vsync_start) | 70*dbf21777SIcenowy Zheng VSDC_DISP_VSYNC_END(mode->vsync_end) | 71*dbf21777SIcenowy Zheng VSDC_DISP_VSYNC_EN); 72*dbf21777SIcenowy Zheng if (!(mode->flags & DRM_MODE_FLAG_PVSYNC)) 73*dbf21777SIcenowy Zheng regmap_set_bits(dc->regs, VSDC_DISP_VSYNC(output), 74*dbf21777SIcenowy Zheng VSDC_DISP_VSYNC_POL); 75*dbf21777SIcenowy Zheng 76*dbf21777SIcenowy Zheng WARN_ON(clk_set_rate(dc->pix_clk[output], mode->crtc_clock * 1000)); 77*dbf21777SIcenowy Zheng } 78*dbf21777SIcenowy Zheng 79*dbf21777SIcenowy Zheng static enum drm_mode_status 80*dbf21777SIcenowy Zheng vs_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) 81*dbf21777SIcenowy Zheng { 82*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 83*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 84*dbf21777SIcenowy Zheng unsigned int output = vcrtc->id; 85*dbf21777SIcenowy Zheng long rate; 86*dbf21777SIcenowy Zheng 87*dbf21777SIcenowy Zheng if (mode->htotal > VSDC_DISP_TIMING_VALUE_MAX) 88*dbf21777SIcenowy Zheng return MODE_BAD_HVALUE; 89*dbf21777SIcenowy Zheng if (mode->vtotal > VSDC_DISP_TIMING_VALUE_MAX) 90*dbf21777SIcenowy Zheng return MODE_BAD_VVALUE; 91*dbf21777SIcenowy Zheng 92*dbf21777SIcenowy Zheng rate = clk_round_rate(dc->pix_clk[output], mode->clock * HZ_PER_KHZ); 93*dbf21777SIcenowy Zheng if (rate <= 0) 94*dbf21777SIcenowy Zheng return MODE_CLOCK_RANGE; 95*dbf21777SIcenowy Zheng 96*dbf21777SIcenowy Zheng return MODE_OK; 97*dbf21777SIcenowy Zheng } 98*dbf21777SIcenowy Zheng 99*dbf21777SIcenowy Zheng static bool vs_crtc_mode_fixup(struct drm_crtc *crtc, 100*dbf21777SIcenowy Zheng const struct drm_display_mode *m, 101*dbf21777SIcenowy Zheng struct drm_display_mode *adjusted_mode) 102*dbf21777SIcenowy Zheng { 103*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 104*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 105*dbf21777SIcenowy Zheng unsigned int output = vcrtc->id; 106*dbf21777SIcenowy Zheng long clk_rate; 107*dbf21777SIcenowy Zheng 108*dbf21777SIcenowy Zheng drm_mode_set_crtcinfo(adjusted_mode, 0); 109*dbf21777SIcenowy Zheng 110*dbf21777SIcenowy Zheng /* Feedback the pixel clock to crtc_clock */ 111*dbf21777SIcenowy Zheng clk_rate = adjusted_mode->crtc_clock * HZ_PER_KHZ; 112*dbf21777SIcenowy Zheng clk_rate = clk_round_rate(dc->pix_clk[output], clk_rate); 113*dbf21777SIcenowy Zheng if (clk_rate <= 0) 114*dbf21777SIcenowy Zheng return false; 115*dbf21777SIcenowy Zheng 116*dbf21777SIcenowy Zheng adjusted_mode->crtc_clock = clk_rate / HZ_PER_KHZ; 117*dbf21777SIcenowy Zheng 118*dbf21777SIcenowy Zheng return true; 119*dbf21777SIcenowy Zheng } 120*dbf21777SIcenowy Zheng 121*dbf21777SIcenowy Zheng static const struct drm_crtc_helper_funcs vs_crtc_helper_funcs = { 122*dbf21777SIcenowy Zheng .atomic_flush = drm_crtc_vblank_atomic_flush, 123*dbf21777SIcenowy Zheng .atomic_enable = vs_crtc_atomic_enable, 124*dbf21777SIcenowy Zheng .atomic_disable = vs_crtc_atomic_disable, 125*dbf21777SIcenowy Zheng .mode_set_nofb = vs_crtc_mode_set_nofb, 126*dbf21777SIcenowy Zheng .mode_valid = vs_crtc_mode_valid, 127*dbf21777SIcenowy Zheng .mode_fixup = vs_crtc_mode_fixup, 128*dbf21777SIcenowy Zheng }; 129*dbf21777SIcenowy Zheng 130*dbf21777SIcenowy Zheng static int vs_crtc_enable_vblank(struct drm_crtc *crtc) 131*dbf21777SIcenowy Zheng { 132*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 133*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 134*dbf21777SIcenowy Zheng 135*dbf21777SIcenowy Zheng regmap_set_bits(dc->regs, VSDC_TOP_IRQ_EN, VSDC_TOP_IRQ_VSYNC(vcrtc->id)); 136*dbf21777SIcenowy Zheng 137*dbf21777SIcenowy Zheng return 0; 138*dbf21777SIcenowy Zheng } 139*dbf21777SIcenowy Zheng 140*dbf21777SIcenowy Zheng static void vs_crtc_disable_vblank(struct drm_crtc *crtc) 141*dbf21777SIcenowy Zheng { 142*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); 143*dbf21777SIcenowy Zheng struct vs_dc *dc = vcrtc->dc; 144*dbf21777SIcenowy Zheng 145*dbf21777SIcenowy Zheng regmap_clear_bits(dc->regs, VSDC_TOP_IRQ_EN, VSDC_TOP_IRQ_VSYNC(vcrtc->id)); 146*dbf21777SIcenowy Zheng } 147*dbf21777SIcenowy Zheng 148*dbf21777SIcenowy Zheng static const struct drm_crtc_funcs vs_crtc_funcs = { 149*dbf21777SIcenowy Zheng .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 150*dbf21777SIcenowy Zheng .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 151*dbf21777SIcenowy Zheng .page_flip = drm_atomic_helper_page_flip, 152*dbf21777SIcenowy Zheng .reset = drm_atomic_helper_crtc_reset, 153*dbf21777SIcenowy Zheng .set_config = drm_atomic_helper_set_config, 154*dbf21777SIcenowy Zheng .enable_vblank = vs_crtc_enable_vblank, 155*dbf21777SIcenowy Zheng .disable_vblank = vs_crtc_disable_vblank, 156*dbf21777SIcenowy Zheng }; 157*dbf21777SIcenowy Zheng 158*dbf21777SIcenowy Zheng struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc, 159*dbf21777SIcenowy Zheng unsigned int output) 160*dbf21777SIcenowy Zheng { 161*dbf21777SIcenowy Zheng struct vs_crtc *vcrtc; 162*dbf21777SIcenowy Zheng struct drm_plane *primary; 163*dbf21777SIcenowy Zheng int ret; 164*dbf21777SIcenowy Zheng 165*dbf21777SIcenowy Zheng vcrtc = drmm_kzalloc(drm_dev, sizeof(*vcrtc), GFP_KERNEL); 166*dbf21777SIcenowy Zheng if (!vcrtc) 167*dbf21777SIcenowy Zheng return ERR_PTR(-ENOMEM); 168*dbf21777SIcenowy Zheng vcrtc->dc = dc; 169*dbf21777SIcenowy Zheng vcrtc->id = output; 170*dbf21777SIcenowy Zheng 171*dbf21777SIcenowy Zheng /* Create our primary plane */ 172*dbf21777SIcenowy Zheng primary = vs_primary_plane_init(drm_dev, dc); 173*dbf21777SIcenowy Zheng if (IS_ERR(primary)) { 174*dbf21777SIcenowy Zheng drm_err(drm_dev, "Couldn't create the primary plane\n"); 175*dbf21777SIcenowy Zheng return ERR_PTR(PTR_ERR(primary)); 176*dbf21777SIcenowy Zheng } 177*dbf21777SIcenowy Zheng 178*dbf21777SIcenowy Zheng ret = drmm_crtc_init_with_planes(drm_dev, &vcrtc->base, 179*dbf21777SIcenowy Zheng primary, 180*dbf21777SIcenowy Zheng NULL, 181*dbf21777SIcenowy Zheng &vs_crtc_funcs, 182*dbf21777SIcenowy Zheng NULL); 183*dbf21777SIcenowy Zheng if (ret) { 184*dbf21777SIcenowy Zheng drm_err(drm_dev, "Couldn't initialize CRTC\n"); 185*dbf21777SIcenowy Zheng return ERR_PTR(ret); 186*dbf21777SIcenowy Zheng } 187*dbf21777SIcenowy Zheng 188*dbf21777SIcenowy Zheng drm_crtc_helper_add(&vcrtc->base, &vs_crtc_helper_funcs); 189*dbf21777SIcenowy Zheng 190*dbf21777SIcenowy Zheng return vcrtc; 191*dbf21777SIcenowy Zheng } 192