brintos

brintos / linux-shallow public Read only

0
0
Text · 22.7 KiB · fb71658 Raw
832 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2010 Matt Turner.4 * Copyright 2012 Red Hat5 *6 * Authors: Matthew Garrett7 *	    Matt Turner8 *	    Dave Airlie9 */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 object135 *   * Then addr is the number of _pixels_ (not bytes) from the start of136 *     VRAM to the first pixel we want to display. (divided by 2 for 32bit137 *     framebuffers)138 *   * addr is stored in the CRTCEXT0, CRTCC and CRTCD registers139 *      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 the145 *  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 also161	 * 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	else236		misc &= ~MGAREG_MISC_HSYNCPOL;237 238	if (mode->flags & DRM_MODE_FLAG_NVSYNC)239		misc |= MGAREG_MISC_VSYNCPOL;240	else241		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. The300 * offset is a multiple of the pixel size and depends on the display301 * 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	else312		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		else362			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; put408	 *       msleep(50) before changing SCROFF409	 */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; put429	 *       msleep(50) before changing SCROFF430	 */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 plane451 */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_INVALID464};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 * CRTC568 */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 the578	 * 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		else647			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	else670		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	else693		__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 config728 */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 to736	 * drm_connector_helper_funcs.get_modes by trying to read the737	 * display modes. Protect access to I/O registers by acquiring738	 * 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_XRGB8888778	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 device792	 * 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}832