]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/mgag200/mgag200_cursor.c
drm/mgag200: Move cursor BO swapping into mgag200_show_cursor()
[linux.git] / drivers / gpu / drm / mgag200 / mgag200_cursor.c
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 static int mgag200_cursor_update(struct mga_device *mdev, void *dst, void *src,
16                                  unsigned int width, unsigned int height)
17 {
18         struct drm_device *dev = mdev->dev;
19         unsigned int i, row, col;
20         uint32_t colour_set[16];
21         uint32_t *next_space = &colour_set[0];
22         uint32_t *palette_iter;
23         uint32_t this_colour;
24         bool found = false;
25         int colour_count = 0;
26         u8 reg_index;
27         u8 this_row[48];
28
29         memset(&colour_set[0], 0, sizeof(uint32_t)*16);
30         /* width*height*4 = 16384 */
31         for (i = 0; i < 16384; i += 4) {
32                 this_colour = ioread32(src + i);
33                 /* No transparency */
34                 if (this_colour>>24 != 0xff &&
35                         this_colour>>24 != 0x0) {
36                         if (warn_transparent) {
37                                 dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n");
38                                 dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
39                                 warn_transparent = false; /* Only tell the user once. */
40                         }
41                         return -EINVAL;
42                 }
43                 /* Don't need to store transparent pixels as colours */
44                 if (this_colour>>24 == 0x0)
45                         continue;
46                 found = false;
47                 for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) {
48                         if (*palette_iter == this_colour) {
49                                 found = true;
50                                 break;
51                         }
52                 }
53                 if (found)
54                         continue;
55                 /* We only support 4bit paletted cursors */
56                 if (colour_count >= 16) {
57                         if (warn_palette) {
58                                 dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n");
59                                 dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
60                                 warn_palette = false; /* Only tell the user once. */
61                         }
62                         return -EINVAL;
63                 }
64                 *next_space = this_colour;
65                 next_space++;
66                 colour_count++;
67         }
68
69         /* Program colours from cursor icon into palette */
70         for (i = 0; i < colour_count; i++) {
71                 if (i <= 2)
72                         reg_index = 0x8 + i*0x4;
73                 else
74                         reg_index = 0x60 + i*0x3;
75                 WREG_DAC(reg_index, colour_set[i] & 0xff);
76                 WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff);
77                 WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff);
78                 BUG_ON((colour_set[i]>>24 & 0xff) != 0xff);
79         }
80
81         /* now write colour indices into hardware cursor buffer */
82         for (row = 0; row < 64; row++) {
83                 memset(&this_row[0], 0, 48);
84                 for (col = 0; col < 64; col++) {
85                         this_colour = ioread32(src + 4*(col + 64*row));
86                         /* write transparent pixels */
87                         if (this_colour>>24 == 0x0) {
88                                 this_row[47 - col/8] |= 0x80>>(col%8);
89                                 continue;
90                         }
91
92                         /* write colour index here */
93                         for (i = 0; i < colour_count; i++) {
94                                 if (colour_set[i] == this_colour) {
95                                         if (col % 2)
96                                                 this_row[col/2] |= i<<4;
97                                         else
98                                                 this_row[col/2] |= i;
99                                         break;
100                                 }
101                         }
102                 }
103                 memcpy_toio(dst + row*48, &this_row[0], 48);
104         }
105
106         return 0;
107 }
108
109 static void mgag200_cursor_set_base(struct mga_device *mdev, u64 address)
110 {
111         u8 addrl = (address >> 10) & 0xff;
112         u8 addrh = (address >> 18) & 0x3f;
113
114         /* Program gpu address of cursor buffer */
115         WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, addrl);
116         WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, addrh);
117 }
118
119 static int mgag200_show_cursor(struct mga_device *mdev, void *src,
120                                unsigned int width, unsigned int height)
121 {
122         struct drm_device *dev = mdev->dev;
123         struct drm_gem_vram_object *pixels_1 = mdev->cursor.pixels_1;
124         struct drm_gem_vram_object *pixels_2 = mdev->cursor.pixels_2;
125         struct drm_gem_vram_object *pixels_current = mdev->cursor.pixels_current;
126         struct drm_gem_vram_object *pixels_next;
127         void *dst;
128         s64 off;
129         int ret;
130
131         if (!pixels_1 || !pixels_2) {
132                 WREG8(MGA_CURPOSXL, 0);
133                 WREG8(MGA_CURPOSXH, 0);
134                 return -ENOTSUPP; /* Didn't allocate space for cursors */
135         }
136
137         if (WARN_ON(pixels_current &&
138                     pixels_1 != pixels_current &&
139                     pixels_2 != pixels_current)) {
140                 return -ENOTSUPP; /* inconsistent state */
141         }
142
143         if (pixels_current == pixels_1)
144                 pixels_next = pixels_2;
145         else
146                 pixels_next = pixels_1;
147
148         dst = drm_gem_vram_vmap(pixels_next);
149         if (IS_ERR(dst)) {
150                 ret = PTR_ERR(dst);
151                 dev_err(&dev->pdev->dev,
152                         "failed to map cursor updates: %d\n", ret);
153                 return ret;
154         }
155         off = drm_gem_vram_offset(pixels_next);
156         if (off < 0) {
157                 ret = (int)off;
158                 dev_err(&dev->pdev->dev,
159                         "failed to get cursor scanout address: %d\n", ret);
160                 goto err_drm_gem_vram_vunmap;
161         }
162
163         ret = mgag200_cursor_update(mdev, dst, src, width, height);
164         if (ret)
165                 goto err_drm_gem_vram_vunmap;
166         mgag200_cursor_set_base(mdev, off);
167
168         /* Adjust cursor control register to turn on the cursor */
169         WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */
170
171         if (pixels_current)
172                 drm_gem_vram_unpin(pixels_current);
173         mdev->cursor.pixels_current = pixels_next;
174
175         drm_gem_vram_vunmap(pixels_next, dst);
176
177         return 0;
178
179 err_drm_gem_vram_vunmap:
180         drm_gem_vram_vunmap(pixels_next, dst);
181         return ret;
182 }
183
184 /*
185  * Hide the cursor off screen. We can't disable the cursor hardware because
186  * it takes too long to re-activate and causes momentary corruption.
187  */
188 static void mgag200_hide_cursor(struct mga_device *mdev)
189 {
190         WREG8(MGA_CURPOSXL, 0);
191         WREG8(MGA_CURPOSXH, 0);
192         if (mdev->cursor.pixels_current)
193                 drm_gem_vram_unpin(mdev->cursor.pixels_current);
194         mdev->cursor.pixels_current = NULL;
195 }
196
197 static void mgag200_move_cursor(struct mga_device *mdev, int x, int y)
198 {
199         if (WARN_ON(x <= 0))
200                 return;
201         if (WARN_ON(y <= 0))
202                 return;
203         if (WARN_ON(x & ~0xffff))
204                 return;
205         if (WARN_ON(y & ~0xffff))
206                 return;
207
208         WREG8(MGA_CURPOSXL, x & 0xff);
209         WREG8(MGA_CURPOSXH, (x>>8) & 0xff);
210
211         WREG8(MGA_CURPOSYL, y & 0xff);
212         WREG8(MGA_CURPOSYH, (y>>8) & 0xff);
213 }
214
215 int mgag200_cursor_init(struct mga_device *mdev)
216 {
217         struct drm_device *dev = mdev->dev;
218
219         /*
220          * Make small buffers to store a hardware cursor (double
221          * buffered icon updates)
222          */
223         mdev->cursor.pixels_1 = drm_gem_vram_create(dev, &dev->vram_mm->bdev,
224                                                     roundup(48*64, PAGE_SIZE),
225                                                     0, 0);
226         mdev->cursor.pixels_2 = drm_gem_vram_create(dev, &dev->vram_mm->bdev,
227                                                     roundup(48*64, PAGE_SIZE),
228                                                     0, 0);
229         if (IS_ERR(mdev->cursor.pixels_2) || IS_ERR(mdev->cursor.pixels_1)) {
230                 mdev->cursor.pixels_1 = NULL;
231                 mdev->cursor.pixels_2 = NULL;
232                 dev_warn(&dev->pdev->dev,
233                         "Could not allocate space for cursors. Not doing hardware cursors.\n");
234         }
235         mdev->cursor.pixels_current = NULL;
236
237         return 0;
238 }
239
240 void mgag200_cursor_fini(struct mga_device *mdev)
241 { }
242
243 int mgag200_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
244                             uint32_t handle, uint32_t width, uint32_t height)
245 {
246         struct drm_device *dev = crtc->dev;
247         struct mga_device *mdev = (struct mga_device *)dev->dev_private;
248         struct drm_gem_object *obj;
249         struct drm_gem_vram_object *gbo = NULL;
250         int ret;
251         u8 *src;
252
253         if (!handle || !file_priv) {
254                 mgag200_hide_cursor(mdev);
255                 return 0;
256         }
257
258         if (width != 64 || height != 64) {
259                 WREG8(MGA_CURPOSXL, 0);
260                 WREG8(MGA_CURPOSXH, 0);
261                 return -EINVAL;
262         }
263
264         obj = drm_gem_object_lookup(file_priv, handle);
265         if (!obj)
266                 return -ENOENT;
267         gbo = drm_gem_vram_of_gem(obj);
268         src = drm_gem_vram_vmap(gbo);
269         if (IS_ERR(src)) {
270                 ret = PTR_ERR(src);
271                 dev_err(&dev->pdev->dev,
272                         "failed to map user buffer updates\n");
273                 goto err_drm_gem_object_put_unlocked;
274         }
275
276         ret = mgag200_show_cursor(mdev, src, width, height);
277         if (ret)
278                 goto err_drm_gem_vram_vunmap;
279
280         /* Now update internal buffer pointers */
281         drm_gem_vram_vunmap(gbo, src);
282         drm_gem_object_put_unlocked(obj);
283
284         return 0;
285 err_drm_gem_vram_vunmap:
286         drm_gem_vram_vunmap(gbo, src);
287 err_drm_gem_object_put_unlocked:
288         drm_gem_object_put_unlocked(obj);
289         return ret;
290 }
291
292 int mgag200_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
293 {
294         struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private;
295
296         /* Our origin is at (64,64) */
297         x += 64;
298         y += 64;
299
300         mgag200_move_cursor(mdev, x, y);
301
302         return 0;
303 }