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