xref: /linux/drivers/gpu/drm/verisilicon/vs_crtc.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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