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
18static 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*/
35static 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
67static 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
99int 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
152int 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