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