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