1 /*
2  * Copyright (c) 2018 Loongson Technology Co., Ltd.
3  * Authors:
4  *	Chen Zhu <zhuchen@loongson.cn>
5  *	Yaling Fang <fangyaling@loongson.cn>
6  *	Dandan Zhang <zhangdandan@loongson.cn>
7  *	Huacai Chen <chenhc@lemote.com>
8  *	Jiaxun Yang <jiaxun.yang@flygoat.com>
9  * This program is free software; you can redistribute  it and/or modify it
10  * under  the terms of  the GNU General  Public License as published by the
11  * Free Software Foundation;  either version 2 of the  License, or (at your
12  * option) any later version.
13  */
14 
15 #include <linux/delay.h>
16 #include <drm/drm_atomic_helper.h>
17 #include <drm/drm_plane.h>
18 #include <drm/drm_crtc_helper.h>
19 #include <drm/drm_plane_helper.h>
20 #include <drm/drm_fb_cma_helper.h>
21 #include <drm/drm_fourcc.h>
22 #include <drm/drm_gem_cma_helper.h>
23 #include "loongson_drv.h"
24 
25 /**
26  * This file contains setup code for the CRTC
27  */
28 
29 DEFINE_SPINLOCK(loongson_crtc_lock);
30 
loongson_crtc_enable_vblank(struct drm_crtc *crtc)31 static int loongson_crtc_enable_vblank(struct drm_crtc *crtc)
32 {
33 	struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
34 	struct loongson_drm_device *ldev = lcrtc->ldev;
35 
36 	if(lcrtc->crtc_id == 0) {
37 		ldev->int_reg |= (BIT(INT_DVO0_FB_END) << 16);
38 	} else {
39 		ldev->int_reg |= (BIT(INT_DVO1_FB_END) << 16);
40 	}
41 
42 	spin_lock(&loongson_reglock);
43 	writel(ldev->int_reg, ldev->mmio + FB_INT_REG);
44 	spin_unlock(&loongson_reglock);
45 
46 	return 0;
47 }
48 
loongson_crtc_disable_vblank(struct drm_crtc *crtc)49 static void loongson_crtc_disable_vblank(struct drm_crtc *crtc)
50 {
51 	struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
52 	struct loongson_drm_device *ldev = lcrtc->ldev;
53 
54 
55 	if(lcrtc->crtc_id == 0) {
56 		ldev->int_reg &= (~BIT(INT_DVO0_FB_END) << 16);
57 	} else {
58 		ldev->int_reg &= (~BIT(INT_DVO1_FB_END) << 16);
59 	}
60 
61 	spin_lock(&loongson_reglock);
62 	writel(ldev->int_reg, ldev->mmio + FB_INT_REG);
63 	spin_unlock(&loongson_reglock);
64 }
65 
66 #define PLL_REF_CLK_MHZ    100
67 #define PCLK_PRECISION_INDICATOR 10000
68 
69 /**
70  * cal_freq
71  *
72  * @pixclock: unsigned int
73  * @pll_config: point to the pix_pll structure
74  *
75  * Calculate frequency
76  */
cal_freq(unsigned int pixclock, struct pix_pll *pll_config)77 static unsigned int cal_freq(unsigned int pixclock, struct pix_pll *pll_config)
78 {
79 	int i, j, loopc_offset;
80 	unsigned int refc_set[] = {4, 5, 3};
81 	unsigned int prec_set[] = {1, 5, 10, 50, 100};   /*in 1/PCLK_PRECISION_INDICATOR*/
82 	unsigned int pstdiv, loopc, refc;
83 	unsigned int precision_req, precision;
84 	unsigned int loopc_min, loopc_max, loopc_mid;
85 	unsigned long long real_dvo, req_dvo;
86 
87 	/*try precision from high to low*/
88 	for (j = 0; j < sizeof(prec_set)/sizeof(int); j++){
89 		precision_req = prec_set[j];
90 
91 		/*try each refc*/
92 		for (i = 0; i < sizeof(refc_set)/sizeof(int); i++) {
93 			refc = refc_set[i];
94 			loopc_min = (1200 / PLL_REF_CLK_MHZ) * refc;  /*1200 / (PLL_REF_CLK_MHZ / refc)*/
95 			loopc_max = (3200 / PLL_REF_CLK_MHZ) * refc;  /*3200 / (PLL_REF_CLK_MHZ / refc)*/
96 			loopc_mid = (2200 / PLL_REF_CLK_MHZ) * refc;  /*(loopc_min + loopc_max) / 2;*/
97 			loopc_offset = -1;
98 
99 			/*try each loopc*/
100 			for (loopc = loopc_mid; (loopc <= loopc_max) && (loopc >= loopc_min); loopc += loopc_offset) {
101 				if(loopc_offset < 0)
102 					loopc_offset = -loopc_offset;
103 				else
104 					loopc_offset = -(loopc_offset+1);
105 
106 				pstdiv = loopc * PLL_REF_CLK_MHZ * 1000 / refc / pixclock;
107 				if((pstdiv > 127) || (pstdiv < 1))
108 					continue;
109 
110 				/*real_freq is float type which is not available, but read_freq * pstdiv is available.*/
111 				req_dvo  = (pixclock * pstdiv);
112 				real_dvo = (loopc * PLL_REF_CLK_MHZ * 1000 / refc);
113 				precision = abs(real_dvo * PCLK_PRECISION_INDICATOR / req_dvo - PCLK_PRECISION_INDICATOR);
114 
115 				if(precision < precision_req){
116 					pll_config->l2_div = pstdiv;
117 					pll_config->l1_loopc = loopc;
118 					pll_config->l1_frefc = refc;
119 					if(j > 1)
120 						printk("Warning: PIX clock precision degraded to %d / %d\n", precision_req, PCLK_PRECISION_INDICATOR);
121 					return 1;
122 				}
123 			}
124 		}
125 	}
126 	return 0;
127 }
128 
129 /**
130  * config_pll
131  *
132  * @pll_base: represent a long type
133  * @pll_cfg: point to the pix_pll srtucture
134  *
135  * Config pll apply to ls7a
136  */
config_pll(void *pll_base, struct pix_pll *pll_cfg)137 static void config_pll(void *pll_base, struct pix_pll *pll_cfg)
138 {
139 	unsigned long val;
140 
141 	/* clear sel_pll_out0 */
142 	val = readl(pll_base + 0x4);
143 	val &= ~(1UL << 8);
144 	writel(val, pll_base + 0x4);
145 	/* set pll_pd */
146 	val = readl(pll_base + 0x4);
147 	val |= (1UL << 13);
148 	writel(val, pll_base + 0x4);
149 	/* clear set_pll_param */
150 	val = readl(pll_base + 0x4);
151 	val &= ~(1UL << 11);
152 	writel(val, pll_base + 0x4);
153 	/* clear old value & config new value */
154 	val = readl(pll_base + 0x4);
155 	val &= ~(0x7fUL << 0);
156 	val |= (pll_cfg->l1_frefc << 0); /* refc */
157 	writel(val, pll_base + 0x4);
158 	val = readl(pll_base + 0x0);
159 	val &= ~(0x7fUL << 0);
160 	val |= (pll_cfg->l2_div << 0);   /* div */
161 	val &= ~(0x1ffUL << 21);
162 	val |= (pll_cfg->l1_loopc << 21);/* loopc */
163 	writel(val, pll_base + 0x0);
164 	/* set set_pll_param */
165 	val = readl(pll_base + 0x4);
166 	val |= (1UL << 11);
167 	writel(val, pll_base + 0x4);
168 	/* clear pll_pd */
169 	val = readl(pll_base + 0x4);
170 	val &= ~(1UL << 13);
171 	writel(val, pll_base + 0x4);
172 	/* wait pll lock */
173 	while(!(readl(pll_base + 0x4) & 0x80))
174 		cpu_relax();
175 	/* set sel_pll_out0 */
176 	val = readl(pll_base + 0x4);
177 	val |= (1UL << 8);
178 	writel(val, pll_base + 0x4);
179 }
180 
loongson_config_pll(int id, unsigned int pix_freq)181 static void loongson_config_pll(int id, unsigned int pix_freq)
182 {
183 	unsigned int out;
184 	struct pix_pll pll_cfg;
185 
186 	out = cal_freq(pix_freq, &pll_cfg);
187 	if (id == 0)
188 		config_pll(LS7A_PIX0_PLL, &pll_cfg);
189 	else
190 		config_pll(LS7A_PIX1_PLL, &pll_cfg);
191 }
192 
193 /**
194  * These provide the minimum set of functions required to handle a CRTC
195  * Each driver is responsible for filling out this structure at startup time
196  *
197  * The drm_crtc_funcs structure is the central CRTC management structure
198  * in the DRM. Each CRTC controls one or more connectors
199  */
200 static const struct drm_crtc_funcs loongson_swcursor_crtc_funcs = {
201 	.destroy = drm_crtc_cleanup,
202 	.set_config = drm_atomic_helper_set_config,
203 	.page_flip = drm_atomic_helper_page_flip,
204 	.reset = drm_atomic_helper_crtc_reset,
205 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
206 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
207 	.enable_vblank = loongson_crtc_enable_vblank,
208 	.disable_vblank = loongson_crtc_disable_vblank,
209 };
210 
211 static const struct drm_crtc_funcs loongson_hwcursor_crtc_funcs = {
212 	.cursor_set2 = loongson_crtc_cursor_set2,
213 	.cursor_move = loongson_crtc_cursor_move,
214 	.destroy = drm_crtc_cleanup,
215 	.set_config = drm_atomic_helper_set_config,
216 	.page_flip = drm_atomic_helper_page_flip,
217 	.reset = drm_atomic_helper_crtc_reset,
218 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
219 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
220 	.enable_vblank = loongson_crtc_enable_vblank,
221 	.disable_vblank = loongson_crtc_disable_vblank,
222 };
223 
224 static const uint32_t loongson_formats[] = {
225 	DRM_FORMAT_RGB565,
226 	DRM_FORMAT_RGB888,
227 	DRM_FORMAT_XRGB8888,
228 	DRM_FORMAT_ARGB8888,
229 };
230 
231 static const uint64_t loongson_format_modifiers[] = {
232 	DRM_FORMAT_MOD_LINEAR,
233 	DRM_FORMAT_MOD_INVALID
234 };
235 
loongson_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)236 static enum drm_mode_status loongson_crtc_mode_valid(struct drm_crtc *crtc,
237 						    const struct drm_display_mode *mode)
238 {
239 	int id = crtc->index;
240 	struct drm_device *dev = crtc->dev;
241 	struct loongson_drm_device *ldev = (struct loongson_drm_device*)dev->dev_private;
242 
243 	if (mode->hdisplay > get_crtc_max_width(ldev, id))
244 		return MODE_BAD;
245 	if (mode->vdisplay > get_crtc_max_height(ldev, id))
246 		return MODE_BAD;
247 	if (ldev->num_crtc == 1) {
248 		if (mode->hdisplay % 16)
249 			return MODE_BAD;
250 	} else {
251 		if (mode->hdisplay % 64)
252 			return MODE_BAD;
253 	}
254 
255 	return MODE_OK;
256 }
257 
crtc_read(struct loongson_crtc *lcrtc, u32 offset)258 u32 crtc_read(struct loongson_crtc *lcrtc, u32 offset)
259 {
260 	struct loongson_drm_device *ldev = lcrtc->ldev;
261 	return readl(ldev->mmio + offset + (lcrtc->crtc_id * CRTC_REG_OFFSET));
262 }
263 
crtc_write(struct loongson_crtc *lcrtc, u32 offset, u32 val)264 void crtc_write(struct loongson_crtc *lcrtc, u32 offset, u32 val)
265 {
266 	struct loongson_drm_device *ldev = lcrtc->ldev;
267 	writel(val, ldev->mmio + offset + (lcrtc->crtc_id * CRTC_REG_OFFSET));
268 }
269 
loongson_crtc_mode_set_nofb(struct drm_crtc *crtc)270 static void loongson_crtc_mode_set_nofb(struct drm_crtc *crtc)
271 {
272 	unsigned int hr, hss, hse, hfl;
273 	unsigned int vr, vss, vse, vfl;
274 	unsigned int pix_freq;
275 	unsigned long flags;
276 	struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
277 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
278 
279 	hr	= mode->hdisplay;
280 	hss	= mode->hsync_start;
281 	hse	= mode->hsync_end;
282 	hfl	= mode->htotal;
283 
284 	vr	= mode->vdisplay;
285 	vss	= mode->vsync_start;
286 	vse	= mode->vsync_end;
287 	vfl	= mode->vtotal;
288 
289 	pix_freq = mode->clock;
290 
291 	DRM_DEBUG("crtc_id = %d, hr = %d, hss = %d, hse = %d, hfl = %d, vr = %d, vss = %d, vse = %d, vfl = %d, pix_freq = %d,\n",
292 			lcrtc->crtc_id, hr, hss, hse, hfl, vr, vss, vse, vfl, pix_freq);
293 
294 	spin_lock_irqsave(&loongson_reglock, flags);
295 	crtc_write(lcrtc, FB_DITCFG_DVO_REG, 0);
296 	crtc_write(lcrtc, FB_DITTAB_LO_DVO_REG, 0);
297 	crtc_write(lcrtc, FB_DITTAB_HI_DVO_REG, 0);
298 	crtc_write(lcrtc, FB_PANCFG_DVO_REG, 0x80001311);
299 	crtc_write(lcrtc, FB_PANTIM_DVO_REG, 0);
300 
301 	crtc_write(lcrtc, FB_HDISPLAY_DVO_REG, (mode->crtc_htotal << 16) | mode->crtc_hdisplay);
302 	crtc_write(lcrtc, FB_HSYNC_DVO_REG, 0x40000000 | (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start);
303 
304 	crtc_write(lcrtc, FB_VDISPLAY_DVO_REG, (mode->crtc_vtotal << 16) | mode->crtc_vdisplay);
305 	crtc_write(lcrtc, FB_VSYNC_DVO_REG, 0x40000000 | (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start);
306 
307 	crtc_write(lcrtc, FB_STRI_DVO_REG, (crtc->primary->state->fb->pitches[0] + 255) & ~255);
308 
309 	DRM_DEBUG("Stride: %x\n",(crtc->primary->state->fb->pitches[0] + 255) & ~255);
310 
311 	switch (crtc->primary->state->fb->format->format) {
312 	case DRM_FORMAT_RGB565:
313 		lcrtc->cfg_reg |= 0x3;
314 		crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg);
315 		break;
316 	case DRM_FORMAT_RGB888:
317 	default:
318 		lcrtc->cfg_reg |= 0x4;
319 		crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg);
320 		break;
321 	}
322 	spin_unlock_irqrestore(&loongson_reglock, flags);
323 
324 	loongson_config_pll(lcrtc->crtc_id, mode->clock);
325 }
326 
loongson_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)327 static void loongson_crtc_atomic_enable(struct drm_crtc *crtc,
328 				       struct drm_crtc_state *old_state)
329 {
330 	unsigned long flags;
331 	struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
332 
333 	if (lcrtc->cfg_reg & CFG_ENABLE)
334 		goto vblank_on;
335 
336 	lcrtc->cfg_reg |= CFG_ENABLE;
337 	spin_lock_irqsave(&loongson_reglock, flags);
338 	crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg);
339 	spin_unlock_irqrestore(&loongson_reglock, flags);
340 
341 vblank_on:
342 	drm_crtc_vblank_on(crtc);
343 }
344 
loongson_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)345 static void loongson_crtc_atomic_disable(struct drm_crtc *crtc,
346 					struct drm_crtc_state *old_state)
347 {
348 	unsigned long flags;
349 	struct loongson_crtc *lcrtc = to_loongson_crtc(crtc);
350 
351 	lcrtc->cfg_reg &= ~CFG_ENABLE;
352 	spin_lock_irqsave(&loongson_reglock, flags);
353 	crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg);
354 	spin_unlock_irqrestore(&loongson_reglock, flags);
355 
356 	spin_lock_irq(&crtc->dev->event_lock);
357 	if (crtc->state->event) {
358 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
359 		crtc->state->event = NULL;
360 	}
361 	spin_unlock_irq(&crtc->dev->event_lock);
362 
363 	drm_crtc_vblank_off(crtc);
364 }
365 
loongson_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state)366 static void loongson_crtc_atomic_flush(struct drm_crtc *crtc,
367 				  struct drm_crtc_state *old_crtc_state)
368 {
369 	struct drm_pending_vblank_event *event = crtc->state->event;
370 
371 	if (event) {
372 		crtc->state->event = NULL;
373 
374 		spin_lock_irq(&crtc->dev->event_lock);
375 		if (drm_crtc_vblank_get(crtc) == 0)
376 			drm_crtc_arm_vblank_event(crtc, event);
377 		else
378 			drm_crtc_send_vblank_event(crtc, event);
379 		spin_unlock_irq(&crtc->dev->event_lock);
380 	}
381 }
382 
loongson_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state)383 static void loongson_plane_atomic_update(struct drm_plane *plane,
384 					struct drm_plane_state *old_state)
385 {
386 	int id, clonemode;
387 	unsigned int pitch;
388 	unsigned long flags;
389 	struct loongson_crtc *lcrtc;
390 	struct loongson_drm_device *ldev;
391 	struct drm_plane_state *state = plane->state;
392 
393 	if (!state->crtc || !state->fb)
394 		return;
395 
396 	pitch = state->fb->pitches[0];
397 	lcrtc = to_loongson_crtc(state->crtc);
398 	ldev = lcrtc->ldev;
399 	id = lcrtc->crtc_id;
400 	clonemode = clone_mode(ldev);
401 
402 	/* CRTC1 cloned from CRTC0 in clone mode */
403 	if (clonemode)
404 		ldev->lcrtc[1].cfg_reg |= CFG_PANELSWITCH;
405 	else
406 		ldev->lcrtc[1].cfg_reg &= ~CFG_PANELSWITCH;
407 
408 	spin_lock_irqsave(&loongson_reglock, flags);
409 	crtc_write(lcrtc, FB_STRI_DVO_REG, (pitch + 255) & ~255);
410 	if (crtc_read(lcrtc, FB_CFG_DVO_REG) & CFG_FBNUM)
411 		crtc_write(lcrtc, FB_ADDR0_DVO_REG, drm_fb_cma_get_gem_addr(state->fb, state, 0));
412 	else
413 		crtc_write(lcrtc, FB_ADDR1_DVO_REG, drm_fb_cma_get_gem_addr(state->fb, state, 0));
414 
415 	lcrtc->cfg_reg |= CFG_ENABLE;
416 	crtc_write(lcrtc, FB_CFG_DVO_REG, lcrtc->cfg_reg | CFG_FBSWITCH);
417 	if (clonemode) {
418 		if (id)
419 			crtc_write(&ldev->lcrtc[0], FB_CFG_DVO_REG, ldev->lcrtc[0].cfg_reg | CFG_ENABLE);
420 		else
421 			crtc_write(&ldev->lcrtc[1], FB_CFG_DVO_REG, ldev->lcrtc[1].cfg_reg | CFG_ENABLE);
422 	}
423 	spin_unlock_irqrestore(&loongson_reglock, flags);
424 
425 	udelay(2500);
426 }
427 
428 /**
429  * These provide the minimum set of functions required to handle a CRTC
430  *
431  * The drm_crtc_helper_funcs is a helper operations for CRTC
432  */
433 static const struct drm_crtc_helper_funcs loongson_crtc_helper_funcs = {
434 	.mode_valid = loongson_crtc_mode_valid,
435 	.mode_set_nofb	= loongson_crtc_mode_set_nofb,
436 	.atomic_enable	= loongson_crtc_atomic_enable,
437 	.atomic_disable	= loongson_crtc_atomic_disable,
438 	.atomic_flush	= loongson_crtc_atomic_flush,
439 };
440 
loongson_plane_destroy(struct drm_plane *plane)441 static void loongson_plane_destroy(struct drm_plane *plane)
442 {
443 	drm_plane_cleanup(plane);
444 }
445 
loongson_format_mod_supported(struct drm_plane *plane, uint32_t format, uint64_t modifier)446 static bool loongson_format_mod_supported(struct drm_plane *plane,
447 					   uint32_t format, uint64_t modifier)
448 {
449 	return (modifier == DRM_FORMAT_MOD_LINEAR);
450 }
451 
452 static const struct drm_plane_funcs loongson_plane_funcs = {
453 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
454 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
455 	.destroy = loongson_plane_destroy,
456 	.disable_plane = drm_atomic_helper_disable_plane,
457 	.reset = drm_atomic_helper_plane_reset,
458 	.update_plane = drm_atomic_helper_update_plane,
459 	.format_mod_supported = loongson_format_mod_supported,
460 };
461 
462 static const struct drm_plane_helper_funcs loongson_plane_helper_funcs = {
463 	.atomic_update = loongson_plane_atomic_update,
464 };
465 
466 /**
467  * loongosn_crtc_init
468  *
469  * @ldev: point to the loongson_drm_device structure
470  *
471  * Init CRTC
472  */
loongson_crtc_init(struct loongson_drm_device *ldev)473 int loongson_crtc_init(struct loongson_drm_device *ldev)
474 {
475 	int i, ret;
476 	extern bool hw_cursor;
477 
478 	for(i=0;i<ldev->num_crtc;i++){
479 		ldev->lcrtc[i].ldev = ldev;
480 		ldev->lcrtc[i].crtc_id = i;
481 
482 		ldev->lcrtc[i].cfg_reg = CFG_RESET;
483 		ldev->lcrtc[i].primary = devm_kzalloc(ldev->dev->dev, sizeof(*ldev->lcrtc[i].primary), GFP_KERNEL);
484 		if (!ldev->lcrtc[i].primary)
485 			return -ENOMEM;
486 
487 		ret = drm_universal_plane_init(ldev->dev, ldev->lcrtc[i].primary, BIT(i), &loongson_plane_funcs,
488 				       loongson_formats, ARRAY_SIZE(loongson_formats),
489 				       loongson_format_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL);
490 		if (ret)
491 			return ret;
492 
493 		drm_plane_helper_add(ldev->lcrtc[i].primary, &loongson_plane_helper_funcs);
494 
495 		if (hw_cursor)
496 			ret = drm_crtc_init_with_planes(ldev->dev, &ldev->lcrtc[i].base,ldev->lcrtc[i].primary,
497 					NULL, &loongson_hwcursor_crtc_funcs, NULL);
498 		else
499 			ret = drm_crtc_init_with_planes(ldev->dev, &ldev->lcrtc[i].base,ldev->lcrtc[i].primary,
500 					NULL, &loongson_swcursor_crtc_funcs, NULL);
501 		if (ret) {
502 			loongson_plane_destroy(ldev->lcrtc[i].primary);
503 			return ret;
504 		}
505 		drm_crtc_helper_add(&ldev->lcrtc[i].base, &loongson_crtc_helper_funcs);
506 	}
507 
508 	return 0;
509 }
510