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