xref: /linux/drivers/gpu/drm/mgag200/mgag200_mode.c (revision 566ab427f827b0256d3e8ce0235d088e6a9c28bd)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2010 Matt Turner.
4  * Copyright 2012 Red Hat
5  *
6  * Authors: Matthew Garrett
7  *	    Matt Turner
8  *	    Dave Airlie
9  */
10 
11 #include <linux/delay.h>
12 #include <linux/iosys-map.h>
13 
14 #include <drm/drm_atomic.h>
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_damage_helper.h>
17 #include <drm/drm_edid.h>
18 #include <drm/drm_format_helper.h>
19 #include <drm/drm_fourcc.h>
20 #include <drm/drm_framebuffer.h>
21 #include <drm/drm_gem_atomic_helper.h>
22 #include <drm/drm_gem_framebuffer_helper.h>
23 #include <drm/drm_panic.h>
24 #include <drm/drm_print.h>
25 #include <drm/drm_vblank.h>
26 
27 #include "mgag200_ddc.h"
28 #include "mgag200_drv.h"
29 
30 /*
31  * This file contains setup code for the CRTC.
32  */
33 
34 void mgag200_crtc_set_gamma_linear(struct mga_device *mdev,
35 				   const struct drm_format_info *format)
36 {
37 	int i;
38 
39 	WREG8(DAC_INDEX + MGA1064_INDEX, 0);
40 
41 	switch (format->format) {
42 	case DRM_FORMAT_RGB565:
43 		/* Use better interpolation, to take 32 values from 0 to 255 */
44 		for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) {
45 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4);
46 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16);
47 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4);
48 		}
49 		/* Green has one more bit, so add padding with 0 for red and blue. */
50 		for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) {
51 			WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
52 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16);
53 			WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
54 		}
55 		break;
56 	case DRM_FORMAT_RGB888:
57 	case DRM_FORMAT_XRGB8888:
58 		for (i = 0; i < MGAG200_LUT_SIZE; i++) {
59 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
60 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
61 			WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
62 		}
63 		break;
64 	default:
65 		drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
66 			      &format->format);
67 		break;
68 	}
69 }
70 
71 void mgag200_crtc_set_gamma(struct mga_device *mdev,
72 			    const struct drm_format_info *format,
73 			    struct drm_color_lut *lut)
74 {
75 	int i;
76 
77 	WREG8(DAC_INDEX + MGA1064_INDEX, 0);
78 
79 	switch (format->format) {
80 	case DRM_FORMAT_RGB565:
81 		/* Use better interpolation, to take 32 values from lut[0] to lut[255] */
82 		for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) {
83 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8);
84 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8);
85 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8);
86 		}
87 		/* Green has one more bit, so add padding with 0 for red and blue. */
88 		for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) {
89 			WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
90 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8);
91 			WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
92 		}
93 		break;
94 	case DRM_FORMAT_RGB888:
95 	case DRM_FORMAT_XRGB8888:
96 		for (i = 0; i < MGAG200_LUT_SIZE; i++) {
97 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8);
98 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8);
99 			WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8);
100 		}
101 		break;
102 	default:
103 		drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
104 			      &format->format);
105 		break;
106 	}
107 }
108 
109 static inline void mga_wait_vsync(struct mga_device *mdev)
110 {
111 	unsigned long timeout = jiffies + HZ/10;
112 	unsigned int status = 0;
113 
114 	do {
115 		status = RREG32(MGAREG_STATUS);
116 	} while ((status & 0x08) && time_before(jiffies, timeout));
117 	timeout = jiffies + HZ/10;
118 	status = 0;
119 	do {
120 		status = RREG32(MGAREG_STATUS);
121 	} while (!(status & 0x08) && time_before(jiffies, timeout));
122 }
123 
124 static inline void mga_wait_busy(struct mga_device *mdev)
125 {
126 	unsigned long timeout = jiffies + HZ;
127 	unsigned int status = 0;
128 	do {
129 		status = RREG8(MGAREG_STATUS + 2);
130 	} while ((status & 0x01) && time_before(jiffies, timeout));
131 }
132 
133 /*
134  * This is how the framebuffer base address is stored in g200 cards:
135  *   * Assume @offset is the gpu_addr variable of the framebuffer object
136  *   * Then addr is the number of _pixels_ (not bytes) from the start of
137  *     VRAM to the first pixel we want to display. (divided by 2 for 32bit
138  *     framebuffers)
139  *   * addr is stored in the CRTCEXT0, CRTCC and CRTCD registers
140  *      addr<20> -> CRTCEXT0<6>
141  *      addr<19-16> -> CRTCEXT0<3-0>
142  *      addr<15-8> -> CRTCC<7-0>
143  *      addr<7-0> -> CRTCD<7-0>
144  *
145  *  CRTCEXT0 has to be programmed last to trigger an update and make the
146  *  new addr variable take effect.
147  */
148 static void mgag200_set_startadd(struct mga_device *mdev,
149 				 unsigned long offset)
150 {
151 	struct drm_device *dev = &mdev->base;
152 	u32 startadd;
153 	u8 crtcc, crtcd, crtcext0;
154 
155 	startadd = offset / 8;
156 
157 	if (startadd > 0)
158 		drm_WARN_ON_ONCE(dev, mdev->info->bug_no_startadd);
159 
160 	/*
161 	 * Can't store addresses any higher than that, but we also
162 	 * don't have more than 16 MiB of memory, so it should be fine.
163 	 */
164 	drm_WARN_ON(dev, startadd > 0x1fffff);
165 
166 	RREG_ECRT(0x00, crtcext0);
167 
168 	crtcc = (startadd >> 8) & 0xff;
169 	crtcd = startadd & 0xff;
170 	crtcext0 &= 0xb0;
171 	crtcext0 |= ((startadd >> 14) & BIT(6)) |
172 		    ((startadd >> 16) & 0x0f);
173 
174 	WREG_CRT(0x0c, crtcc);
175 	WREG_CRT(0x0d, crtcd);
176 	WREG_ECRT(0x00, crtcext0);
177 }
178 
179 void mgag200_init_registers(struct mga_device *mdev)
180 {
181 	u8 crtc11, misc;
182 
183 	WREG_SEQ(2, 0x0f);
184 	WREG_SEQ(3, 0x00);
185 	WREG_SEQ(4, 0x0e);
186 
187 	WREG_CRT(10, 0);
188 	WREG_CRT(11, 0);
189 	WREG_CRT(12, 0);
190 	WREG_CRT(13, 0);
191 	WREG_CRT(14, 0);
192 	WREG_CRT(15, 0);
193 
194 	RREG_CRT(0x11, crtc11);
195 	crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT |
196 		    MGAREG_CRTC11_VINTEN |
197 		    MGAREG_CRTC11_VINTCLR);
198 	WREG_CRT(0x11, crtc11);
199 
200 	misc = RREG8(MGA_MISC_IN);
201 	misc |= MGAREG_MISC_IOADSEL;
202 	WREG8(MGA_MISC_OUT, misc);
203 }
204 
205 void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode,
206 			   bool set_vidrst)
207 {
208 	unsigned int hdispend, hsyncstr, hsyncend, htotal, hblkstr, hblkend;
209 	unsigned int vdispend, vsyncstr, vsyncend, vtotal, vblkstr, vblkend;
210 	unsigned int linecomp;
211 	u8 misc, crtcext1, crtcext2, crtcext5;
212 
213 	hdispend = mode->crtc_hdisplay / 8 - 1;
214 	hsyncstr = mode->crtc_hsync_start / 8 - 1;
215 	hsyncend = mode->crtc_hsync_end / 8 - 1;
216 	htotal = mode->crtc_htotal / 8 - 1;
217 	/* Work around hardware quirk */
218 	if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04)
219 		htotal++;
220 	hblkstr = mode->crtc_hblank_start / 8 - 1;
221 	hblkend = htotal;
222 
223 	vdispend = mode->crtc_vdisplay - 1;
224 	vsyncstr = mode->crtc_vsync_start - 1;
225 	vsyncend = mode->crtc_vsync_end - 1;
226 	vtotal = mode->crtc_vtotal - 2;
227 	vblkstr = mode->crtc_vblank_start;
228 	vblkend = vtotal + 1;
229 
230 	/*
231 	 * There's no VBLANK interrupt on Matrox chipsets, so we use
232 	 * the VLINE interrupt instead. It triggers when the current
233 	 * <linecomp> has been reached. For VBLANK, this is the first
234 	 * non-visible line at the bottom of the screen. Therefore,
235 	 * keep <linecomp> in sync with <vblkstr>.
236 	 */
237 	linecomp = vblkstr;
238 
239 	misc = RREG8(MGA_MISC_IN);
240 
241 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
242 		misc |= MGAREG_MISC_HSYNCPOL;
243 	else
244 		misc &= ~MGAREG_MISC_HSYNCPOL;
245 
246 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
247 		misc |= MGAREG_MISC_VSYNCPOL;
248 	else
249 		misc &= ~MGAREG_MISC_VSYNCPOL;
250 
251 	crtcext1 = (((htotal - 4) & 0x100) >> 8) |
252 		   ((hblkstr & 0x100) >> 7) |
253 		   ((hsyncstr & 0x100) >> 6) |
254 		    (hblkend & 0x40);
255 	if (set_vidrst)
256 		crtcext1 |= MGAREG_CRTCEXT1_VRSTEN |
257 			    MGAREG_CRTCEXT1_HRSTEN;
258 
259 	crtcext2 = ((vtotal & 0xc00) >> 10) |
260 		   ((vdispend & 0x400) >> 8) |
261 		   ((vblkstr & 0xc00) >> 7) |
262 		   ((vsyncstr & 0xc00) >> 5) |
263 		   ((linecomp & 0x400) >> 3);
264 	crtcext5 = 0x00;
265 
266 	WREG_CRT(0x00, htotal - 4);
267 	WREG_CRT(0x01, hdispend);
268 	WREG_CRT(0x02, hblkstr);
269 	WREG_CRT(0x03, (hblkend & 0x1f) | 0x80);
270 	WREG_CRT(0x04, hsyncstr);
271 	WREG_CRT(0x05, ((hblkend & 0x20) << 2) | (hsyncend & 0x1f));
272 	WREG_CRT(0x06, vtotal & 0xff);
273 	WREG_CRT(0x07, ((vtotal & 0x100) >> 8) |
274 		       ((vdispend & 0x100) >> 7) |
275 		       ((vsyncstr & 0x100) >> 6) |
276 		       ((vblkstr & 0x100) >> 5) |
277 		       ((linecomp & 0x100) >> 4) |
278 		       ((vtotal & 0x200) >> 4) |
279 		       ((vdispend & 0x200) >> 3) |
280 		       ((vsyncstr & 0x200) >> 2));
281 	WREG_CRT(0x09, ((vblkstr & 0x200) >> 4) |
282 		       ((linecomp & 0x200) >> 3));
283 	WREG_CRT(0x10, vsyncstr & 0xff);
284 	WREG_CRT(0x11, (vsyncend & 0x0f) | 0x20);
285 	WREG_CRT(0x12, vdispend & 0xff);
286 	WREG_CRT(0x14, 0);
287 	WREG_CRT(0x15, vblkstr & 0xff);
288 	WREG_CRT(0x16, vblkend & 0xff);
289 	WREG_CRT(0x17, 0xc3);
290 	WREG_CRT(0x18, linecomp & 0xff);
291 
292 	WREG_ECRT(0x01, crtcext1);
293 	WREG_ECRT(0x02, crtcext2);
294 	WREG_ECRT(0x05, crtcext5);
295 
296 	WREG8(MGA_MISC_OUT, misc);
297 }
298 
299 static u8 mgag200_get_bpp_shift(const struct drm_format_info *format)
300 {
301 	static const u8 bpp_shift[] = {0, 1, 0, 2};
302 
303 	return bpp_shift[format->cpp[0] - 1];
304 }
305 
306 /*
307  * Calculates the HW offset value from the framebuffer's pitch. The
308  * offset is a multiple of the pixel size and depends on the display
309  * format.
310  */
311 static u32 mgag200_calculate_offset(struct mga_device *mdev,
312 				    const struct drm_framebuffer *fb)
313 {
314 	u32 offset = fb->pitches[0] / fb->format->cpp[0];
315 	u8 bppshift = mgag200_get_bpp_shift(fb->format);
316 
317 	if (fb->format->cpp[0] * 8 == 24)
318 		offset = (offset * 3) >> (4 - bppshift);
319 	else
320 		offset = offset >> (4 - bppshift);
321 
322 	return offset;
323 }
324 
325 static void mgag200_set_offset(struct mga_device *mdev,
326 			       const struct drm_framebuffer *fb)
327 {
328 	u8 crtc13, crtcext0;
329 	u32 offset = mgag200_calculate_offset(mdev, fb);
330 
331 	RREG_ECRT(0, crtcext0);
332 
333 	crtc13 = offset & 0xff;
334 
335 	crtcext0 &= ~MGAREG_CRTCEXT0_OFFSET_MASK;
336 	crtcext0 |= (offset >> 4) & MGAREG_CRTCEXT0_OFFSET_MASK;
337 
338 	WREG_CRT(0x13, crtc13);
339 	WREG_ECRT(0x00, crtcext0);
340 }
341 
342 void mgag200_set_format_regs(struct mga_device *mdev, const struct drm_format_info *format)
343 {
344 	struct drm_device *dev = &mdev->base;
345 	unsigned int bpp, bppshift, scale;
346 	u8 crtcext3, xmulctrl;
347 
348 	bpp = format->cpp[0] * 8;
349 
350 	bppshift = mgag200_get_bpp_shift(format);
351 	switch (bpp) {
352 	case 24:
353 		scale = ((1 << bppshift) * 3) - 1;
354 		break;
355 	default:
356 		scale = (1 << bppshift) - 1;
357 		break;
358 	}
359 
360 	RREG_ECRT(3, crtcext3);
361 
362 	switch (bpp) {
363 	case 8:
364 		xmulctrl = MGA1064_MUL_CTL_8bits;
365 		break;
366 	case 16:
367 		if (format->depth == 15)
368 			xmulctrl = MGA1064_MUL_CTL_15bits;
369 		else
370 			xmulctrl = MGA1064_MUL_CTL_16bits;
371 		break;
372 	case 24:
373 		xmulctrl = MGA1064_MUL_CTL_24bits;
374 		break;
375 	case 32:
376 		xmulctrl = MGA1064_MUL_CTL_32_24bits;
377 		break;
378 	default:
379 		/* BUG: We should have caught this problem already. */
380 		drm_WARN_ON(dev, "invalid format depth\n");
381 		return;
382 	}
383 
384 	crtcext3 &= ~GENMASK(2, 0);
385 	crtcext3 |= scale;
386 
387 	WREG_DAC(MGA1064_MUL_CTL, xmulctrl);
388 
389 	WREG_GFX(0, 0x00);
390 	WREG_GFX(1, 0x00);
391 	WREG_GFX(2, 0x00);
392 	WREG_GFX(3, 0x00);
393 	WREG_GFX(4, 0x00);
394 	WREG_GFX(5, 0x40);
395 	/* GCTL6 should be 0x05, but we configure memmapsl to 0xb8000 (text mode),
396 	 * so that it doesn't hang when running kexec/kdump on G200_SE rev42.
397 	 */
398 	WREG_GFX(6, 0x0d);
399 	WREG_GFX(7, 0x0f);
400 	WREG_GFX(8, 0x0f);
401 
402 	WREG_ECRT(3, crtcext3);
403 }
404 
405 void mgag200_enable_display(struct mga_device *mdev)
406 {
407 	u8 seq0, crtcext1;
408 
409 	RREG_SEQ(0x00, seq0);
410 	seq0 |= MGAREG_SEQ0_SYNCRST |
411 		MGAREG_SEQ0_ASYNCRST;
412 	WREG_SEQ(0x00, seq0);
413 
414 	/*
415 	 * TODO: replace busy waiting with vblank IRQ; put
416 	 *       msleep(50) before changing SCROFF
417 	 */
418 	mga_wait_vsync(mdev);
419 	mga_wait_busy(mdev);
420 
421 	RREG_ECRT(0x01, crtcext1);
422 	crtcext1 &= ~MGAREG_CRTCEXT1_VSYNCOFF;
423 	crtcext1 &= ~MGAREG_CRTCEXT1_HSYNCOFF;
424 	WREG_ECRT(0x01, crtcext1);
425 }
426 
427 static void mgag200_disable_display(struct mga_device *mdev)
428 {
429 	u8 seq0, crtcext1;
430 
431 	RREG_SEQ(0x00, seq0);
432 	seq0 &= ~MGAREG_SEQ0_SYNCRST;
433 	WREG_SEQ(0x00, seq0);
434 
435 	/*
436 	 * TODO: replace busy waiting with vblank IRQ; put
437 	 *       msleep(50) before changing SCROFF
438 	 */
439 	mga_wait_vsync(mdev);
440 	mga_wait_busy(mdev);
441 
442 	RREG_ECRT(0x01, crtcext1);
443 	crtcext1 |= MGAREG_CRTCEXT1_VSYNCOFF |
444 		    MGAREG_CRTCEXT1_HSYNCOFF;
445 	WREG_ECRT(0x01, crtcext1);
446 }
447 
448 static void mgag200_handle_damage(struct mga_device *mdev, const struct iosys_map *vmap,
449 				  struct drm_framebuffer *fb, struct drm_rect *clip)
450 {
451 	struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(mdev->vram);
452 
453 	iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
454 	drm_fb_memcpy(&dst, fb->pitches, vmap, fb, clip);
455 }
456 
457 /*
458  * Primary plane
459  */
460 
461 const uint32_t mgag200_primary_plane_formats[] = {
462 	DRM_FORMAT_XRGB8888,
463 	DRM_FORMAT_RGB565,
464 	DRM_FORMAT_RGB888,
465 };
466 
467 const size_t mgag200_primary_plane_formats_size = ARRAY_SIZE(mgag200_primary_plane_formats);
468 
469 const uint64_t mgag200_primary_plane_fmtmods[] = {
470 	DRM_FORMAT_MOD_LINEAR,
471 	DRM_FORMAT_MOD_INVALID
472 };
473 
474 int mgag200_primary_plane_helper_atomic_check(struct drm_plane *plane,
475 					      struct drm_atomic_state *new_state)
476 {
477 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
478 	struct drm_framebuffer *new_fb = new_plane_state->fb;
479 	struct drm_framebuffer *fb = NULL;
480 	struct drm_crtc *new_crtc = new_plane_state->crtc;
481 	struct drm_crtc_state *new_crtc_state = NULL;
482 	struct mgag200_crtc_state *new_mgag200_crtc_state;
483 	int ret;
484 
485 	if (new_crtc)
486 		new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_crtc);
487 
488 	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
489 						  DRM_PLANE_NO_SCALING,
490 						  DRM_PLANE_NO_SCALING,
491 						  false, true);
492 	if (ret)
493 		return ret;
494 	else if (!new_plane_state->visible)
495 		return 0;
496 
497 	if (plane->state)
498 		fb = plane->state->fb;
499 
500 	if (!fb || (fb->format != new_fb->format))
501 		new_crtc_state->mode_changed = true; /* update PLL settings */
502 
503 	new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
504 	new_mgag200_crtc_state->format = new_fb->format;
505 
506 	return 0;
507 }
508 
509 void mgag200_primary_plane_helper_atomic_update(struct drm_plane *plane,
510 						struct drm_atomic_state *old_state)
511 {
512 	struct drm_device *dev = plane->dev;
513 	struct mga_device *mdev = to_mga_device(dev);
514 	struct drm_plane_state *plane_state = plane->state;
515 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane);
516 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
517 	struct drm_framebuffer *fb = plane_state->fb;
518 	struct drm_atomic_helper_damage_iter iter;
519 	struct drm_rect damage;
520 
521 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
522 	drm_atomic_for_each_plane_damage(&iter, &damage) {
523 		mgag200_handle_damage(mdev, shadow_plane_state->data, fb, &damage);
524 	}
525 
526 	/* Always scanout image at VRAM offset 0 */
527 	mgag200_set_startadd(mdev, (u32)0);
528 	mgag200_set_offset(mdev, fb);
529 }
530 
531 void mgag200_primary_plane_helper_atomic_enable(struct drm_plane *plane,
532 						struct drm_atomic_state *state)
533 {
534 	struct drm_device *dev = plane->dev;
535 	struct mga_device *mdev = to_mga_device(dev);
536 	u8 seq1;
537 
538 	RREG_SEQ(0x01, seq1);
539 	seq1 &= ~MGAREG_SEQ1_SCROFF;
540 	WREG_SEQ(0x01, seq1);
541 	msleep(20);
542 }
543 
544 void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane,
545 						 struct drm_atomic_state *old_state)
546 {
547 	struct drm_device *dev = plane->dev;
548 	struct mga_device *mdev = to_mga_device(dev);
549 	u8 seq1;
550 
551 	RREG_SEQ(0x01, seq1);
552 	seq1 |= MGAREG_SEQ1_SCROFF;
553 	WREG_SEQ(0x01, seq1);
554 	msleep(20);
555 }
556 
557 int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
558 						    struct drm_scanout_buffer *sb)
559 {
560 	struct mga_device *mdev = to_mga_device(plane->dev);
561 	struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(mdev->vram);
562 
563 	if (plane->state && plane->state->fb) {
564 		sb->format = plane->state->fb->format;
565 		sb->width = plane->state->fb->width;
566 		sb->height = plane->state->fb->height;
567 		sb->pitch[0] = plane->state->fb->pitches[0];
568 		sb->map[0] = map;
569 		return 0;
570 	}
571 	return -ENODEV;
572 }
573 
574 /*
575  * CRTC
576  */
577 
578 enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc,
579 						    const struct drm_display_mode *mode)
580 {
581 	struct mga_device *mdev = to_mga_device(crtc->dev);
582 	const struct mgag200_device_info *info = mdev->info;
583 
584 	/*
585 	 * Some devices have additional limits on the size of the
586 	 * display mode.
587 	 */
588 	if (mode->hdisplay > info->max_hdisplay)
589 		return MODE_VIRTUAL_X;
590 	if (mode->vdisplay > info->max_vdisplay)
591 		return MODE_VIRTUAL_Y;
592 
593 	if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
594 	    (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
595 		return MODE_H_ILLEGAL;
596 	}
597 
598 	if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
599 	    mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
600 	    mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
601 	    mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) {
602 		return MODE_BAD;
603 	}
604 
605 	return MODE_OK;
606 }
607 
608 int mgag200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
609 {
610 	struct drm_device *dev = crtc->dev;
611 	struct mga_device *mdev = to_mga_device(dev);
612 	const struct mgag200_device_funcs *funcs = mdev->funcs;
613 	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
614 	struct drm_property_blob *new_gamma_lut = new_crtc_state->gamma_lut;
615 	int ret;
616 
617 	if (!new_crtc_state->enable)
618 		return 0;
619 
620 	ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
621 	if (ret)
622 		return ret;
623 
624 	if (new_crtc_state->mode_changed) {
625 		if (funcs->pixpllc_atomic_check) {
626 			ret = funcs->pixpllc_atomic_check(crtc, new_state);
627 			if (ret)
628 				return ret;
629 		}
630 	}
631 
632 	if (new_crtc_state->color_mgmt_changed && new_gamma_lut) {
633 		if (new_gamma_lut->length != MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) {
634 			drm_dbg(dev, "Wrong size for gamma_lut %zu\n", new_gamma_lut->length);
635 			return -EINVAL;
636 		}
637 	}
638 
639 	return 0;
640 }
641 
642 void mgag200_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *old_state)
643 {
644 	struct drm_crtc_state *crtc_state = crtc->state;
645 	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
646 	struct drm_device *dev = crtc->dev;
647 	struct mga_device *mdev = to_mga_device(dev);
648 	struct drm_pending_vblank_event *event;
649 	unsigned long flags;
650 
651 	if (crtc_state->enable && crtc_state->color_mgmt_changed) {
652 		const struct drm_format_info *format = mgag200_crtc_state->format;
653 
654 		if (crtc_state->gamma_lut)
655 			mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data);
656 		else
657 			mgag200_crtc_set_gamma_linear(mdev, format);
658 	}
659 
660 	event = crtc->state->event;
661 	if (event) {
662 		crtc->state->event = NULL;
663 
664 		spin_lock_irqsave(&dev->event_lock, flags);
665 		if (drm_crtc_vblank_get(crtc) != 0)
666 			drm_crtc_send_vblank_event(crtc, event);
667 		else
668 			drm_crtc_arm_vblank_event(crtc, event);
669 		spin_unlock_irqrestore(&dev->event_lock, flags);
670 	}
671 }
672 
673 void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *old_state)
674 {
675 	struct drm_device *dev = crtc->dev;
676 	struct mga_device *mdev = to_mga_device(dev);
677 	const struct mgag200_device_funcs *funcs = mdev->funcs;
678 	struct drm_crtc_state *crtc_state = crtc->state;
679 	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
680 	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
681 	const struct drm_format_info *format = mgag200_crtc_state->format;
682 
683 	mgag200_set_format_regs(mdev, format);
684 	mgag200_set_mode_regs(mdev, adjusted_mode, mgag200_crtc_state->set_vidrst);
685 
686 	if (funcs->pixpllc_atomic_update)
687 		funcs->pixpllc_atomic_update(crtc, old_state);
688 
689 	if (crtc_state->gamma_lut)
690 		mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data);
691 	else
692 		mgag200_crtc_set_gamma_linear(mdev, format);
693 
694 	mgag200_enable_display(mdev);
695 
696 	drm_crtc_vblank_on(crtc);
697 }
698 
699 void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *old_state)
700 {
701 	struct mga_device *mdev = to_mga_device(crtc->dev);
702 
703 	drm_crtc_vblank_off(crtc);
704 
705 	mgag200_disable_display(mdev);
706 }
707 
708 bool mgag200_crtc_helper_get_scanout_position(struct drm_crtc *crtc, bool in_vblank_irq,
709 					      int *vpos, int *hpos,
710 					      ktime_t *stime, ktime_t *etime,
711 					      const struct drm_display_mode *mode)
712 {
713 	struct mga_device *mdev = to_mga_device(crtc->dev);
714 	u32 vcount;
715 
716 	if (stime)
717 		*stime = ktime_get();
718 
719 	if (vpos) {
720 		vcount = RREG32(MGAREG_VCOUNT);
721 		*vpos = vcount & GENMASK(11, 0);
722 	}
723 
724 	if (hpos)
725 		*hpos = mode->htotal >> 1; // near middle of scanline on average
726 
727 	if (etime)
728 		*etime = ktime_get();
729 
730 	return true;
731 }
732 
733 void mgag200_crtc_reset(struct drm_crtc *crtc)
734 {
735 	struct mgag200_crtc_state *mgag200_crtc_state;
736 
737 	if (crtc->state)
738 		crtc->funcs->atomic_destroy_state(crtc, crtc->state);
739 
740 	mgag200_crtc_state = kzalloc(sizeof(*mgag200_crtc_state), GFP_KERNEL);
741 	if (mgag200_crtc_state)
742 		__drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base);
743 	else
744 		__drm_atomic_helper_crtc_reset(crtc, NULL);
745 }
746 
747 struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
748 {
749 	struct drm_crtc_state *crtc_state = crtc->state;
750 	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
751 	struct mgag200_crtc_state *new_mgag200_crtc_state;
752 
753 	if (!crtc_state)
754 		return NULL;
755 
756 	new_mgag200_crtc_state = kzalloc(sizeof(*new_mgag200_crtc_state), GFP_KERNEL);
757 	if (!new_mgag200_crtc_state)
758 		return NULL;
759 	__drm_atomic_helper_crtc_duplicate_state(crtc, &new_mgag200_crtc_state->base);
760 
761 	new_mgag200_crtc_state->format = mgag200_crtc_state->format;
762 	memcpy(&new_mgag200_crtc_state->pixpllc, &mgag200_crtc_state->pixpllc,
763 	       sizeof(new_mgag200_crtc_state->pixpllc));
764 	new_mgag200_crtc_state->set_vidrst = mgag200_crtc_state->set_vidrst;
765 
766 	return &new_mgag200_crtc_state->base;
767 }
768 
769 void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
770 {
771 	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
772 
773 	__drm_atomic_helper_crtc_destroy_state(&mgag200_crtc_state->base);
774 	kfree(mgag200_crtc_state);
775 }
776 
777 int mgag200_crtc_enable_vblank(struct drm_crtc *crtc)
778 {
779 	struct mga_device *mdev = to_mga_device(crtc->dev);
780 	u32 ien;
781 
782 	WREG32(MGAREG_ICLEAR, MGAREG_ICLEAR_VLINEICLR);
783 
784 	ien = RREG32(MGAREG_IEN);
785 	ien |= MGAREG_IEN_VLINEIEN;
786 	WREG32(MGAREG_IEN, ien);
787 
788 	return 0;
789 }
790 
791 void mgag200_crtc_disable_vblank(struct drm_crtc *crtc)
792 {
793 	struct mga_device *mdev = to_mga_device(crtc->dev);
794 	u32 ien;
795 
796 	ien = RREG32(MGAREG_IEN);
797 	ien &= ~(MGAREG_IEN_VLINEIEN);
798 	WREG32(MGAREG_IEN, ien);
799 }
800 
801 /*
802  * Mode config
803  */
804 
805 static void mgag200_mode_config_helper_atomic_commit_tail(struct drm_atomic_state *state)
806 {
807 	struct mga_device *mdev = to_mga_device(state->dev);
808 
809 	/*
810 	 * Concurrent operations could possibly trigger a call to
811 	 * drm_connector_helper_funcs.get_modes by trying to read the
812 	 * display modes. Protect access to I/O registers by acquiring
813 	 * the I/O-register lock.
814 	 */
815 	mutex_lock(&mdev->rmmio_lock);
816 	drm_atomic_helper_commit_tail(state);
817 	mutex_unlock(&mdev->rmmio_lock);
818 }
819 
820 static const struct drm_mode_config_helper_funcs mgag200_mode_config_helper_funcs = {
821 	.atomic_commit_tail = mgag200_mode_config_helper_atomic_commit_tail,
822 };
823 
824 /* Calculates a mode's required memory bandwidth (in KiB/sec). */
825 static uint32_t mgag200_calculate_mode_bandwidth(const struct drm_display_mode *mode,
826 						 unsigned int bits_per_pixel)
827 {
828 	uint32_t total_area, divisor;
829 	uint64_t active_area, pixels_per_second, bandwidth;
830 	uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
831 
832 	divisor = 1024;
833 
834 	if (!mode->htotal || !mode->vtotal || !mode->clock)
835 		return 0;
836 
837 	active_area = mode->hdisplay * mode->vdisplay;
838 	total_area = mode->htotal * mode->vtotal;
839 
840 	pixels_per_second = active_area * mode->clock * 1000;
841 	do_div(pixels_per_second, total_area);
842 
843 	bandwidth = pixels_per_second * bytes_per_pixel * 100;
844 	do_div(bandwidth, divisor);
845 
846 	return (uint32_t)bandwidth;
847 }
848 
849 static enum drm_mode_status mgag200_mode_config_mode_valid(struct drm_device *dev,
850 							   const struct drm_display_mode *mode)
851 {
852 	static const unsigned int max_bpp = 4; // DRM_FORMAT_XRGB8888
853 	struct mga_device *mdev = to_mga_device(dev);
854 	unsigned long fbsize, fbpages, max_fbpages;
855 	const struct mgag200_device_info *info = mdev->info;
856 
857 	max_fbpages = mdev->vram_available >> PAGE_SHIFT;
858 
859 	fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
860 	fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
861 
862 	if (fbpages > max_fbpages)
863 		return MODE_MEM;
864 
865 	/*
866 	 * Test the mode's required memory bandwidth if the device
867 	 * specifies a maximum. Not all devices do though.
868 	 */
869 	if (info->max_mem_bandwidth) {
870 		uint32_t mode_bandwidth = mgag200_calculate_mode_bandwidth(mode, max_bpp * 8);
871 
872 		if (mode_bandwidth > (info->max_mem_bandwidth * 1024))
873 			return MODE_BAD;
874 	}
875 
876 	return MODE_OK;
877 }
878 
879 static const struct drm_mode_config_funcs mgag200_mode_config_funcs = {
880 	.fb_create = drm_gem_fb_create_with_dirty,
881 	.mode_valid = mgag200_mode_config_mode_valid,
882 	.atomic_check = drm_atomic_helper_check,
883 	.atomic_commit = drm_atomic_helper_commit,
884 };
885 
886 int mgag200_mode_config_init(struct mga_device *mdev, resource_size_t vram_available)
887 {
888 	struct drm_device *dev = &mdev->base;
889 	int ret;
890 
891 	mdev->vram_available = vram_available;
892 
893 	ret = drmm_mode_config_init(dev);
894 	if (ret) {
895 		drm_err(dev, "drmm_mode_config_init() failed: %d\n", ret);
896 		return ret;
897 	}
898 
899 	dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH;
900 	dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT;
901 	dev->mode_config.preferred_depth = 24;
902 	dev->mode_config.funcs = &mgag200_mode_config_funcs;
903 	dev->mode_config.helper_private = &mgag200_mode_config_helper_funcs;
904 
905 	return 0;
906 }
907