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  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13 
14 #include <asm/cacheflush.h>
15 #include <drm/drm_gem_cma_helper.h>
16 #include "loongson_drv.h"
17 
flush_scache_range(void *addr, unsigned long size)18 static void flush_scache_range(void *addr, unsigned long size)
19 {
20 	int inc;
21 	unsigned long start, end;
22 
23 	start = (unsigned long)addr;
24 	end   = (unsigned long)addr + size;
25 	inc   = cpu_last_level_cache_line_size();
26 
27 	for (; start < end; start += inc)
28 		flush_cache_line(Cache_LEAF3, start);
29 }
30 
31 /*
32   Hide the cursor off screen. We can't disable the cursor hardware because it
33   takes too long to re-activate and causes momentary corruption
34 */
loongson_hide_cursor(struct drm_crtc *crtc)35 static void loongson_hide_cursor(struct drm_crtc *crtc)
36 {
37 	unsigned long flags;
38 	volatile void __iomem *base;
39 	struct drm_device *dev = crtc->dev;
40 	struct loongson_drm_device *ldev = (struct loongson_drm_device *)dev->dev_private;
41 	struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc);
42 	unsigned int tmp, crtc_id = loongson_crtc->crtc_id;
43 
44 	base = ldev->mmio;
45 	tmp = readl(base + FB_CUR_CFG_REG);
46 	tmp &= ~0xff;
47 	if (clone_mode(ldev)) {
48 		spin_lock_irqsave(&loongson_reglock, flags);
49 		writel(tmp | 0x00, base + FB_CUR_CFG_REG);
50 		spin_unlock_irqrestore(&loongson_reglock, flags);
51 		ldev->cursor_showed = false;
52 	} else {
53 		if (ldev->cursor_crtc_id != crtc_id)
54 			return;
55 
56 		spin_lock_irqsave(&loongson_reglock, flags);
57 		if (crtc_id) {
58 			writel(tmp | 0x10, base + FB_CUR_CFG_REG);
59 		} else {
60 			writel(tmp | 0x00, base + FB_CUR_CFG_REG);
61 		}
62 		spin_unlock_irqrestore(&loongson_reglock, flags);
63 		ldev->cursor_showed = false;
64 	}
65 }
66 
loongson_show_cursor(struct drm_crtc *crtc)67 static void loongson_show_cursor(struct drm_crtc *crtc)
68 {
69 	unsigned long flags;
70 	volatile void __iomem *base;
71 	struct drm_device *dev = crtc->dev;
72 	struct loongson_drm_device *ldev = (struct loongson_drm_device *)dev->dev_private;
73 	struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc);
74 	unsigned int crtc_id = loongson_crtc->crtc_id;
75 
76 	base = ldev->mmio;
77 	if (clone_mode(ldev)) {
78 		spin_lock_irqsave(&loongson_reglock, flags);
79 		writel(0x00050202, base + FB_CUR_CFG_REG);
80 		spin_unlock_irqrestore(&loongson_reglock, flags);
81 		ldev->cursor_crtc_id = 0;
82 		ldev->cursor_showed = true;
83 	} else {
84 		if (ldev->cursor_crtc_id == crtc_id) {
85 			spin_lock_irqsave(&loongson_reglock, flags);
86 			if(crtc_id == 0){
87 				writel(0x00050202, base + FB_CUR_CFG_REG);
88 		        }else{
89 				writel(0x00050212, base + FB_CUR_CFG_REG);
90 			}
91 			spin_unlock_irqrestore(&loongson_reglock, flags);
92 
93 			ldev->cursor_showed = true;
94 			ldev->cursor_crtc_id = crtc_id;
95 		}
96 	}
97 }
98 
loongson_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y)99 int loongson_crtc_cursor_set2(struct drm_crtc *crtc,
100 			struct drm_file *file_priv,
101 			uint32_t handle,
102 			uint32_t width,
103 			uint32_t height,
104 			int32_t hot_x, int32_t hot_y)
105 {
106 	u32 gpu_addr;
107 	unsigned long flags;
108 	unsigned int crtc_id;
109 	volatile void __iomem *base;
110 	struct drm_gem_object *obj;
111 	struct drm_device *dev = crtc->dev;
112 	struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc);
113 	struct loongson_drm_device *ldev = (struct loongson_drm_device *)dev->dev_private;
114 	struct drm_gem_cma_object *cma, *cursor = ldev->cursor;
115 
116 	base = ldev->mmio;
117         crtc_id = loongson_crtc->crtc_id;
118 
119 	if ((width != 32 || height != 32) && handle) {
120 		return -EINVAL;
121 	}
122 
123 	if (!handle || !file_priv) {
124 		loongson_hide_cursor(crtc);
125 		return 0;
126 	}
127 
128 	obj = drm_gem_object_lookup(file_priv, handle);
129 	if (!obj)
130 		return -ENOENT;
131 
132 	cma = to_drm_gem_cma_obj(obj);
133 
134 	flush_scache_range(cma->vaddr, 32*32*4);
135 	memcpy(cursor->vaddr, cma->vaddr, 32*32*4);
136 
137 	/* Program gpu address of cursor buffer */
138 	gpu_addr = ldev->cursor->paddr;
139 	spin_lock_irqsave(&loongson_reglock, flags);
140 	writel(gpu_addr, base + FB_CUR_ADDR_REG);
141 	writel(0x00eeeeee, base + FB_CUR_BACK_REG);
142 	writel(0x00aaaaaa, base + FB_CUR_FORE_REG);
143 	spin_unlock_irqrestore(&loongson_reglock, flags);
144 
145 	loongson_show_cursor(crtc);
146 
147 	drm_gem_object_put(obj);
148 
149 	return 0;
150 }
151 
loongson_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)152 int loongson_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
153 {
154 	unsigned long flags;
155 	unsigned int tmp, crtc_id;
156 	int xorign = 0, yorign = 0;
157 	volatile void __iomem *base;
158 	struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc);
159 	struct loongson_drm_device *ldev = (struct loongson_drm_device *)crtc->dev->dev_private;
160 
161 	base = ldev->mmio;
162         crtc_id = loongson_crtc->crtc_id;
163 
164 	/* upper edge condition */
165 	yorign = y + crtc->y;
166 	if (yorign < 0)
167 		y = 0;
168 
169 	/* left edge conditon */
170 	xorign = x + crtc->x;
171 	if (xorign < 0)
172 		x = 0;
173 
174 	/* move from one crtc to another, check which crtc should he shown
175 	 * the x or y < 0, it means the cursor it out of current review,
176 	 * && xorign/ yorign > 0, it means the cursor is in the framebuffer
177 	 * but not in curren review */
178 	if ((x < 0 && xorign > 0) || (y < 0 && yorign > 0)) {
179 		if(ldev->cursor_crtc_id == crtc_id && !clone_mode(ldev))
180 		 /*the cursor is not show, so hide if the (x,y) is in active crtc*/
181 			loongson_hide_cursor(crtc);
182 		return 0;
183 	}
184 
185 	if (x < 0)
186 		x = 0;
187 	if (y < 0)
188 		y = 0;
189 
190 	tmp = x & 0xffff;
191 	tmp |= y << 16;
192 	spin_lock_irqsave(&loongson_reglock, flags);
193 	writel(tmp, base + FB_CUR_LOC_ADDR_REG);
194 	spin_unlock_irqrestore(&loongson_reglock, flags);
195 	if (ldev->cursor_crtc_id != crtc_id && !clone_mode(ldev)) {
196 		ldev->cursor_crtc_id = crtc_id;
197 		ldev->cursor_showed = false;
198 	}
199 	if (!ldev->cursor_showed)
200 		loongson_show_cursor(crtc);
201 
202 	return 0;
203 }
204