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
29DEFINE_SPINLOCK(loongson_crtc_lock);
30
31static 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
49static 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 */
77static 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 */
137static 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
181static 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 */
200static 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
211static 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
224static const uint32_t loongson_formats[] = {
225	DRM_FORMAT_RGB565,
226	DRM_FORMAT_RGB888,
227	DRM_FORMAT_XRGB8888,
228	DRM_FORMAT_ARGB8888,
229};
230
231static const uint64_t loongson_format_modifiers[] = {
232	DRM_FORMAT_MOD_LINEAR,
233	DRM_FORMAT_MOD_INVALID
234};
235
236static 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
258u32 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
264void 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
270static 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
327static 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
341vblank_on:
342	drm_crtc_vblank_on(crtc);
343}
344
345static 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
366static 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
383static 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 */
433static 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
441static void loongson_plane_destroy(struct drm_plane *plane)
442{
443	drm_plane_cleanup(plane);
444}
445
446static 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
452static 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
462static 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 */
473int 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