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 
lsdc_crtc0_soft_reset(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_soft_reset(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_enable(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_disable(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_enable(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_disable(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_scan_pos(struct lsdc_crtc * lcrtc,int * hpos,int * vpos)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 
lsdc_crtc1_scan_pos(struct lsdc_crtc * lcrtc,int * hpos,int * vpos)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 
lsdc_crtc0_enable_vblank(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_disable_vblank(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_enable_vblank(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_disable_vblank(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_flip(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_flip(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_clone(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_clone(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_set_mode(struct lsdc_crtc * lcrtc,const struct drm_display_mode * mode)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 
lsdc_crtc1_set_mode(struct lsdc_crtc * lcrtc,const struct drm_display_mode * mode)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 
lsdc_crtc0_reset(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_reset(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_get_vblank_count(struct lsdc_crtc * lcrtc)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 
lsdc_crtc1_get_vblank_count(struct lsdc_crtc * lcrtc)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 
lsdc_crtc0_set_dma_step(struct lsdc_crtc * lcrtc,enum lsdc_dma_steps dma_step)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 
lsdc_crtc1_set_dma_step(struct lsdc_crtc * lcrtc,enum lsdc_dma_steps dma_step)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 
lsdc_crtc_reset(struct drm_crtc * crtc)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 
lsdc_crtc_atomic_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)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 *
lsdc_crtc_atomic_duplicate_state(struct drm_crtc * crtc)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 
lsdc_crtc_get_vblank_counter(struct drm_crtc * crtc)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 
lsdc_crtc_enable_vblank(struct drm_crtc * crtc)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 
lsdc_crtc_disable_vblank(struct drm_crtc * crtc)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 
lsdc_crtc_show_regs(struct seq_file * m,void * arg)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 
lsdc_crtc_show_scan_position(struct seq_file * m,void * arg)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 
lsdc_crtc_show_vblank_counter(struct seq_file * m,void * arg)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 
lsdc_pixpll_show_clock(struct seq_file * m,void * arg)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 
lsdc_crtc_man_op_show(struct seq_file * m,void * data)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 
lsdc_crtc_man_op_open(struct inode * inode,struct file * file)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 
lsdc_crtc_man_op_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)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 
lsdc_crtc_late_register(struct drm_crtc * crtc)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 
lsdc_crtc_atomic_print_state(struct drm_printer * p,const struct drm_crtc_state * state)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
lsdc_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)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 
lsdc_pixpll_atomic_check(struct drm_crtc * crtc,struct drm_crtc_state * state)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 
lsdc_crtc_helper_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)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 
lsdc_crtc_mode_set_nofb(struct drm_crtc * crtc)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 
lsdc_crtc_send_vblank(struct drm_crtc * crtc)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 
lsdc_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)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 
lsdc_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)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 
lsdc_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)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 
lsdc_crtc_get_scanout_position(struct drm_crtc * crtc,bool in_vblank_irq,int * vpos,int * hpos,ktime_t * stime,ktime_t * etime,const struct drm_display_mode * mode)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 
ls7a1000_crtc_init(struct drm_device * ddev,struct drm_crtc * crtc,struct drm_plane * primary,struct drm_plane * cursor,unsigned int index,bool has_vblank)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 
ls7a2000_crtc_init(struct drm_device * ddev,struct drm_crtc * crtc,struct drm_plane * primary,struct drm_plane * cursor,unsigned int index,bool has_vblank)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