1c51669eaSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2414c4531SDave Airlie /* 3414c4531SDave Airlie * Copyright 2010 Matt Turner. 4414c4531SDave Airlie * Copyright 2012 Red Hat 5414c4531SDave Airlie * 6414c4531SDave Airlie * Authors: Matthew Garrett 7414c4531SDave Airlie * Matt Turner 8414c4531SDave Airlie * Dave Airlie 9414c4531SDave Airlie */ 10414c4531SDave Airlie 11414c4531SDave Airlie #include <linux/delay.h> 127938f421SLucas De Marchi #include <linux/iosys-map.h> 13414c4531SDave Airlie 1488fabb75SThomas Zimmermann #include <drm/drm_atomic_helper.h> 1588fabb75SThomas Zimmermann #include <drm/drm_atomic_state_helper.h> 16760285e7SDavid Howells #include <drm/drm_crtc_helper.h> 17913ec479SThomas Zimmermann #include <drm/drm_damage_helper.h> 18913ec479SThomas Zimmermann #include <drm/drm_format_helper.h> 199f397801SSam Ravnborg #include <drm/drm_fourcc.h> 204862ffaeSThomas Zimmermann #include <drm/drm_gem_atomic_helper.h> 215635b7cfSThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h> 223cb9ae4fSDaniel Vetter #include <drm/drm_plane_helper.h> 2388fabb75SThomas Zimmermann #include <drm/drm_print.h> 24fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 2503e44ad1SThomas Zimmermann #include <drm/drm_simple_kms_helper.h> 26414c4531SDave Airlie 27414c4531SDave Airlie #include "mgag200_drv.h" 28414c4531SDave Airlie 29414c4531SDave Airlie #define MGAG200_LUT_SIZE 256 30414c4531SDave Airlie 31414c4531SDave Airlie /* 32414c4531SDave Airlie * This file contains setup code for the CRTC. 33414c4531SDave Airlie */ 34414c4531SDave Airlie 35c577b2f4SJocelyn Falempe static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, 36c577b2f4SJocelyn Falempe const struct drm_format_info *format) 37414c4531SDave Airlie { 38414c4531SDave Airlie int i; 39414c4531SDave Airlie 40414c4531SDave Airlie WREG8(DAC_INDEX + MGA1064_INDEX, 0); 41414c4531SDave Airlie 42c577b2f4SJocelyn Falempe switch (format->format) { 43c577b2f4SJocelyn Falempe case DRM_FORMAT_RGB565: 44c577b2f4SJocelyn Falempe /* Use better interpolation, to take 32 values from 0 to 255 */ 45c577b2f4SJocelyn Falempe for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { 46c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); 47c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); 48c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); 49de7500eaSEgbert Eich } 50c577b2f4SJocelyn Falempe /* Green has one more bit, so add padding with 0 for red and blue. */ 51c577b2f4SJocelyn Falempe for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { 52c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); 53c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); 54c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); 55de7500eaSEgbert Eich } 56c577b2f4SJocelyn Falempe break; 57c577b2f4SJocelyn Falempe case DRM_FORMAT_RGB888: 58c577b2f4SJocelyn Falempe case DRM_FORMAT_XRGB8888: 59414c4531SDave Airlie for (i = 0; i < MGAG200_LUT_SIZE; i++) { 60c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i); 61c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i); 62c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, i); 63c577b2f4SJocelyn Falempe } 64c577b2f4SJocelyn Falempe break; 65c577b2f4SJocelyn Falempe default: 66c577b2f4SJocelyn Falempe drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", 67c577b2f4SJocelyn Falempe &format->format); 68c577b2f4SJocelyn Falempe break; 69c577b2f4SJocelyn Falempe } 70c577b2f4SJocelyn Falempe } 71c577b2f4SJocelyn Falempe 72c577b2f4SJocelyn Falempe static void mgag200_crtc_set_gamma(struct mga_device *mdev, 73c577b2f4SJocelyn Falempe const struct drm_format_info *format, 74c577b2f4SJocelyn Falempe struct drm_color_lut *lut) 75c577b2f4SJocelyn Falempe { 76c577b2f4SJocelyn Falempe int i; 77c577b2f4SJocelyn Falempe 78c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_INDEX, 0); 79c577b2f4SJocelyn Falempe 80c577b2f4SJocelyn Falempe switch (format->format) { 81c577b2f4SJocelyn Falempe case DRM_FORMAT_RGB565: 82c577b2f4SJocelyn Falempe /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ 83c577b2f4SJocelyn Falempe for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { 84c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8); 85c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); 86c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8); 87c577b2f4SJocelyn Falempe } 88c577b2f4SJocelyn Falempe /* Green has one more bit, so add padding with 0 for red and blue. */ 89c577b2f4SJocelyn Falempe for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { 90c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); 91c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); 92c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); 93c577b2f4SJocelyn Falempe } 94c577b2f4SJocelyn Falempe break; 95c577b2f4SJocelyn Falempe case DRM_FORMAT_RGB888: 96c577b2f4SJocelyn Falempe case DRM_FORMAT_XRGB8888: 97c577b2f4SJocelyn Falempe for (i = 0; i < MGAG200_LUT_SIZE; i++) { 98c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8); 99c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8); 100c577b2f4SJocelyn Falempe WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8); 101c577b2f4SJocelyn Falempe } 102c577b2f4SJocelyn Falempe break; 103c577b2f4SJocelyn Falempe default: 104c577b2f4SJocelyn Falempe drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", 105c577b2f4SJocelyn Falempe &format->format); 106c577b2f4SJocelyn Falempe break; 107414c4531SDave Airlie } 108414c4531SDave Airlie } 109414c4531SDave Airlie 110414c4531SDave Airlie static inline void mga_wait_vsync(struct mga_device *mdev) 111414c4531SDave Airlie { 1123cdc0e8dSChristopher Harvey unsigned long timeout = jiffies + HZ/10; 113414c4531SDave Airlie unsigned int status = 0; 114414c4531SDave Airlie 115414c4531SDave Airlie do { 116414c4531SDave Airlie status = RREG32(MGAREG_Status); 1173cdc0e8dSChristopher Harvey } while ((status & 0x08) && time_before(jiffies, timeout)); 1183cdc0e8dSChristopher Harvey timeout = jiffies + HZ/10; 119414c4531SDave Airlie status = 0; 120414c4531SDave Airlie do { 121414c4531SDave Airlie status = RREG32(MGAREG_Status); 1223cdc0e8dSChristopher Harvey } while (!(status & 0x08) && time_before(jiffies, timeout)); 123414c4531SDave Airlie } 124414c4531SDave Airlie 125414c4531SDave Airlie static inline void mga_wait_busy(struct mga_device *mdev) 126414c4531SDave Airlie { 1273cdc0e8dSChristopher Harvey unsigned long timeout = jiffies + HZ; 128414c4531SDave Airlie unsigned int status = 0; 129414c4531SDave Airlie do { 130414c4531SDave Airlie status = RREG8(MGAREG_Status + 2); 1313cdc0e8dSChristopher Harvey } while ((status & 0x01) && time_before(jiffies, timeout)); 132414c4531SDave Airlie } 133414c4531SDave Airlie 134904347fbSThomas Zimmermann static void mgag200_g200wb_hold_bmc(struct mga_device *mdev) 135414c4531SDave Airlie { 136414c4531SDave Airlie u8 tmp; 137414c4531SDave Airlie int iter_max; 138414c4531SDave Airlie 139414c4531SDave Airlie /* 1- The first step is to warn the BMC of an upcoming mode change. 140414c4531SDave Airlie * We are putting the misc<0> to output.*/ 141414c4531SDave Airlie 142414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL); 143414c4531SDave Airlie tmp = RREG8(DAC_DATA); 144414c4531SDave Airlie tmp |= 0x10; 145414c4531SDave Airlie WREG_DAC(MGA1064_GEN_IO_CTL, tmp); 146414c4531SDave Airlie 147414c4531SDave Airlie /* we are putting a 1 on the misc<0> line */ 148414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA); 149414c4531SDave Airlie tmp = RREG8(DAC_DATA); 150414c4531SDave Airlie tmp |= 0x10; 151414c4531SDave Airlie WREG_DAC(MGA1064_GEN_IO_DATA, tmp); 152414c4531SDave Airlie 153414c4531SDave Airlie /* 2- Second step to mask and further scan request 154414c4531SDave Airlie * This will be done by asserting the remfreqmsk bit (XSPAREREG<7>) 155414c4531SDave Airlie */ 156414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_SPAREREG); 157414c4531SDave Airlie tmp = RREG8(DAC_DATA); 158414c4531SDave Airlie tmp |= 0x80; 159414c4531SDave Airlie WREG_DAC(MGA1064_SPAREREG, tmp); 160414c4531SDave Airlie 161414c4531SDave Airlie /* 3a- the third step is to verifu if there is an active scan 162414c4531SDave Airlie * We are searching for a 0 on remhsyncsts <XSPAREREG<0>) 163414c4531SDave Airlie */ 164414c4531SDave Airlie iter_max = 300; 165414c4531SDave Airlie while (!(tmp & 0x1) && iter_max) { 166414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_SPAREREG); 167414c4531SDave Airlie tmp = RREG8(DAC_DATA); 168414c4531SDave Airlie udelay(1000); 169414c4531SDave Airlie iter_max--; 170414c4531SDave Airlie } 171414c4531SDave Airlie 172414c4531SDave Airlie /* 3b- this step occurs only if the remove is actually scanning 173414c4531SDave Airlie * we are waiting for the end of the frame which is a 1 on 174414c4531SDave Airlie * remvsyncsts (XSPAREREG<1>) 175414c4531SDave Airlie */ 176414c4531SDave Airlie if (iter_max) { 177414c4531SDave Airlie iter_max = 300; 178414c4531SDave Airlie while ((tmp & 0x2) && iter_max) { 179414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_SPAREREG); 180414c4531SDave Airlie tmp = RREG8(DAC_DATA); 181414c4531SDave Airlie udelay(1000); 182414c4531SDave Airlie iter_max--; 183414c4531SDave Airlie } 184414c4531SDave Airlie } 185414c4531SDave Airlie } 186414c4531SDave Airlie 187904347fbSThomas Zimmermann static void mgag200_g200wb_release_bmc(struct mga_device *mdev) 188414c4531SDave Airlie { 189414c4531SDave Airlie u8 tmp; 190414c4531SDave Airlie 191414c4531SDave Airlie /* 1- The first step is to ensure that the vrsten and hrsten are set */ 192414c4531SDave Airlie WREG8(MGAREG_CRTCEXT_INDEX, 1); 193414c4531SDave Airlie tmp = RREG8(MGAREG_CRTCEXT_DATA); 194414c4531SDave Airlie WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88); 195414c4531SDave Airlie 196414c4531SDave Airlie /* 2- second step is to assert the rstlvl2 */ 197414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_REMHEADCTL2); 198414c4531SDave Airlie tmp = RREG8(DAC_DATA); 199414c4531SDave Airlie tmp |= 0x8; 200414c4531SDave Airlie WREG8(DAC_DATA, tmp); 201414c4531SDave Airlie 202414c4531SDave Airlie /* wait 10 us */ 203414c4531SDave Airlie udelay(10); 204414c4531SDave Airlie 205414c4531SDave Airlie /* 3- deassert rstlvl2 */ 206414c4531SDave Airlie tmp &= ~0x08; 207414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_REMHEADCTL2); 208414c4531SDave Airlie WREG8(DAC_DATA, tmp); 209414c4531SDave Airlie 210414c4531SDave Airlie /* 4- remove mask of scan request */ 211414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_SPAREREG); 212414c4531SDave Airlie tmp = RREG8(DAC_DATA); 213414c4531SDave Airlie tmp &= ~0x80; 214414c4531SDave Airlie WREG8(DAC_DATA, tmp); 215414c4531SDave Airlie 216414c4531SDave Airlie /* 5- put back a 0 on the misc<0> line */ 217414c4531SDave Airlie WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA); 218414c4531SDave Airlie tmp = RREG8(DAC_DATA); 219414c4531SDave Airlie tmp &= ~0x10; 220414c4531SDave Airlie WREG_DAC(MGA1064_GEN_IO_DATA, tmp); 221414c4531SDave Airlie } 222414c4531SDave Airlie 2239f1d0366SChristopher Harvey /* 224d6237687SThomas Zimmermann * This is how the framebuffer base address is stored in g200 cards: 225d6237687SThomas Zimmermann * * Assume @offset is the gpu_addr variable of the framebuffer object 226d6237687SThomas Zimmermann * * Then addr is the number of _pixels_ (not bytes) from the start of 227d6237687SThomas Zimmermann * VRAM to the first pixel we want to display. (divided by 2 for 32bit 228d6237687SThomas Zimmermann * framebuffers) 229d6237687SThomas Zimmermann * * addr is stored in the CRTCEXT0, CRTCC and CRTCD registers 230d6237687SThomas Zimmermann * addr<20> -> CRTCEXT0<6> 231d6237687SThomas Zimmermann * addr<19-16> -> CRTCEXT0<3-0> 232d6237687SThomas Zimmermann * addr<15-8> -> CRTCC<7-0> 233d6237687SThomas Zimmermann * addr<7-0> -> CRTCD<7-0> 234d6237687SThomas Zimmermann * 235d6237687SThomas Zimmermann * CRTCEXT0 has to be programmed last to trigger an update and make the 236d6237687SThomas Zimmermann * new addr variable take effect. 2379f1d0366SChristopher Harvey */ 238d6237687SThomas Zimmermann static void mgag200_set_startadd(struct mga_device *mdev, 239d6237687SThomas Zimmermann unsigned long offset) 240414c4531SDave Airlie { 241832eddf5SThomas Zimmermann struct drm_device *dev = &mdev->base; 242d6237687SThomas Zimmermann u32 startadd; 243d6237687SThomas Zimmermann u8 crtcc, crtcd, crtcext0; 244414c4531SDave Airlie 245d6237687SThomas Zimmermann startadd = offset / 8; 246414c4531SDave Airlie 247d2addf89SJocelyn Falempe if (startadd > 0) 248d2addf89SJocelyn Falempe drm_WARN_ON_ONCE(dev, mdev->flags & MGAG200_FLAG_HW_BUG_NO_STARTADD); 249d2addf89SJocelyn Falempe 250d6237687SThomas Zimmermann /* 251d6237687SThomas Zimmermann * Can't store addresses any higher than that, but we also 252d6237687SThomas Zimmermann * don't have more than 16 MiB of memory, so it should be fine. 253d6237687SThomas Zimmermann */ 254d6237687SThomas Zimmermann drm_WARN_ON(dev, startadd > 0x1fffff); 255414c4531SDave Airlie 256d6237687SThomas Zimmermann RREG_ECRT(0x00, crtcext0); 257d6237687SThomas Zimmermann 258d6237687SThomas Zimmermann crtcc = (startadd >> 8) & 0xff; 259d6237687SThomas Zimmermann crtcd = startadd & 0xff; 260d6237687SThomas Zimmermann crtcext0 &= 0xb0; 261d6237687SThomas Zimmermann crtcext0 |= ((startadd >> 14) & BIT(6)) | 262d6237687SThomas Zimmermann ((startadd >> 16) & 0x0f); 263d6237687SThomas Zimmermann 264d6237687SThomas Zimmermann WREG_CRT(0x0c, crtcc); 265d6237687SThomas Zimmermann WREG_CRT(0x0d, crtcd); 266d6237687SThomas Zimmermann WREG_ECRT(0x00, crtcext0); 267414c4531SDave Airlie } 268414c4531SDave Airlie 2694f710d7cSThomas Zimmermann static void mgag200_set_dac_regs(struct mga_device *mdev) 2704f710d7cSThomas Zimmermann { 2714f710d7cSThomas Zimmermann size_t i; 2724f710d7cSThomas Zimmermann u8 dacvalue[] = { 2734f710d7cSThomas Zimmermann /* 0x00: */ 0, 0, 0, 0, 0, 0, 0x00, 0, 2744f710d7cSThomas Zimmermann /* 0x08: */ 0, 0, 0, 0, 0, 0, 0, 0, 2754f710d7cSThomas Zimmermann /* 0x10: */ 0, 0, 0, 0, 0, 0, 0, 0, 2764f710d7cSThomas Zimmermann /* 0x18: */ 0x00, 0, 0xC9, 0xFF, 0xBF, 0x20, 0x1F, 0x20, 2774f710d7cSThomas Zimmermann /* 0x20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2784f710d7cSThomas Zimmermann /* 0x28: */ 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x40, 2794f710d7cSThomas Zimmermann /* 0x30: */ 0x00, 0xB0, 0x00, 0xC2, 0x34, 0x14, 0x02, 0x83, 2804f710d7cSThomas Zimmermann /* 0x38: */ 0x00, 0x93, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3A, 2814f710d7cSThomas Zimmermann /* 0x40: */ 0, 0, 0, 0, 0, 0, 0, 0, 2824f710d7cSThomas Zimmermann /* 0x48: */ 0, 0, 0, 0, 0, 0, 0, 0 2834f710d7cSThomas Zimmermann }; 2844f710d7cSThomas Zimmermann 2854f710d7cSThomas Zimmermann switch (mdev->type) { 286e20dfd27SThomas Zimmermann case G200_PCI: 287e20dfd27SThomas Zimmermann case G200_AGP: 288e20dfd27SThomas Zimmermann dacvalue[MGA1064_SYS_PLL_M] = 0x04; 289e20dfd27SThomas Zimmermann dacvalue[MGA1064_SYS_PLL_N] = 0x2D; 290e20dfd27SThomas Zimmermann dacvalue[MGA1064_SYS_PLL_P] = 0x19; 291e20dfd27SThomas Zimmermann break; 2924f710d7cSThomas Zimmermann case G200_SE_A: 2934f710d7cSThomas Zimmermann case G200_SE_B: 2944f710d7cSThomas Zimmermann dacvalue[MGA1064_VREF_CTL] = 0x03; 2954f710d7cSThomas Zimmermann dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL; 2964f710d7cSThomas Zimmermann dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_DAC_EN | 2974f710d7cSThomas Zimmermann MGA1064_MISC_CTL_VGA8 | 2984f710d7cSThomas Zimmermann MGA1064_MISC_CTL_DAC_RAM_CS; 2994f710d7cSThomas Zimmermann break; 3004f710d7cSThomas Zimmermann case G200_WB: 3014f710d7cSThomas Zimmermann case G200_EW3: 3024f710d7cSThomas Zimmermann dacvalue[MGA1064_VREF_CTL] = 0x07; 3034f710d7cSThomas Zimmermann break; 3044f710d7cSThomas Zimmermann case G200_EV: 3054f710d7cSThomas Zimmermann dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL; 3064f710d7cSThomas Zimmermann dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | 3074f710d7cSThomas Zimmermann MGA1064_MISC_CTL_DAC_RAM_CS; 3084f710d7cSThomas Zimmermann break; 3094f710d7cSThomas Zimmermann case G200_EH: 3104f710d7cSThomas Zimmermann case G200_EH3: 3114f710d7cSThomas Zimmermann dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 | 3124f710d7cSThomas Zimmermann MGA1064_MISC_CTL_DAC_RAM_CS; 3134f710d7cSThomas Zimmermann break; 3144f710d7cSThomas Zimmermann case G200_ER: 3154f710d7cSThomas Zimmermann break; 3164f710d7cSThomas Zimmermann } 3174f710d7cSThomas Zimmermann 3184f710d7cSThomas Zimmermann for (i = 0; i < ARRAY_SIZE(dacvalue); i++) { 3194f710d7cSThomas Zimmermann if ((i <= 0x17) || 3204f710d7cSThomas Zimmermann (i == 0x1b) || 3214f710d7cSThomas Zimmermann (i == 0x1c) || 3224f710d7cSThomas Zimmermann ((i >= 0x1f) && (i <= 0x29)) || 3234f710d7cSThomas Zimmermann ((i >= 0x30) && (i <= 0x37))) 3244f710d7cSThomas Zimmermann continue; 3254f710d7cSThomas Zimmermann if (IS_G200_SE(mdev) && 3264f710d7cSThomas Zimmermann ((i == 0x2c) || (i == 0x2d) || (i == 0x2e))) 3274f710d7cSThomas Zimmermann continue; 3284f710d7cSThomas Zimmermann if ((mdev->type == G200_EV || 3294f710d7cSThomas Zimmermann mdev->type == G200_WB || 3304f710d7cSThomas Zimmermann mdev->type == G200_EH || 3314f710d7cSThomas Zimmermann mdev->type == G200_EW3 || 3324f710d7cSThomas Zimmermann mdev->type == G200_EH3) && 3334f710d7cSThomas Zimmermann (i >= 0x44) && (i <= 0x4e)) 3344f710d7cSThomas Zimmermann continue; 3354f710d7cSThomas Zimmermann 3364f710d7cSThomas Zimmermann WREG_DAC(i, dacvalue[i]); 3374f710d7cSThomas Zimmermann } 3384f710d7cSThomas Zimmermann 3394f710d7cSThomas Zimmermann if (mdev->type == G200_ER) 3404f710d7cSThomas Zimmermann WREG_DAC(0x90, 0); 3414f710d7cSThomas Zimmermann } 3424f710d7cSThomas Zimmermann 3434f710d7cSThomas Zimmermann static void mgag200_init_regs(struct mga_device *mdev) 3444f710d7cSThomas Zimmermann { 3459053cad2SThomas Zimmermann u8 crtc11, misc; 3464f710d7cSThomas Zimmermann 3474f710d7cSThomas Zimmermann mgag200_set_dac_regs(mdev); 3484f710d7cSThomas Zimmermann 3494f710d7cSThomas Zimmermann WREG_SEQ(2, 0x0f); 3504f710d7cSThomas Zimmermann WREG_SEQ(3, 0x00); 3514f710d7cSThomas Zimmermann WREG_SEQ(4, 0x0e); 3524f710d7cSThomas Zimmermann 3534f710d7cSThomas Zimmermann WREG_CRT(10, 0); 3544f710d7cSThomas Zimmermann WREG_CRT(11, 0); 3554f710d7cSThomas Zimmermann WREG_CRT(12, 0); 3564f710d7cSThomas Zimmermann WREG_CRT(13, 0); 3574f710d7cSThomas Zimmermann WREG_CRT(14, 0); 3584f710d7cSThomas Zimmermann WREG_CRT(15, 0); 3594f710d7cSThomas Zimmermann 360da568d5eSThomas Zimmermann RREG_CRT(0x11, crtc11); 361da568d5eSThomas Zimmermann crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT | 362da568d5eSThomas Zimmermann MGAREG_CRTC11_VINTEN | 363da568d5eSThomas Zimmermann MGAREG_CRTC11_VINTCLR); 364da568d5eSThomas Zimmermann WREG_CRT(0x11, crtc11); 365da568d5eSThomas Zimmermann 3664f710d7cSThomas Zimmermann if (mdev->type == G200_ER) 3674f710d7cSThomas Zimmermann WREG_ECRT(0x24, 0x5); 3684f710d7cSThomas Zimmermann 3694f710d7cSThomas Zimmermann if (mdev->type == G200_EW3) 3704f710d7cSThomas Zimmermann WREG_ECRT(0x34, 0x5); 3714f710d7cSThomas Zimmermann 3724f710d7cSThomas Zimmermann misc = RREG8(MGA_MISC_IN); 373b9fa77ecSThomas Zimmermann misc |= MGAREG_MISC_IOADSEL; 3744f710d7cSThomas Zimmermann WREG8(MGA_MISC_OUT, misc); 3754f710d7cSThomas Zimmermann } 3764f710d7cSThomas Zimmermann 377a6edae07SThomas Zimmermann static void mgag200_set_mode_regs(struct mga_device *mdev, 378a6edae07SThomas Zimmermann const struct drm_display_mode *mode) 379a6edae07SThomas Zimmermann { 380a6edae07SThomas Zimmermann unsigned int hdisplay, hsyncstart, hsyncend, htotal; 381a6edae07SThomas Zimmermann unsigned int vdisplay, vsyncstart, vsyncend, vtotal; 382db05f8d3SThomas Zimmermann u8 misc, crtcext1, crtcext2, crtcext5; 383a6edae07SThomas Zimmermann 384a6edae07SThomas Zimmermann hdisplay = mode->hdisplay / 8 - 1; 385a6edae07SThomas Zimmermann hsyncstart = mode->hsync_start / 8 - 1; 386a6edae07SThomas Zimmermann hsyncend = mode->hsync_end / 8 - 1; 387a6edae07SThomas Zimmermann htotal = mode->htotal / 8 - 1; 388a6edae07SThomas Zimmermann 389a6edae07SThomas Zimmermann /* Work around hardware quirk */ 390a6edae07SThomas Zimmermann if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04) 391a6edae07SThomas Zimmermann htotal++; 392a6edae07SThomas Zimmermann 393a6edae07SThomas Zimmermann vdisplay = mode->vdisplay - 1; 394a6edae07SThomas Zimmermann vsyncstart = mode->vsync_start - 1; 395a6edae07SThomas Zimmermann vsyncend = mode->vsync_end - 1; 396a6edae07SThomas Zimmermann vtotal = mode->vtotal - 2; 397a6edae07SThomas Zimmermann 398db05f8d3SThomas Zimmermann misc = RREG8(MGA_MISC_IN); 399db05f8d3SThomas Zimmermann 400a6edae07SThomas Zimmermann if (mode->flags & DRM_MODE_FLAG_NHSYNC) 401db05f8d3SThomas Zimmermann misc |= MGAREG_MISC_HSYNCPOL; 402db05f8d3SThomas Zimmermann else 403db05f8d3SThomas Zimmermann misc &= ~MGAREG_MISC_HSYNCPOL; 404db05f8d3SThomas Zimmermann 405a6edae07SThomas Zimmermann if (mode->flags & DRM_MODE_FLAG_NVSYNC) 406db05f8d3SThomas Zimmermann misc |= MGAREG_MISC_VSYNCPOL; 407db05f8d3SThomas Zimmermann else 408db05f8d3SThomas Zimmermann misc &= ~MGAREG_MISC_VSYNCPOL; 409a6edae07SThomas Zimmermann 410a6edae07SThomas Zimmermann crtcext1 = (((htotal - 4) & 0x100) >> 8) | 411a6edae07SThomas Zimmermann ((hdisplay & 0x100) >> 7) | 412a6edae07SThomas Zimmermann ((hsyncstart & 0x100) >> 6) | 413a6edae07SThomas Zimmermann (htotal & 0x40); 414a6edae07SThomas Zimmermann if (mdev->type == G200_WB || mdev->type == G200_EW3) 415a6edae07SThomas Zimmermann crtcext1 |= BIT(7) | /* vrsten */ 416a6edae07SThomas Zimmermann BIT(3); /* hrsten */ 417a6edae07SThomas Zimmermann 418a6edae07SThomas Zimmermann crtcext2 = ((vtotal & 0xc00) >> 10) | 419a6edae07SThomas Zimmermann ((vdisplay & 0x400) >> 8) | 420a6edae07SThomas Zimmermann ((vdisplay & 0xc00) >> 7) | 421a6edae07SThomas Zimmermann ((vsyncstart & 0xc00) >> 5) | 422a6edae07SThomas Zimmermann ((vdisplay & 0x400) >> 3); 423a6edae07SThomas Zimmermann crtcext5 = 0x00; 424a6edae07SThomas Zimmermann 425a6edae07SThomas Zimmermann WREG_CRT(0, htotal - 4); 426a6edae07SThomas Zimmermann WREG_CRT(1, hdisplay); 427a6edae07SThomas Zimmermann WREG_CRT(2, hdisplay); 428a6edae07SThomas Zimmermann WREG_CRT(3, (htotal & 0x1F) | 0x80); 429a6edae07SThomas Zimmermann WREG_CRT(4, hsyncstart); 430a6edae07SThomas Zimmermann WREG_CRT(5, ((htotal & 0x20) << 2) | (hsyncend & 0x1F)); 431a6edae07SThomas Zimmermann WREG_CRT(6, vtotal & 0xFF); 432a6edae07SThomas Zimmermann WREG_CRT(7, ((vtotal & 0x100) >> 8) | 433a6edae07SThomas Zimmermann ((vdisplay & 0x100) >> 7) | 434a6edae07SThomas Zimmermann ((vsyncstart & 0x100) >> 6) | 435a6edae07SThomas Zimmermann ((vdisplay & 0x100) >> 5) | 436a6edae07SThomas Zimmermann ((vdisplay & 0x100) >> 4) | /* linecomp */ 437a6edae07SThomas Zimmermann ((vtotal & 0x200) >> 4) | 438a6edae07SThomas Zimmermann ((vdisplay & 0x200) >> 3) | 439a6edae07SThomas Zimmermann ((vsyncstart & 0x200) >> 2)); 440a6edae07SThomas Zimmermann WREG_CRT(9, ((vdisplay & 0x200) >> 4) | 441a6edae07SThomas Zimmermann ((vdisplay & 0x200) >> 3)); 442a6edae07SThomas Zimmermann WREG_CRT(16, vsyncstart & 0xFF); 443a6edae07SThomas Zimmermann WREG_CRT(17, (vsyncend & 0x0F) | 0x20); 444a6edae07SThomas Zimmermann WREG_CRT(18, vdisplay & 0xFF); 445a6edae07SThomas Zimmermann WREG_CRT(20, 0); 446a6edae07SThomas Zimmermann WREG_CRT(21, vdisplay & 0xFF); 447a6edae07SThomas Zimmermann WREG_CRT(22, (vtotal + 1) & 0xFF); 448a6edae07SThomas Zimmermann WREG_CRT(23, 0xc3); 449a6edae07SThomas Zimmermann WREG_CRT(24, vdisplay & 0xFF); 450a6edae07SThomas Zimmermann 451a6edae07SThomas Zimmermann WREG_ECRT(0x01, crtcext1); 452a6edae07SThomas Zimmermann WREG_ECRT(0x02, crtcext2); 453a6edae07SThomas Zimmermann WREG_ECRT(0x05, crtcext5); 454db05f8d3SThomas Zimmermann 455db05f8d3SThomas Zimmermann WREG8(MGA_MISC_OUT, misc); 456a6edae07SThomas Zimmermann } 457a6edae07SThomas Zimmermann 458d9cc564bSThomas Zimmermann static u8 mgag200_get_bpp_shift(const struct drm_format_info *format) 45972a03a35SThomas Zimmermann { 460d9cc564bSThomas Zimmermann static const u8 bpp_shift[] = {0, 1, 0, 2}; 461d9cc564bSThomas Zimmermann 462d9cc564bSThomas Zimmermann return bpp_shift[format->cpp[0] - 1]; 46372a03a35SThomas Zimmermann } 46472a03a35SThomas Zimmermann 46572a03a35SThomas Zimmermann /* 46672a03a35SThomas Zimmermann * Calculates the HW offset value from the framebuffer's pitch. The 46772a03a35SThomas Zimmermann * offset is a multiple of the pixel size and depends on the display 46872a03a35SThomas Zimmermann * format. 46972a03a35SThomas Zimmermann */ 47072a03a35SThomas Zimmermann static u32 mgag200_calculate_offset(struct mga_device *mdev, 47172a03a35SThomas Zimmermann const struct drm_framebuffer *fb) 47272a03a35SThomas Zimmermann { 47372a03a35SThomas Zimmermann u32 offset = fb->pitches[0] / fb->format->cpp[0]; 474d9cc564bSThomas Zimmermann u8 bppshift = mgag200_get_bpp_shift(fb->format); 47572a03a35SThomas Zimmermann 47672a03a35SThomas Zimmermann if (fb->format->cpp[0] * 8 == 24) 47772a03a35SThomas Zimmermann offset = (offset * 3) >> (4 - bppshift); 47872a03a35SThomas Zimmermann else 47972a03a35SThomas Zimmermann offset = offset >> (4 - bppshift); 48072a03a35SThomas Zimmermann 48172a03a35SThomas Zimmermann return offset; 48272a03a35SThomas Zimmermann } 48372a03a35SThomas Zimmermann 48472a03a35SThomas Zimmermann static void mgag200_set_offset(struct mga_device *mdev, 48572a03a35SThomas Zimmermann const struct drm_framebuffer *fb) 48672a03a35SThomas Zimmermann { 48772a03a35SThomas Zimmermann u8 crtc13, crtcext0; 48872a03a35SThomas Zimmermann u32 offset = mgag200_calculate_offset(mdev, fb); 48972a03a35SThomas Zimmermann 49072a03a35SThomas Zimmermann RREG_ECRT(0, crtcext0); 49172a03a35SThomas Zimmermann 49272a03a35SThomas Zimmermann crtc13 = offset & 0xff; 49372a03a35SThomas Zimmermann 49472a03a35SThomas Zimmermann crtcext0 &= ~MGAREG_CRTCEXT0_OFFSET_MASK; 49572a03a35SThomas Zimmermann crtcext0 |= (offset >> 4) & MGAREG_CRTCEXT0_OFFSET_MASK; 49672a03a35SThomas Zimmermann 49772a03a35SThomas Zimmermann WREG_CRT(0x13, crtc13); 49872a03a35SThomas Zimmermann WREG_ECRT(0x00, crtcext0); 49972a03a35SThomas Zimmermann } 50072a03a35SThomas Zimmermann 501836d5368SThomas Zimmermann static void mgag200_set_format_regs(struct mga_device *mdev, 502836d5368SThomas Zimmermann const struct drm_framebuffer *fb) 503836d5368SThomas Zimmermann { 504832eddf5SThomas Zimmermann struct drm_device *dev = &mdev->base; 505836d5368SThomas Zimmermann const struct drm_format_info *format = fb->format; 506836d5368SThomas Zimmermann unsigned int bpp, bppshift, scale; 507836d5368SThomas Zimmermann u8 crtcext3, xmulctrl; 508836d5368SThomas Zimmermann 509836d5368SThomas Zimmermann bpp = format->cpp[0] * 8; 510836d5368SThomas Zimmermann 511d9cc564bSThomas Zimmermann bppshift = mgag200_get_bpp_shift(format); 512836d5368SThomas Zimmermann switch (bpp) { 513836d5368SThomas Zimmermann case 24: 514836d5368SThomas Zimmermann scale = ((1 << bppshift) * 3) - 1; 515836d5368SThomas Zimmermann break; 516836d5368SThomas Zimmermann default: 517836d5368SThomas Zimmermann scale = (1 << bppshift) - 1; 518836d5368SThomas Zimmermann break; 519836d5368SThomas Zimmermann } 520836d5368SThomas Zimmermann 521836d5368SThomas Zimmermann RREG_ECRT(3, crtcext3); 522836d5368SThomas Zimmermann 523836d5368SThomas Zimmermann switch (bpp) { 524836d5368SThomas Zimmermann case 8: 525836d5368SThomas Zimmermann xmulctrl = MGA1064_MUL_CTL_8bits; 526836d5368SThomas Zimmermann break; 527836d5368SThomas Zimmermann case 16: 528836d5368SThomas Zimmermann if (format->depth == 15) 529836d5368SThomas Zimmermann xmulctrl = MGA1064_MUL_CTL_15bits; 530836d5368SThomas Zimmermann else 531836d5368SThomas Zimmermann xmulctrl = MGA1064_MUL_CTL_16bits; 532836d5368SThomas Zimmermann break; 533836d5368SThomas Zimmermann case 24: 534836d5368SThomas Zimmermann xmulctrl = MGA1064_MUL_CTL_24bits; 535836d5368SThomas Zimmermann break; 536836d5368SThomas Zimmermann case 32: 537836d5368SThomas Zimmermann xmulctrl = MGA1064_MUL_CTL_32_24bits; 538836d5368SThomas Zimmermann break; 539836d5368SThomas Zimmermann default: 540836d5368SThomas Zimmermann /* BUG: We should have caught this problem already. */ 541836d5368SThomas Zimmermann drm_WARN_ON(dev, "invalid format depth\n"); 542836d5368SThomas Zimmermann return; 543836d5368SThomas Zimmermann } 544836d5368SThomas Zimmermann 545836d5368SThomas Zimmermann crtcext3 &= ~GENMASK(2, 0); 546836d5368SThomas Zimmermann crtcext3 |= scale; 547836d5368SThomas Zimmermann 548836d5368SThomas Zimmermann WREG_DAC(MGA1064_MUL_CTL, xmulctrl); 549836d5368SThomas Zimmermann 550836d5368SThomas Zimmermann WREG_GFX(0, 0x00); 551836d5368SThomas Zimmermann WREG_GFX(1, 0x00); 552836d5368SThomas Zimmermann WREG_GFX(2, 0x00); 553836d5368SThomas Zimmermann WREG_GFX(3, 0x00); 554836d5368SThomas Zimmermann WREG_GFX(4, 0x00); 555836d5368SThomas Zimmermann WREG_GFX(5, 0x40); 556028a73e1SJocelyn Falempe /* GCTL6 should be 0x05, but we configure memmapsl to 0xb8000 (text mode), 557028a73e1SJocelyn Falempe * so that it doesn't hang when running kexec/kdump on G200_SE rev42. 558028a73e1SJocelyn Falempe */ 559028a73e1SJocelyn Falempe WREG_GFX(6, 0x0d); 560836d5368SThomas Zimmermann WREG_GFX(7, 0x0f); 561836d5368SThomas Zimmermann WREG_GFX(8, 0x0f); 562836d5368SThomas Zimmermann 563836d5368SThomas Zimmermann WREG_ECRT(3, crtcext3); 564836d5368SThomas Zimmermann } 565836d5368SThomas Zimmermann 5662e5ccbbaSThomas Zimmermann static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev) 5672e5ccbbaSThomas Zimmermann { 5682e5ccbbaSThomas Zimmermann static uint32_t RESET_FLAG = 0x00200000; /* undocumented magic value */ 5692e5ccbbaSThomas Zimmermann u32 memctl; 5702e5ccbbaSThomas Zimmermann 5712e5ccbbaSThomas Zimmermann memctl = RREG32(MGAREG_MEMCTL); 5722e5ccbbaSThomas Zimmermann 5732e5ccbbaSThomas Zimmermann memctl |= RESET_FLAG; 5742e5ccbbaSThomas Zimmermann WREG32(MGAREG_MEMCTL, memctl); 5752e5ccbbaSThomas Zimmermann 5762e5ccbbaSThomas Zimmermann udelay(1000); 5772e5ccbbaSThomas Zimmermann 5782e5ccbbaSThomas Zimmermann memctl &= ~RESET_FLAG; 5792e5ccbbaSThomas Zimmermann WREG32(MGAREG_MEMCTL, memctl); 5802e5ccbbaSThomas Zimmermann } 5812e5ccbbaSThomas Zimmermann 5827fc1ae56SThomas Zimmermann static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, 5837fc1ae56SThomas Zimmermann const struct drm_display_mode *mode, 5847fc1ae56SThomas Zimmermann const struct drm_framebuffer *fb) 5857fc1ae56SThomas Zimmermann { 586fb18825fSThomas Zimmermann u32 unique_rev_id = mdev->model.g200se.unique_rev_id; 5877fc1ae56SThomas Zimmermann unsigned int hiprilvl; 5887fc1ae56SThomas Zimmermann u8 crtcext6; 5897fc1ae56SThomas Zimmermann 590fb18825fSThomas Zimmermann if (unique_rev_id >= 0x04) { 5917fc1ae56SThomas Zimmermann hiprilvl = 0; 592fb18825fSThomas Zimmermann } else if (unique_rev_id >= 0x02) { 5937fc1ae56SThomas Zimmermann unsigned int bpp; 5947fc1ae56SThomas Zimmermann unsigned long mb; 5957fc1ae56SThomas Zimmermann 5967fc1ae56SThomas Zimmermann if (fb->format->cpp[0] * 8 > 16) 5977fc1ae56SThomas Zimmermann bpp = 32; 5987fc1ae56SThomas Zimmermann else if (fb->format->cpp[0] * 8 > 8) 5997fc1ae56SThomas Zimmermann bpp = 16; 6007fc1ae56SThomas Zimmermann else 6017fc1ae56SThomas Zimmermann bpp = 8; 6027fc1ae56SThomas Zimmermann 6037fc1ae56SThomas Zimmermann mb = (mode->clock * bpp) / 1000; 6047fc1ae56SThomas Zimmermann if (mb > 3100) 6057fc1ae56SThomas Zimmermann hiprilvl = 0; 6067fc1ae56SThomas Zimmermann else if (mb > 2600) 6077fc1ae56SThomas Zimmermann hiprilvl = 1; 6087fc1ae56SThomas Zimmermann else if (mb > 1900) 6097fc1ae56SThomas Zimmermann hiprilvl = 2; 6107fc1ae56SThomas Zimmermann else if (mb > 1160) 6117fc1ae56SThomas Zimmermann hiprilvl = 3; 6127fc1ae56SThomas Zimmermann else if (mb > 440) 6137fc1ae56SThomas Zimmermann hiprilvl = 4; 6147fc1ae56SThomas Zimmermann else 6157fc1ae56SThomas Zimmermann hiprilvl = 5; 6167fc1ae56SThomas Zimmermann 617fb18825fSThomas Zimmermann } else if (unique_rev_id >= 0x01) { 6187fc1ae56SThomas Zimmermann hiprilvl = 3; 6197fc1ae56SThomas Zimmermann } else { 6207fc1ae56SThomas Zimmermann hiprilvl = 4; 6217fc1ae56SThomas Zimmermann } 6227fc1ae56SThomas Zimmermann 6237fc1ae56SThomas Zimmermann crtcext6 = hiprilvl; /* implicitly sets maxhipri to 0 */ 6247fc1ae56SThomas Zimmermann 6257fc1ae56SThomas Zimmermann WREG_ECRT(0x06, crtcext6); 6267fc1ae56SThomas Zimmermann } 6277fc1ae56SThomas Zimmermann 6287fc1ae56SThomas Zimmermann static void mgag200_g200ev_set_hiprilvl(struct mga_device *mdev) 6297fc1ae56SThomas Zimmermann { 6307fc1ae56SThomas Zimmermann WREG_ECRT(0x06, 0x00); 6317fc1ae56SThomas Zimmermann } 6327fc1ae56SThomas Zimmermann 633153fef41SThomas Zimmermann static void mgag200_enable_display(struct mga_device *mdev) 634414c4531SDave Airlie { 63570c3881eSThomas Zimmermann u8 seq0, seq1, crtcext1; 63670c3881eSThomas Zimmermann 63770c3881eSThomas Zimmermann RREG_SEQ(0x00, seq0); 63870c3881eSThomas Zimmermann seq0 |= MGAREG_SEQ0_SYNCRST | 63970c3881eSThomas Zimmermann MGAREG_SEQ0_ASYNCRST; 64070c3881eSThomas Zimmermann WREG_SEQ(0x00, seq0); 641414c4531SDave Airlie 642153fef41SThomas Zimmermann /* 643153fef41SThomas Zimmermann * TODO: replace busy waiting with vblank IRQ; put 644153fef41SThomas Zimmermann * msleep(50) before changing SCROFF 645153fef41SThomas Zimmermann */ 646414c4531SDave Airlie mga_wait_vsync(mdev); 647414c4531SDave Airlie mga_wait_busy(mdev); 648153fef41SThomas Zimmermann 649153fef41SThomas Zimmermann RREG_SEQ(0x01, seq1); 650153fef41SThomas Zimmermann seq1 &= ~MGAREG_SEQ1_SCROFF; 651153fef41SThomas Zimmermann WREG_SEQ(0x01, seq1); 652153fef41SThomas Zimmermann 653414c4531SDave Airlie msleep(20); 654153fef41SThomas Zimmermann 655153fef41SThomas Zimmermann RREG_ECRT(0x01, crtcext1); 656153fef41SThomas Zimmermann crtcext1 &= ~MGAREG_CRTCEXT1_VSYNCOFF; 657153fef41SThomas Zimmermann crtcext1 &= ~MGAREG_CRTCEXT1_HSYNCOFF; 658153fef41SThomas Zimmermann WREG_ECRT(0x01, crtcext1); 659153fef41SThomas Zimmermann } 660153fef41SThomas Zimmermann 661153fef41SThomas Zimmermann static void mgag200_disable_display(struct mga_device *mdev) 662153fef41SThomas Zimmermann { 66370c3881eSThomas Zimmermann u8 seq0, seq1, crtcext1; 66470c3881eSThomas Zimmermann 66570c3881eSThomas Zimmermann RREG_SEQ(0x00, seq0); 66670c3881eSThomas Zimmermann seq0 &= ~MGAREG_SEQ0_SYNCRST; 66770c3881eSThomas Zimmermann WREG_SEQ(0x00, seq0); 668153fef41SThomas Zimmermann 669153fef41SThomas Zimmermann /* 670153fef41SThomas Zimmermann * TODO: replace busy waiting with vblank IRQ; put 671153fef41SThomas Zimmermann * msleep(50) before changing SCROFF 672153fef41SThomas Zimmermann */ 673153fef41SThomas Zimmermann mga_wait_vsync(mdev); 674153fef41SThomas Zimmermann mga_wait_busy(mdev); 675153fef41SThomas Zimmermann 676153fef41SThomas Zimmermann RREG_SEQ(0x01, seq1); 677153fef41SThomas Zimmermann seq1 |= MGAREG_SEQ1_SCROFF; 678153fef41SThomas Zimmermann WREG_SEQ(0x01, seq1); 679153fef41SThomas Zimmermann 680153fef41SThomas Zimmermann msleep(20); 681153fef41SThomas Zimmermann 682153fef41SThomas Zimmermann RREG_ECRT(0x01, crtcext1); 683153fef41SThomas Zimmermann crtcext1 |= MGAREG_CRTCEXT1_VSYNCOFF | 684153fef41SThomas Zimmermann MGAREG_CRTCEXT1_HSYNCOFF; 685153fef41SThomas Zimmermann WREG_ECRT(0x01, crtcext1); 686414c4531SDave Airlie } 687414c4531SDave Airlie 688414c4531SDave Airlie /* 68981a15b9aSThomas Zimmermann * Connector 69081a15b9aSThomas Zimmermann */ 69181a15b9aSThomas Zimmermann 69216f14564SThomas Zimmermann static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector) 693414c4531SDave Airlie { 6945913ab94SThomas Zimmermann struct mga_device *mdev = to_mga_device(connector->dev); 69516f14564SThomas Zimmermann int ret; 696414c4531SDave Airlie 6975913ab94SThomas Zimmermann /* 6985913ab94SThomas Zimmermann * Protect access to I/O registers from concurrent modesetting 6995913ab94SThomas Zimmermann * by acquiring the I/O-register lock. 7005913ab94SThomas Zimmermann */ 7015913ab94SThomas Zimmermann mutex_lock(&mdev->rmmio_lock); 70216f14564SThomas Zimmermann ret = drm_connector_helper_get_modes_from_ddc(connector); 7035913ab94SThomas Zimmermann mutex_unlock(&mdev->rmmio_lock); 7045913ab94SThomas Zimmermann 705414c4531SDave Airlie return ret; 706414c4531SDave Airlie } 707414c4531SDave Airlie 70871cb7495SVille Syrjälä static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = { 70916f14564SThomas Zimmermann .get_modes = mgag200_vga_connector_helper_get_modes, 710414c4531SDave Airlie }; 711414c4531SDave Airlie 71271cb7495SVille Syrjälä static const struct drm_connector_funcs mga_vga_connector_funcs = { 71388fabb75SThomas Zimmermann .reset = drm_atomic_helper_connector_reset, 714414c4531SDave Airlie .fill_modes = drm_helper_probe_single_connector_modes, 715b279df24SThomas Zimmermann .destroy = drm_connector_cleanup, 71688fabb75SThomas Zimmermann .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 71788fabb75SThomas Zimmermann .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 718414c4531SDave Airlie }; 719414c4531SDave Airlie 72088fabb75SThomas Zimmermann /* 72188fabb75SThomas Zimmermann * Simple Display Pipe 72288fabb75SThomas Zimmermann */ 72388fabb75SThomas Zimmermann 72488fabb75SThomas Zimmermann static enum drm_mode_status 72588fabb75SThomas Zimmermann mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, 72688fabb75SThomas Zimmermann const struct drm_display_mode *mode) 72788fabb75SThomas Zimmermann { 728475e2b97SThomas Zimmermann struct mga_device *mdev = to_mga_device(pipe->crtc.dev); 729475e2b97SThomas Zimmermann 730475e2b97SThomas Zimmermann if (IS_G200_SE(mdev)) { 731475e2b97SThomas Zimmermann u32 unique_rev_id = mdev->model.g200se.unique_rev_id; 732475e2b97SThomas Zimmermann 733475e2b97SThomas Zimmermann if (unique_rev_id == 0x01) { 734475e2b97SThomas Zimmermann if (mode->hdisplay > 1600) 735475e2b97SThomas Zimmermann return MODE_VIRTUAL_X; 736475e2b97SThomas Zimmermann if (mode->vdisplay > 1200) 737475e2b97SThomas Zimmermann return MODE_VIRTUAL_Y; 738475e2b97SThomas Zimmermann } else if (unique_rev_id == 0x02) { 739475e2b97SThomas Zimmermann if (mode->hdisplay > 1920) 740475e2b97SThomas Zimmermann return MODE_VIRTUAL_X; 741475e2b97SThomas Zimmermann if (mode->vdisplay > 1200) 742475e2b97SThomas Zimmermann return MODE_VIRTUAL_Y; 743475e2b97SThomas Zimmermann } 744475e2b97SThomas Zimmermann } else if (mdev->type == G200_WB) { 745475e2b97SThomas Zimmermann if (mode->hdisplay > 1280) 746475e2b97SThomas Zimmermann return MODE_VIRTUAL_X; 747475e2b97SThomas Zimmermann if (mode->vdisplay > 1024) 748475e2b97SThomas Zimmermann return MODE_VIRTUAL_Y; 749475e2b97SThomas Zimmermann } 750475e2b97SThomas Zimmermann 751475e2b97SThomas Zimmermann if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 || 752475e2b97SThomas Zimmermann (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) { 753475e2b97SThomas Zimmermann return MODE_H_ILLEGAL; 754475e2b97SThomas Zimmermann } 755475e2b97SThomas Zimmermann 756475e2b97SThomas Zimmermann if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 || 757475e2b97SThomas Zimmermann mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 || 758475e2b97SThomas Zimmermann mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 || 759475e2b97SThomas Zimmermann mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) { 760475e2b97SThomas Zimmermann return MODE_BAD; 761475e2b97SThomas Zimmermann } 762475e2b97SThomas Zimmermann 76388fabb75SThomas Zimmermann return MODE_OK; 76488fabb75SThomas Zimmermann } 76588fabb75SThomas Zimmermann 76688fabb75SThomas Zimmermann static void 767913ec479SThomas Zimmermann mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb, 7687938f421SLucas De Marchi struct drm_rect *clip, const struct iosys_map *map) 769913ec479SThomas Zimmermann { 77027bd66ddSThomas Zimmermann void __iomem *dst = mdev->vram; 7714862ffaeSThomas Zimmermann void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ 772913ec479SThomas Zimmermann 77327bd66ddSThomas Zimmermann dst += drm_fb_clip_offset(fb->pitches[0], fb->format, clip); 77427bd66ddSThomas Zimmermann drm_fb_memcpy_toio(dst, fb->pitches[0], vmap, fb, clip); 775913ec479SThomas Zimmermann } 776913ec479SThomas Zimmermann 777913ec479SThomas Zimmermann static void 77888fabb75SThomas Zimmermann mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, 77988fabb75SThomas Zimmermann struct drm_crtc_state *crtc_state, 78088fabb75SThomas Zimmermann struct drm_plane_state *plane_state) 78188fabb75SThomas Zimmermann { 78288fabb75SThomas Zimmermann struct drm_crtc *crtc = &pipe->crtc; 78388fabb75SThomas Zimmermann struct drm_device *dev = crtc->dev; 78488fabb75SThomas Zimmermann struct mga_device *mdev = to_mga_device(dev); 7850a6dab7dSThomas Zimmermann struct mgag200_pll *pixpll = &mdev->pixpll; 78688fabb75SThomas Zimmermann struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; 7870a6dab7dSThomas Zimmermann struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); 78888fabb75SThomas Zimmermann struct drm_framebuffer *fb = plane_state->fb; 7894862ffaeSThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 790913ec479SThomas Zimmermann struct drm_rect fullscreen = { 791913ec479SThomas Zimmermann .x1 = 0, 792913ec479SThomas Zimmermann .x2 = fb->width, 793913ec479SThomas Zimmermann .y1 = 0, 794913ec479SThomas Zimmermann .y2 = fb->height, 795913ec479SThomas Zimmermann }; 79688fabb75SThomas Zimmermann 797931e3f3aSThomas Zimmermann /* 798931e3f3aSThomas Zimmermann * Concurrent operations could possibly trigger a call to 799931e3f3aSThomas Zimmermann * drm_connector_helper_funcs.get_modes by trying to read the 800931e3f3aSThomas Zimmermann * display modes. Protect access to I/O registers by acquiring 801931e3f3aSThomas Zimmermann * the I/O-register lock. 802931e3f3aSThomas Zimmermann */ 803931e3f3aSThomas Zimmermann mutex_lock(&mdev->rmmio_lock); 804931e3f3aSThomas Zimmermann 805895a4790SThomas Zimmermann if (mdev->type == G200_WB || mdev->type == G200_EW3) 806895a4790SThomas Zimmermann mgag200_g200wb_hold_bmc(mdev); 80788fabb75SThomas Zimmermann 80888fabb75SThomas Zimmermann mgag200_set_format_regs(mdev, fb); 80988fabb75SThomas Zimmermann mgag200_set_mode_regs(mdev, adjusted_mode); 8100a6dab7dSThomas Zimmermann 8110a6dab7dSThomas Zimmermann pixpll->funcs->update(pixpll, &mgag200_crtc_state->pixpllc); 81288fabb75SThomas Zimmermann 81388fabb75SThomas Zimmermann if (mdev->type == G200_ER) 81488fabb75SThomas Zimmermann mgag200_g200er_reset_tagfifo(mdev); 81588fabb75SThomas Zimmermann 81688fabb75SThomas Zimmermann if (IS_G200_SE(mdev)) 81788fabb75SThomas Zimmermann mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, fb); 81888fabb75SThomas Zimmermann else if (mdev->type == G200_EV) 81988fabb75SThomas Zimmermann mgag200_g200ev_set_hiprilvl(mdev); 82088fabb75SThomas Zimmermann 821895a4790SThomas Zimmermann if (mdev->type == G200_WB || mdev->type == G200_EW3) 822895a4790SThomas Zimmermann mgag200_g200wb_release_bmc(mdev); 823895a4790SThomas Zimmermann 824c577b2f4SJocelyn Falempe if (crtc_state->gamma_lut) 825c577b2f4SJocelyn Falempe mgag200_crtc_set_gamma(mdev, fb->format, crtc_state->gamma_lut->data); 826c577b2f4SJocelyn Falempe else 827c577b2f4SJocelyn Falempe mgag200_crtc_set_gamma_linear(mdev, fb->format); 828c577b2f4SJocelyn Falempe 829895a4790SThomas Zimmermann mgag200_enable_display(mdev); 830913ec479SThomas Zimmermann 831af022dafSThomas Zimmermann mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]); 832931e3f3aSThomas Zimmermann 833c48a3630SJocelyn Falempe /* Always scanout image at VRAM offset 0 */ 834c48a3630SJocelyn Falempe mgag200_set_startadd(mdev, (u32)0); 835c48a3630SJocelyn Falempe mgag200_set_offset(mdev, fb); 836c48a3630SJocelyn Falempe 837931e3f3aSThomas Zimmermann mutex_unlock(&mdev->rmmio_lock); 83888fabb75SThomas Zimmermann } 83988fabb75SThomas Zimmermann 84088fabb75SThomas Zimmermann static void 84188fabb75SThomas Zimmermann mgag200_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) 84288fabb75SThomas Zimmermann { 84388fabb75SThomas Zimmermann struct drm_crtc *crtc = &pipe->crtc; 844153fef41SThomas Zimmermann struct mga_device *mdev = to_mga_device(crtc->dev); 84588fabb75SThomas Zimmermann 846153fef41SThomas Zimmermann mgag200_disable_display(mdev); 84788fabb75SThomas Zimmermann } 84888fabb75SThomas Zimmermann 84988fabb75SThomas Zimmermann static int 85088fabb75SThomas Zimmermann mgag200_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, 85188fabb75SThomas Zimmermann struct drm_plane_state *plane_state, 85288fabb75SThomas Zimmermann struct drm_crtc_state *crtc_state) 85388fabb75SThomas Zimmermann { 85488fabb75SThomas Zimmermann struct drm_plane *plane = plane_state->plane; 8550a6dab7dSThomas Zimmermann struct drm_device *dev = plane->dev; 8560a6dab7dSThomas Zimmermann struct mga_device *mdev = to_mga_device(dev); 8570a6dab7dSThomas Zimmermann struct mgag200_pll *pixpll = &mdev->pixpll; 8580a6dab7dSThomas Zimmermann struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); 85988fabb75SThomas Zimmermann struct drm_framebuffer *new_fb = plane_state->fb; 86088fabb75SThomas Zimmermann struct drm_framebuffer *fb = NULL; 8610a6dab7dSThomas Zimmermann int ret; 86288fabb75SThomas Zimmermann 86388fabb75SThomas Zimmermann if (!new_fb) 86488fabb75SThomas Zimmermann return 0; 86588fabb75SThomas Zimmermann 86688fabb75SThomas Zimmermann if (plane->state) 86788fabb75SThomas Zimmermann fb = plane->state->fb; 86888fabb75SThomas Zimmermann 86988fabb75SThomas Zimmermann if (!fb || (fb->format != new_fb->format)) 87088fabb75SThomas Zimmermann crtc_state->mode_changed = true; /* update PLL settings */ 87188fabb75SThomas Zimmermann 8720a6dab7dSThomas Zimmermann if (crtc_state->mode_changed) { 8730a6dab7dSThomas Zimmermann ret = pixpll->funcs->compute(pixpll, crtc_state->mode.clock, 8740a6dab7dSThomas Zimmermann &mgag200_crtc_state->pixpllc); 8750a6dab7dSThomas Zimmermann if (ret) 8760a6dab7dSThomas Zimmermann return ret; 8770a6dab7dSThomas Zimmermann } 8780a6dab7dSThomas Zimmermann 879c577b2f4SJocelyn Falempe if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) { 880c577b2f4SJocelyn Falempe if (crtc_state->gamma_lut->length != 881c577b2f4SJocelyn Falempe MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) { 882c577b2f4SJocelyn Falempe drm_err(dev, "Wrong size for gamma_lut %zu\n", 883c577b2f4SJocelyn Falempe crtc_state->gamma_lut->length); 884c577b2f4SJocelyn Falempe return -EINVAL; 885c577b2f4SJocelyn Falempe } 886c577b2f4SJocelyn Falempe } 88788fabb75SThomas Zimmermann return 0; 88888fabb75SThomas Zimmermann } 88988fabb75SThomas Zimmermann 89088fabb75SThomas Zimmermann static void 89188fabb75SThomas Zimmermann mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, 89288fabb75SThomas Zimmermann struct drm_plane_state *old_state) 89388fabb75SThomas Zimmermann { 89488fabb75SThomas Zimmermann struct drm_plane *plane = &pipe->plane; 895c577b2f4SJocelyn Falempe struct drm_crtc *crtc = &pipe->crtc; 89688fabb75SThomas Zimmermann struct drm_device *dev = plane->dev; 89788fabb75SThomas Zimmermann struct mga_device *mdev = to_mga_device(dev); 89888fabb75SThomas Zimmermann struct drm_plane_state *state = plane->state; 8994862ffaeSThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); 90088fabb75SThomas Zimmermann struct drm_framebuffer *fb = state->fb; 901913ec479SThomas Zimmermann struct drm_rect damage; 902c48a3630SJocelyn Falempe struct drm_atomic_helper_damage_iter iter; 90388fabb75SThomas Zimmermann 90488fabb75SThomas Zimmermann if (!fb) 90588fabb75SThomas Zimmermann return; 90688fabb75SThomas Zimmermann 907931e3f3aSThomas Zimmermann mutex_lock(&mdev->rmmio_lock); 908931e3f3aSThomas Zimmermann 909c577b2f4SJocelyn Falempe if (crtc->state->color_mgmt_changed && crtc->state->gamma_lut) 910c577b2f4SJocelyn Falempe mgag200_crtc_set_gamma(mdev, fb->format, crtc->state->gamma_lut->data); 911c577b2f4SJocelyn Falempe 912c48a3630SJocelyn Falempe drm_atomic_helper_damage_iter_init(&iter, old_state, state); 913c48a3630SJocelyn Falempe drm_atomic_for_each_plane_damage(&iter, &damage) { 914af022dafSThomas Zimmermann mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]); 915c48a3630SJocelyn Falempe } 916c48a3630SJocelyn Falempe /* Always scanout image at VRAM offset 0 */ 917c48a3630SJocelyn Falempe mgag200_set_startadd(mdev, (u32)0); 918c48a3630SJocelyn Falempe mgag200_set_offset(mdev, fb); 919931e3f3aSThomas Zimmermann 920931e3f3aSThomas Zimmermann mutex_unlock(&mdev->rmmio_lock); 92188fabb75SThomas Zimmermann } 92288fabb75SThomas Zimmermann 92351b56939SThomas Zimmermann static struct drm_crtc_state * 92451b56939SThomas Zimmermann mgag200_simple_display_pipe_duplicate_crtc_state(struct drm_simple_display_pipe *pipe) 92551b56939SThomas Zimmermann { 92651b56939SThomas Zimmermann struct drm_crtc *crtc = &pipe->crtc; 92751b56939SThomas Zimmermann struct drm_crtc_state *crtc_state = crtc->state; 9280a6dab7dSThomas Zimmermann struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); 92951b56939SThomas Zimmermann struct mgag200_crtc_state *new_mgag200_crtc_state; 93051b56939SThomas Zimmermann 93151b56939SThomas Zimmermann if (!crtc_state) 93251b56939SThomas Zimmermann return NULL; 93351b56939SThomas Zimmermann 93451b56939SThomas Zimmermann new_mgag200_crtc_state = kzalloc(sizeof(*new_mgag200_crtc_state), GFP_KERNEL); 93551b56939SThomas Zimmermann if (!new_mgag200_crtc_state) 93651b56939SThomas Zimmermann return NULL; 93751b56939SThomas Zimmermann __drm_atomic_helper_crtc_duplicate_state(crtc, &new_mgag200_crtc_state->base); 93851b56939SThomas Zimmermann 9390a6dab7dSThomas Zimmermann memcpy(&new_mgag200_crtc_state->pixpllc, &mgag200_crtc_state->pixpllc, 9400a6dab7dSThomas Zimmermann sizeof(new_mgag200_crtc_state->pixpllc)); 9410a6dab7dSThomas Zimmermann 94251b56939SThomas Zimmermann return &new_mgag200_crtc_state->base; 94351b56939SThomas Zimmermann } 94451b56939SThomas Zimmermann 94551b56939SThomas Zimmermann static void mgag200_simple_display_pipe_destroy_crtc_state(struct drm_simple_display_pipe *pipe, 94651b56939SThomas Zimmermann struct drm_crtc_state *crtc_state) 94751b56939SThomas Zimmermann { 94851b56939SThomas Zimmermann struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); 94951b56939SThomas Zimmermann 95051b56939SThomas Zimmermann __drm_atomic_helper_crtc_destroy_state(&mgag200_crtc_state->base); 95151b56939SThomas Zimmermann kfree(mgag200_crtc_state); 95251b56939SThomas Zimmermann } 95351b56939SThomas Zimmermann 95451b56939SThomas Zimmermann static void mgag200_simple_display_pipe_reset_crtc(struct drm_simple_display_pipe *pipe) 95551b56939SThomas Zimmermann { 95651b56939SThomas Zimmermann struct drm_crtc *crtc = &pipe->crtc; 95751b56939SThomas Zimmermann struct mgag200_crtc_state *mgag200_crtc_state; 95851b56939SThomas Zimmermann 95951b56939SThomas Zimmermann if (crtc->state) { 96051b56939SThomas Zimmermann mgag200_simple_display_pipe_destroy_crtc_state(pipe, crtc->state); 96151b56939SThomas Zimmermann crtc->state = NULL; /* must be set to NULL here */ 96251b56939SThomas Zimmermann } 96351b56939SThomas Zimmermann 96451b56939SThomas Zimmermann mgag200_crtc_state = kzalloc(sizeof(*mgag200_crtc_state), GFP_KERNEL); 96551b56939SThomas Zimmermann if (!mgag200_crtc_state) 96651b56939SThomas Zimmermann return; 96751b56939SThomas Zimmermann __drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base); 96851b56939SThomas Zimmermann } 96951b56939SThomas Zimmermann 97088fabb75SThomas Zimmermann static const struct drm_simple_display_pipe_funcs 97188fabb75SThomas Zimmermann mgag200_simple_display_pipe_funcs = { 97288fabb75SThomas Zimmermann .mode_valid = mgag200_simple_display_pipe_mode_valid, 97388fabb75SThomas Zimmermann .enable = mgag200_simple_display_pipe_enable, 97488fabb75SThomas Zimmermann .disable = mgag200_simple_display_pipe_disable, 97588fabb75SThomas Zimmermann .check = mgag200_simple_display_pipe_check, 97688fabb75SThomas Zimmermann .update = mgag200_simple_display_pipe_update, 97751b56939SThomas Zimmermann .reset_crtc = mgag200_simple_display_pipe_reset_crtc, 97851b56939SThomas Zimmermann .duplicate_crtc_state = mgag200_simple_display_pipe_duplicate_crtc_state, 97951b56939SThomas Zimmermann .destroy_crtc_state = mgag200_simple_display_pipe_destroy_crtc_state, 9804862ffaeSThomas Zimmermann DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, 98188fabb75SThomas Zimmermann }; 98288fabb75SThomas Zimmermann 98388fabb75SThomas Zimmermann static const uint32_t mgag200_simple_display_pipe_formats[] = { 98488fabb75SThomas Zimmermann DRM_FORMAT_XRGB8888, 98588fabb75SThomas Zimmermann DRM_FORMAT_RGB565, 98688fabb75SThomas Zimmermann DRM_FORMAT_RGB888, 98788fabb75SThomas Zimmermann }; 98888fabb75SThomas Zimmermann 98988fabb75SThomas Zimmermann static const uint64_t mgag200_simple_display_pipe_fmtmods[] = { 99088fabb75SThomas Zimmermann DRM_FORMAT_MOD_LINEAR, 99188fabb75SThomas Zimmermann DRM_FORMAT_MOD_INVALID 99288fabb75SThomas Zimmermann }; 99388fabb75SThomas Zimmermann 99488fabb75SThomas Zimmermann /* 99588fabb75SThomas Zimmermann * Mode config 99688fabb75SThomas Zimmermann */ 99788fabb75SThomas Zimmermann 998475e2b97SThomas Zimmermann /* Calculates a mode's required memory bandwidth (in KiB/sec). */ 999475e2b97SThomas Zimmermann static uint32_t mgag200_calculate_mode_bandwidth(const struct drm_display_mode *mode, 1000475e2b97SThomas Zimmermann unsigned int bits_per_pixel) 1001475e2b97SThomas Zimmermann { 1002475e2b97SThomas Zimmermann uint32_t total_area, divisor; 1003475e2b97SThomas Zimmermann uint64_t active_area, pixels_per_second, bandwidth; 1004475e2b97SThomas Zimmermann uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8; 1005475e2b97SThomas Zimmermann 1006475e2b97SThomas Zimmermann divisor = 1024; 1007475e2b97SThomas Zimmermann 1008475e2b97SThomas Zimmermann if (!mode->htotal || !mode->vtotal || !mode->clock) 1009475e2b97SThomas Zimmermann return 0; 1010475e2b97SThomas Zimmermann 1011475e2b97SThomas Zimmermann active_area = mode->hdisplay * mode->vdisplay; 1012475e2b97SThomas Zimmermann total_area = mode->htotal * mode->vtotal; 1013475e2b97SThomas Zimmermann 1014475e2b97SThomas Zimmermann pixels_per_second = active_area * mode->clock * 1000; 1015475e2b97SThomas Zimmermann do_div(pixels_per_second, total_area); 1016475e2b97SThomas Zimmermann 1017475e2b97SThomas Zimmermann bandwidth = pixels_per_second * bytes_per_pixel * 100; 1018475e2b97SThomas Zimmermann do_div(bandwidth, divisor); 1019475e2b97SThomas Zimmermann 1020475e2b97SThomas Zimmermann return (uint32_t)bandwidth; 1021475e2b97SThomas Zimmermann } 1022475e2b97SThomas Zimmermann 102369340e52SThomas Zimmermann static enum drm_mode_status mgag200_mode_config_mode_valid(struct drm_device *dev, 102469340e52SThomas Zimmermann const struct drm_display_mode *mode) 102569340e52SThomas Zimmermann { 102669340e52SThomas Zimmermann static const unsigned int max_bpp = 4; // DRM_FORMAT_XRGB8888 102769340e52SThomas Zimmermann struct mga_device *mdev = to_mga_device(dev); 102869340e52SThomas Zimmermann unsigned long fbsize, fbpages, max_fbpages; 102969340e52SThomas Zimmermann 103069340e52SThomas Zimmermann max_fbpages = mdev->vram_fb_available >> PAGE_SHIFT; 103169340e52SThomas Zimmermann 103269340e52SThomas Zimmermann fbsize = mode->hdisplay * mode->vdisplay * max_bpp; 103369340e52SThomas Zimmermann fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE); 103469340e52SThomas Zimmermann 103569340e52SThomas Zimmermann if (fbpages > max_fbpages) 103669340e52SThomas Zimmermann return MODE_MEM; 103769340e52SThomas Zimmermann 1038475e2b97SThomas Zimmermann if (IS_G200_SE(mdev)) { 1039475e2b97SThomas Zimmermann u32 unique_rev_id = mdev->model.g200se.unique_rev_id; 1040475e2b97SThomas Zimmermann 1041475e2b97SThomas Zimmermann if (unique_rev_id == 0x01) { 1042475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (24400 * 1024)) 1043475e2b97SThomas Zimmermann return MODE_BAD; 1044475e2b97SThomas Zimmermann } else if (unique_rev_id == 0x02) { 1045475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (30100 * 1024)) 1046475e2b97SThomas Zimmermann return MODE_BAD; 1047475e2b97SThomas Zimmermann } else { 1048475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (55000 * 1024)) 1049475e2b97SThomas Zimmermann return MODE_BAD; 1050475e2b97SThomas Zimmermann } 1051475e2b97SThomas Zimmermann } else if (mdev->type == G200_WB) { 1052475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (31877 * 1024)) 1053475e2b97SThomas Zimmermann return MODE_BAD; 1054475e2b97SThomas Zimmermann } else if (mdev->type == G200_EV) { 1055475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (32700 * 1024)) 1056475e2b97SThomas Zimmermann return MODE_BAD; 1057475e2b97SThomas Zimmermann } else if (mdev->type == G200_EH) { 1058475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (37500 * 1024)) 1059475e2b97SThomas Zimmermann return MODE_BAD; 1060475e2b97SThomas Zimmermann } else if (mdev->type == G200_ER) { 1061475e2b97SThomas Zimmermann if (mgag200_calculate_mode_bandwidth(mode, max_bpp * 8) > (55000 * 1024)) 1062475e2b97SThomas Zimmermann return MODE_BAD; 1063475e2b97SThomas Zimmermann } 1064475e2b97SThomas Zimmermann 106569340e52SThomas Zimmermann return MODE_OK; 106669340e52SThomas Zimmermann } 106769340e52SThomas Zimmermann 10685635b7cfSThomas Zimmermann static const struct drm_mode_config_funcs mgag200_mode_config_funcs = { 1069913ec479SThomas Zimmermann .fb_create = drm_gem_fb_create_with_dirty, 107069340e52SThomas Zimmermann .mode_valid = mgag200_mode_config_mode_valid, 107188fabb75SThomas Zimmermann .atomic_check = drm_atomic_helper_check, 107288fabb75SThomas Zimmermann .atomic_commit = drm_atomic_helper_commit, 10735635b7cfSThomas Zimmermann }; 10745635b7cfSThomas Zimmermann 1075414c4531SDave Airlie int mgag200_modeset_init(struct mga_device *mdev) 1076414c4531SDave Airlie { 1077832eddf5SThomas Zimmermann struct drm_device *dev = &mdev->base; 10788219f11fSThomas Zimmermann struct mga_i2c_chan *i2c = &mdev->i2c; 10798219f11fSThomas Zimmermann struct drm_connector *connector = &mdev->connector; 108088fabb75SThomas Zimmermann struct drm_simple_display_pipe *pipe = &mdev->display_pipe; 108188fabb75SThomas Zimmermann size_t format_count = ARRAY_SIZE(mgag200_simple_display_pipe_formats); 108203e44ad1SThomas Zimmermann int ret; 1083414c4531SDave Airlie 108488fabb75SThomas Zimmermann mgag200_init_regs(mdev); 108588fabb75SThomas Zimmermann 10865635b7cfSThomas Zimmermann ret = drmm_mode_config_init(dev); 10875635b7cfSThomas Zimmermann if (ret) { 10885635b7cfSThomas Zimmermann drm_err(dev, "drmm_mode_config_init() failed, error %d\n", 10895635b7cfSThomas Zimmermann ret); 10905635b7cfSThomas Zimmermann return ret; 10915635b7cfSThomas Zimmermann } 10925635b7cfSThomas Zimmermann 1093ed5877b6SThomas Zimmermann dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH; 1094ed5877b6SThomas Zimmermann dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT; 1095*73f54d5dSThomas Zimmermann dev->mode_config.preferred_depth = 24; 1096ed5877b6SThomas Zimmermann dev->mode_config.fb_base = mdev->mc.vram_base; 10975635b7cfSThomas Zimmermann dev->mode_config.funcs = &mgag200_mode_config_funcs; 10985635b7cfSThomas Zimmermann 10998219f11fSThomas Zimmermann ret = mgag200_i2c_init(mdev, i2c); 110081a15b9aSThomas Zimmermann if (ret) { 11018219f11fSThomas Zimmermann drm_err(dev, "failed to add DDC bus: %d\n", ret); 110281a15b9aSThomas Zimmermann return ret; 1103414c4531SDave Airlie } 1104414c4531SDave Airlie 11058219f11fSThomas Zimmermann ret = drm_connector_init_with_ddc(dev, connector, 11068219f11fSThomas Zimmermann &mga_vga_connector_funcs, 11078219f11fSThomas Zimmermann DRM_MODE_CONNECTOR_VGA, 11088219f11fSThomas Zimmermann &i2c->adapter); 11098219f11fSThomas Zimmermann if (ret) { 11108219f11fSThomas Zimmermann drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret); 11118219f11fSThomas Zimmermann return ret; 11128219f11fSThomas Zimmermann } 11138219f11fSThomas Zimmermann drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); 11148219f11fSThomas Zimmermann 11152545ac96SThomas Zimmermann ret = mgag200_pixpll_init(&mdev->pixpll, mdev); 11162545ac96SThomas Zimmermann if (ret) 11172545ac96SThomas Zimmermann return ret; 11182545ac96SThomas Zimmermann 111988fabb75SThomas Zimmermann ret = drm_simple_display_pipe_init(dev, pipe, 112088fabb75SThomas Zimmermann &mgag200_simple_display_pipe_funcs, 112188fabb75SThomas Zimmermann mgag200_simple_display_pipe_formats, 112288fabb75SThomas Zimmermann format_count, 112388fabb75SThomas Zimmermann mgag200_simple_display_pipe_fmtmods, 112488fabb75SThomas Zimmermann connector); 112588fabb75SThomas Zimmermann if (ret) { 112688fabb75SThomas Zimmermann drm_err(dev, 112788fabb75SThomas Zimmermann "drm_simple_display_pipe_init() failed, error %d\n", 112888fabb75SThomas Zimmermann ret); 112988fabb75SThomas Zimmermann return ret; 113088fabb75SThomas Zimmermann } 113188fabb75SThomas Zimmermann 11323064debaSJocelyn Falempe drm_plane_enable_fb_damage_clips(&pipe->plane); 11333064debaSJocelyn Falempe 1134c577b2f4SJocelyn Falempe /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ 113588fabb75SThomas Zimmermann drm_mode_crtc_set_gamma_size(&pipe->crtc, MGAG200_LUT_SIZE); 113688fabb75SThomas Zimmermann 1137c577b2f4SJocelyn Falempe drm_crtc_enable_color_mgmt(&pipe->crtc, 0, false, MGAG200_LUT_SIZE); 1138c577b2f4SJocelyn Falempe 113988fabb75SThomas Zimmermann drm_mode_config_reset(dev); 1140414c4531SDave Airlie 1141414c4531SDave Airlie return 0; 1142414c4531SDave Airlie } 1143