Linux Audio

Check our new training course

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