1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2013 Matrox Graphics
4  *
5  * Author: Christopher Harvey <charvey@matrox.com>
6  */
7 
8 #include <drm/drm_pci.h>
9 
10 #include "mgag200_drv.h"
11 
12 static bool warn_transparent = true;
13 static bool warn_palette = true;
14 
15 /*
16   Hide the cursor off screen. We can't disable the cursor hardware because it
17   takes too long to re-activate and causes momentary corruption
18 */
mga_hide_cursor(struct mga_device * mdev)19 static void mga_hide_cursor(struct mga_device *mdev)
20 {
21 	WREG8(MGA_CURPOSXL, 0);
22 	WREG8(MGA_CURPOSXH, 0);
23 	if (mdev->cursor.pixels_current)
24 		drm_gem_vram_unpin(mdev->cursor.pixels_current);
25 	mdev->cursor.pixels_current = NULL;
26 }
27 
mga_crtc_cursor_set(struct drm_crtc * crtc,struct drm_file * file_priv,uint32_t handle,uint32_t width,uint32_t height)28 int mga_crtc_cursor_set(struct drm_crtc *crtc,
29 			struct drm_file *file_priv,
30 			uint32_t handle,
31 			uint32_t width,
32 			uint32_t height)
33 {
34 	struct drm_device *dev = crtc->dev;
35 	struct mga_device *mdev = (struct mga_device *)dev->dev_private;
36 	struct drm_gem_vram_object *pixels_1 = mdev->cursor.pixels_1;
37 	struct drm_gem_vram_object *pixels_2 = mdev->cursor.pixels_2;
38 	struct drm_gem_vram_object *pixels_current = mdev->cursor.pixels_current;
39 	struct drm_gem_vram_object *pixels_next;
40 	struct drm_gem_object *obj;
41 	struct drm_gem_vram_object *gbo = NULL;
42 	int ret = 0;
43 	u8 *src, *dst;
44 	unsigned int i, row, col;
45 	uint32_t colour_set[16];
46 	uint32_t *next_space = &colour_set[0];
47 	uint32_t *palette_iter;
48 	uint32_t this_colour;
49 	bool found = false;
50 	int colour_count = 0;
51 	s64 gpu_addr;
52 	u64 dst_gpu;
53 	u8 reg_index;
54 	u8 this_row[48];
55 
56 	if (!pixels_1 || !pixels_2) {
57 		WREG8(MGA_CURPOSXL, 0);
58 		WREG8(MGA_CURPOSXH, 0);
59 		return -ENOTSUPP; /* Didn't allocate space for cursors */
60 	}
61 
62 	if (WARN_ON(pixels_current &&
63 		    pixels_1 != pixels_current &&
64 		    pixels_2 != pixels_current)) {
65 		return -ENOTSUPP; /* inconsistent state */
66 	}
67 
68 	if (!handle || !file_priv) {
69 		mga_hide_cursor(mdev);
70 		return 0;
71 	}
72 
73 	if (width != 64 || height != 64) {
74 		WREG8(MGA_CURPOSXL, 0);
75 		WREG8(MGA_CURPOSXH, 0);
76 		return -EINVAL;
77 	}
78 
79 	if (pixels_current == pixels_1)
80 		pixels_next = pixels_2;
81 	else
82 		pixels_next = pixels_1;
83 
84 	obj = drm_gem_object_lookup(file_priv, handle);
85 	if (!obj)
86 		return -ENOENT;
87 	gbo = drm_gem_vram_of_gem(obj);
88 	ret = drm_gem_vram_pin(gbo, 0);
89 	if (ret) {
90 		dev_err(&dev->pdev->dev, "failed to lock user bo\n");
91 		goto err_drm_gem_object_put_unlocked;
92 	}
93 	src = drm_gem_vram_kmap(gbo, true, NULL);
94 	if (IS_ERR(src)) {
95 		ret = PTR_ERR(src);
96 		dev_err(&dev->pdev->dev,
97 			"failed to kmap user buffer updates\n");
98 		goto err_drm_gem_vram_unpin_src;
99 	}
100 
101 	/* Pin and map up-coming buffer to write colour indices */
102 	ret = drm_gem_vram_pin(pixels_next, DRM_GEM_VRAM_PL_FLAG_VRAM);
103 	if (ret) {
104 		dev_err(&dev->pdev->dev,
105 			"failed to pin cursor buffer: %d\n", ret);
106 		goto err_drm_gem_vram_kunmap_src;
107 	}
108 	dst = drm_gem_vram_kmap(pixels_next, true, NULL);
109 	if (IS_ERR(dst)) {
110 		ret = PTR_ERR(dst);
111 		dev_err(&dev->pdev->dev,
112 			"failed to kmap cursor updates: %d\n", ret);
113 		goto err_drm_gem_vram_unpin_dst;
114 	}
115 	gpu_addr = drm_gem_vram_offset(pixels_next);
116 	if (gpu_addr < 0) {
117 		ret = (int)gpu_addr;
118 		dev_err(&dev->pdev->dev,
119 			"failed to get cursor scanout address: %d\n", ret);
120 		goto err_drm_gem_vram_kunmap_dst;
121 	}
122 	dst_gpu = (u64)gpu_addr;
123 
124 	memset(&colour_set[0], 0, sizeof(uint32_t)*16);
125 	/* width*height*4 = 16384 */
126 	for (i = 0; i < 16384; i += 4) {
127 		this_colour = ioread32(src + i);
128 		/* No transparency */
129 		if (this_colour>>24 != 0xff &&
130 			this_colour>>24 != 0x0) {
131 			if (warn_transparent) {
132 				dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n");
133 				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
134 				warn_transparent = false; /* Only tell the user once. */
135 			}
136 			ret = -EINVAL;
137 			goto err_drm_gem_vram_kunmap_dst;
138 		}
139 		/* Don't need to store transparent pixels as colours */
140 		if (this_colour>>24 == 0x0)
141 			continue;
142 		found = false;
143 		for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) {
144 			if (*palette_iter == this_colour) {
145 				found = true;
146 				break;
147 			}
148 		}
149 		if (found)
150 			continue;
151 		/* We only support 4bit paletted cursors */
152 		if (colour_count >= 16) {
153 			if (warn_palette) {
154 				dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n");
155 				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
156 				warn_palette = false; /* Only tell the user once. */
157 			}
158 			ret = -EINVAL;
159 			goto err_drm_gem_vram_kunmap_dst;
160 		}
161 		*next_space = this_colour;
162 		next_space++;
163 		colour_count++;
164 	}
165 
166 	/* Program colours from cursor icon into palette */
167 	for (i = 0; i < colour_count; i++) {
168 		if (i <= 2)
169 			reg_index = 0x8 + i*0x4;
170 		else
171 			reg_index = 0x60 + i*0x3;
172 		WREG_DAC(reg_index, colour_set[i] & 0xff);
173 		WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff);
174 		WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff);
175 		BUG_ON((colour_set[i]>>24 & 0xff) != 0xff);
176 	}
177 
178 	/* now write colour indices into hardware cursor buffer */
179 	for (row = 0; row < 64; row++) {
180 		memset(&this_row[0], 0, 48);
181 		for (col = 0; col < 64; col++) {
182 			this_colour = ioread32(src + 4*(col + 64*row));
183 			/* write transparent pixels */
184 			if (this_colour>>24 == 0x0) {
185 				this_row[47 - col/8] |= 0x80>>(col%8);
186 				continue;
187 			}
188 
189 			/* write colour index here */
190 			for (i = 0; i < colour_count; i++) {
191 				if (colour_set[i] == this_colour) {
192 					if (col % 2)
193 						this_row[col/2] |= i<<4;
194 					else
195 						this_row[col/2] |= i;
196 					break;
197 				}
198 			}
199 		}
200 		memcpy_toio(dst + row*48, &this_row[0], 48);
201 	}
202 
203 	/* Program gpu address of cursor buffer */
204 	WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((dst_gpu>>10) & 0xff));
205 	WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((dst_gpu>>18) & 0x3f));
206 
207 	/* Adjust cursor control register to turn on the cursor */
208 	WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */
209 
210 	/* Now update internal buffer pointers */
211 	if (pixels_current)
212 		drm_gem_vram_unpin(pixels_current);
213 	mdev->cursor.pixels_current = pixels_next;
214 
215 	drm_gem_vram_kunmap(pixels_next);
216 	drm_gem_vram_kunmap(gbo);
217 	drm_gem_vram_unpin(gbo);
218 	drm_gem_object_put_unlocked(obj);
219 
220 	return 0;
221 
222 err_drm_gem_vram_kunmap_dst:
223 	drm_gem_vram_kunmap(pixels_next);
224 err_drm_gem_vram_unpin_dst:
225 	drm_gem_vram_unpin(pixels_next);
226 err_drm_gem_vram_kunmap_src:
227 	drm_gem_vram_kunmap(gbo);
228 err_drm_gem_vram_unpin_src:
229 	drm_gem_vram_unpin(gbo);
230 err_drm_gem_object_put_unlocked:
231 	drm_gem_object_put_unlocked(obj);
232 	return ret;
233 }
234 
mga_crtc_cursor_move(struct drm_crtc * crtc,int x,int y)235 int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
236 {
237 	struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private;
238 	/* Our origin is at (64,64) */
239 	x += 64;
240 	y += 64;
241 
242 	BUG_ON(x <= 0);
243 	BUG_ON(y <= 0);
244 	BUG_ON(x & ~0xffff);
245 	BUG_ON(y & ~0xffff);
246 
247 	WREG8(MGA_CURPOSXL, x & 0xff);
248 	WREG8(MGA_CURPOSXH, (x>>8) & 0xff);
249 
250 	WREG8(MGA_CURPOSYL, y & 0xff);
251 	WREG8(MGA_CURPOSYH, (y>>8) & 0xff);
252 	return 0;
253 }
254