2019-05-31 01:09:37 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2012-04-17 15:01:25 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright 2010 Matt Turner.
|
|
|
|
|
* Copyright 2012 Red Hat
|
|
|
|
|
*
|
|
|
|
|
* Authors: Matthew Garrett
|
|
|
|
|
* Matt Turner
|
|
|
|
|
* Dave Airlie
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/delay.h>
|
2022-02-04 09:05:41 -08:00
|
|
|
#include <linux/iosys-map.h>
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2020-05-15 10:32:32 +02:00
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
|
|
|
#include <drm/drm_atomic_state_helper.h>
|
2012-10-02 18:01:07 +01:00
|
|
|
#include <drm/drm_crtc_helper.h>
|
2020-05-15 10:32:33 +02:00
|
|
|
#include <drm/drm_damage_helper.h>
|
|
|
|
|
#include <drm/drm_format_helper.h>
|
2019-06-23 12:35:42 +02:00
|
|
|
#include <drm/drm_fourcc.h>
|
2022-06-14 12:54:49 +03:00
|
|
|
#include <drm/drm_framebuffer.h>
|
2021-02-08 12:55:35 +01:00
|
|
|
#include <drm/drm_gem_atomic_helper.h>
|
2020-05-07 11:03:13 +02:00
|
|
|
#include <drm/drm_gem_framebuffer_helper.h>
|
2014-10-29 10:03:57 +01:00
|
|
|
#include <drm/drm_plane_helper.h>
|
2020-05-15 10:32:32 +02:00
|
|
|
#include <drm/drm_print.h>
|
2019-01-17 22:03:34 +01:00
|
|
|
#include <drm/drm_probe_helper.h>
|
2020-02-28 09:18:27 +01:00
|
|
|
#include <drm/drm_simple_kms_helper.h>
|
2012-04-17 15:01:25 +01:00
|
|
|
|
|
|
|
|
#include "mgag200_drv.h"
|
|
|
|
|
|
|
|
|
|
#define MGAG200_LUT_SIZE 256
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This file contains setup code for the CRTC.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev,
|
|
|
|
|
const struct drm_format_info *format)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
WREG8(DAC_INDEX + MGA1064_INDEX, 0);
|
2020-05-15 10:32:32 +02:00
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
switch (format->format) {
|
|
|
|
|
case DRM_FORMAT_RGB565:
|
|
|
|
|
/* Use better interpolation, to take 32 values from 0 to 255 */
|
|
|
|
|
for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) {
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4);
|
|
|
|
|
}
|
|
|
|
|
/* Green has one more bit, so add padding with 0 for red and blue. */
|
|
|
|
|
for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) {
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DRM_FORMAT_RGB888:
|
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
|
|
|
|
for (i = 0; i < MGAG200_LUT_SIZE; i++) {
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, i);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
|
|
|
|
|
&format->format);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-15 10:32:32 +02:00
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
static void mgag200_crtc_set_gamma(struct mga_device *mdev,
|
|
|
|
|
const struct drm_format_info *format,
|
|
|
|
|
struct drm_color_lut *lut)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2017-07-13 18:25:34 +02:00
|
|
|
|
2012-04-17 15:01:25 +01:00
|
|
|
WREG8(DAC_INDEX + MGA1064_INDEX, 0);
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
switch (format->format) {
|
|
|
|
|
case DRM_FORMAT_RGB565:
|
|
|
|
|
/* Use better interpolation, to take 32 values from lut[0] to lut[255] */
|
|
|
|
|
for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) {
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8);
|
2013-07-17 15:07:27 +02:00
|
|
|
}
|
2022-05-13 10:49:00 +02:00
|
|
|
/* Green has one more bit, so add padding with 0 for red and blue. */
|
|
|
|
|
for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) {
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, 0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DRM_FORMAT_RGB888:
|
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
|
|
|
|
for (i = 0; i < MGAG200_LUT_SIZE; i++) {
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8);
|
|
|
|
|
WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
|
|
|
|
|
&format->format);
|
|
|
|
|
break;
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void mga_wait_vsync(struct mga_device *mdev)
|
|
|
|
|
{
|
2013-05-06 15:56:17 +00:00
|
|
|
unsigned long timeout = jiffies + HZ/10;
|
2012-04-17 15:01:25 +01:00
|
|
|
unsigned int status = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
status = RREG32(MGAREG_Status);
|
2013-05-06 15:56:17 +00:00
|
|
|
} while ((status & 0x08) && time_before(jiffies, timeout));
|
|
|
|
|
timeout = jiffies + HZ/10;
|
2012-04-17 15:01:25 +01:00
|
|
|
status = 0;
|
|
|
|
|
do {
|
|
|
|
|
status = RREG32(MGAREG_Status);
|
2013-05-06 15:56:17 +00:00
|
|
|
} while (!(status & 0x08) && time_before(jiffies, timeout));
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void mga_wait_busy(struct mga_device *mdev)
|
|
|
|
|
{
|
2013-05-06 15:56:17 +00:00
|
|
|
unsigned long timeout = jiffies + HZ;
|
2012-04-17 15:01:25 +01:00
|
|
|
unsigned int status = 0;
|
|
|
|
|
do {
|
|
|
|
|
status = RREG8(MGAREG_Status + 2);
|
2013-05-06 15:56:17 +00:00
|
|
|
} while ((status & 0x01) && time_before(jiffies, timeout));
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-07 10:24:10 +02:00
|
|
|
static void mgag200_g200wb_hold_bmc(struct mga_device *mdev)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
|
|
|
|
u8 tmp;
|
|
|
|
|
int iter_max;
|
|
|
|
|
|
|
|
|
|
/* 1- The first step is to warn the BMC of an upcoming mode change.
|
|
|
|
|
* We are putting the misc<0> to output.*/
|
|
|
|
|
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
tmp |= 0x10;
|
|
|
|
|
WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
|
|
|
|
|
|
|
|
|
|
/* we are putting a 1 on the misc<0> line */
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
tmp |= 0x10;
|
|
|
|
|
WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
|
|
|
|
|
|
|
|
|
|
/* 2- Second step to mask and further scan request
|
|
|
|
|
* This will be done by asserting the remfreqmsk bit (XSPAREREG<7>)
|
|
|
|
|
*/
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_SPAREREG);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
tmp |= 0x80;
|
|
|
|
|
WREG_DAC(MGA1064_SPAREREG, tmp);
|
|
|
|
|
|
|
|
|
|
/* 3a- the third step is to verifu if there is an active scan
|
|
|
|
|
* We are searching for a 0 on remhsyncsts <XSPAREREG<0>)
|
|
|
|
|
*/
|
|
|
|
|
iter_max = 300;
|
|
|
|
|
while (!(tmp & 0x1) && iter_max) {
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_SPAREREG);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
udelay(1000);
|
|
|
|
|
iter_max--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 3b- this step occurs only if the remove is actually scanning
|
|
|
|
|
* we are waiting for the end of the frame which is a 1 on
|
|
|
|
|
* remvsyncsts (XSPAREREG<1>)
|
|
|
|
|
*/
|
|
|
|
|
if (iter_max) {
|
|
|
|
|
iter_max = 300;
|
|
|
|
|
while ((tmp & 0x2) && iter_max) {
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_SPAREREG);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
udelay(1000);
|
|
|
|
|
iter_max--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 10:24:10 +02:00
|
|
|
static void mgag200_g200wb_release_bmc(struct mga_device *mdev)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
|
|
|
|
u8 tmp;
|
|
|
|
|
|
|
|
|
|
/* 1- The first step is to ensure that the vrsten and hrsten are set */
|
|
|
|
|
WREG8(MGAREG_CRTCEXT_INDEX, 1);
|
|
|
|
|
tmp = RREG8(MGAREG_CRTCEXT_DATA);
|
|
|
|
|
WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88);
|
|
|
|
|
|
|
|
|
|
/* 2- second step is to assert the rstlvl2 */
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
tmp |= 0x8;
|
|
|
|
|
WREG8(DAC_DATA, tmp);
|
|
|
|
|
|
|
|
|
|
/* wait 10 us */
|
|
|
|
|
udelay(10);
|
|
|
|
|
|
|
|
|
|
/* 3- deassert rstlvl2 */
|
|
|
|
|
tmp &= ~0x08;
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
|
|
|
|
|
WREG8(DAC_DATA, tmp);
|
|
|
|
|
|
|
|
|
|
/* 4- remove mask of scan request */
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_SPAREREG);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
tmp &= ~0x80;
|
|
|
|
|
WREG8(DAC_DATA, tmp);
|
|
|
|
|
|
|
|
|
|
/* 5- put back a 0 on the misc<0> line */
|
|
|
|
|
WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
|
|
|
|
|
tmp = RREG8(DAC_DATA);
|
|
|
|
|
tmp &= ~0x10;
|
|
|
|
|
WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-08 19:10:38 +00:00
|
|
|
/*
|
2020-05-15 10:32:20 +02:00
|
|
|
* This is how the framebuffer base address is stored in g200 cards:
|
|
|
|
|
* * Assume @offset is the gpu_addr variable of the framebuffer object
|
|
|
|
|
* * Then addr is the number of _pixels_ (not bytes) from the start of
|
|
|
|
|
* VRAM to the first pixel we want to display. (divided by 2 for 32bit
|
|
|
|
|
* framebuffers)
|
|
|
|
|
* * addr is stored in the CRTCEXT0, CRTCC and CRTCD registers
|
|
|
|
|
* addr<20> -> CRTCEXT0<6>
|
|
|
|
|
* addr<19-16> -> CRTCEXT0<3-0>
|
|
|
|
|
* addr<15-8> -> CRTCC<7-0>
|
|
|
|
|
* addr<7-0> -> CRTCD<7-0>
|
|
|
|
|
*
|
|
|
|
|
* CRTCEXT0 has to be programmed last to trigger an update and make the
|
|
|
|
|
* new addr variable take effect.
|
2013-05-08 19:10:38 +00:00
|
|
|
*/
|
2020-05-15 10:32:20 +02:00
|
|
|
static void mgag200_set_startadd(struct mga_device *mdev,
|
|
|
|
|
unsigned long offset)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
2020-06-05 15:58:02 +02:00
|
|
|
struct drm_device *dev = &mdev->base;
|
2020-05-15 10:32:20 +02:00
|
|
|
u32 startadd;
|
|
|
|
|
u8 crtcc, crtcd, crtcext0;
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2020-05-15 10:32:20 +02:00
|
|
|
startadd = offset / 8;
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2022-05-04 15:40:26 +02:00
|
|
|
if (startadd > 0)
|
2022-06-01 13:25:19 +02:00
|
|
|
drm_WARN_ON_ONCE(dev, mdev->info->bug_no_startadd);
|
2022-05-04 15:40:26 +02:00
|
|
|
|
2020-05-15 10:32:20 +02:00
|
|
|
/*
|
|
|
|
|
* Can't store addresses any higher than that, but we also
|
|
|
|
|
* don't have more than 16 MiB of memory, so it should be fine.
|
|
|
|
|
*/
|
|
|
|
|
drm_WARN_ON(dev, startadd > 0x1fffff);
|
|
|
|
|
|
|
|
|
|
RREG_ECRT(0x00, crtcext0);
|
|
|
|
|
|
|
|
|
|
crtcc = (startadd >> 8) & 0xff;
|
|
|
|
|
crtcd = startadd & 0xff;
|
|
|
|
|
crtcext0 &= 0xb0;
|
|
|
|
|
crtcext0 |= ((startadd >> 14) & BIT(6)) |
|
|
|
|
|
((startadd >> 16) & 0x0f);
|
|
|
|
|
|
|
|
|
|
WREG_CRT(0x0c, crtcc);
|
|
|
|
|
WREG_CRT(0x0d, crtcd);
|
|
|
|
|
WREG_ECRT(0x00, crtcext0);
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:29 +02:00
|
|
|
static void mgag200_set_dac_regs(struct mga_device *mdev)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
u8 dacvalue[] = {
|
|
|
|
|
/* 0x00: */ 0, 0, 0, 0, 0, 0, 0x00, 0,
|
|
|
|
|
/* 0x08: */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
/* 0x10: */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
/* 0x18: */ 0x00, 0, 0xC9, 0xFF, 0xBF, 0x20, 0x1F, 0x20,
|
|
|
|
|
/* 0x20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
/* 0x28: */ 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x40,
|
|
|
|
|
/* 0x30: */ 0x00, 0xB0, 0x00, 0xC2, 0x34, 0x14, 0x02, 0x83,
|
|
|
|
|
/* 0x38: */ 0x00, 0x93, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3A,
|
|
|
|
|
/* 0x40: */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
/* 0x48: */ 0, 0, 0, 0, 0, 0, 0, 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (mdev->type) {
|
2020-07-30 12:28:44 +02:00
|
|
|
case G200_PCI:
|
|
|
|
|
case G200_AGP:
|
|
|
|
|
dacvalue[MGA1064_SYS_PLL_M] = 0x04;
|
|
|
|
|
dacvalue[MGA1064_SYS_PLL_N] = 0x2D;
|
|
|
|
|
dacvalue[MGA1064_SYS_PLL_P] = 0x19;
|
|
|
|
|
break;
|
2020-05-15 10:32:29 +02:00
|
|
|
case G200_SE_A:
|
|
|
|
|
case G200_SE_B:
|
|
|
|
|
dacvalue[MGA1064_VREF_CTL] = 0x03;
|
|
|
|
|
dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL;
|
|
|
|
|
dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_DAC_EN |
|
|
|
|
|
MGA1064_MISC_CTL_VGA8 |
|
|
|
|
|
MGA1064_MISC_CTL_DAC_RAM_CS;
|
|
|
|
|
break;
|
|
|
|
|
case G200_WB:
|
|
|
|
|
case G200_EW3:
|
|
|
|
|
dacvalue[MGA1064_VREF_CTL] = 0x07;
|
|
|
|
|
break;
|
|
|
|
|
case G200_EV:
|
|
|
|
|
dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL;
|
|
|
|
|
dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 |
|
|
|
|
|
MGA1064_MISC_CTL_DAC_RAM_CS;
|
|
|
|
|
break;
|
|
|
|
|
case G200_EH:
|
|
|
|
|
case G200_EH3:
|
|
|
|
|
dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 |
|
|
|
|
|
MGA1064_MISC_CTL_DAC_RAM_CS;
|
|
|
|
|
break;
|
|
|
|
|
case G200_ER:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
|
|
|
|
|
if ((i <= 0x17) ||
|
|
|
|
|
(i == 0x1b) ||
|
|
|
|
|
(i == 0x1c) ||
|
|
|
|
|
((i >= 0x1f) && (i <= 0x29)) ||
|
|
|
|
|
((i >= 0x30) && (i <= 0x37)))
|
|
|
|
|
continue;
|
|
|
|
|
if (IS_G200_SE(mdev) &&
|
|
|
|
|
((i == 0x2c) || (i == 0x2d) || (i == 0x2e)))
|
|
|
|
|
continue;
|
|
|
|
|
if ((mdev->type == G200_EV ||
|
|
|
|
|
mdev->type == G200_WB ||
|
|
|
|
|
mdev->type == G200_EH ||
|
|
|
|
|
mdev->type == G200_EW3 ||
|
|
|
|
|
mdev->type == G200_EH3) &&
|
|
|
|
|
(i >= 0x44) && (i <= 0x4e))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
WREG_DAC(i, dacvalue[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mdev->type == G200_ER)
|
|
|
|
|
WREG_DAC(0x90, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mgag200_init_regs(struct mga_device *mdev)
|
|
|
|
|
{
|
2020-07-30 12:28:42 +02:00
|
|
|
u8 crtc11, misc;
|
2020-05-15 10:32:29 +02:00
|
|
|
|
|
|
|
|
mgag200_set_dac_regs(mdev);
|
|
|
|
|
|
|
|
|
|
WREG_SEQ(2, 0x0f);
|
|
|
|
|
WREG_SEQ(3, 0x00);
|
|
|
|
|
WREG_SEQ(4, 0x0e);
|
|
|
|
|
|
|
|
|
|
WREG_CRT(10, 0);
|
|
|
|
|
WREG_CRT(11, 0);
|
|
|
|
|
WREG_CRT(12, 0);
|
|
|
|
|
WREG_CRT(13, 0);
|
|
|
|
|
WREG_CRT(14, 0);
|
|
|
|
|
WREG_CRT(15, 0);
|
|
|
|
|
|
2020-07-07 10:24:05 +02:00
|
|
|
RREG_CRT(0x11, crtc11);
|
|
|
|
|
crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT |
|
|
|
|
|
MGAREG_CRTC11_VINTEN |
|
|
|
|
|
MGAREG_CRTC11_VINTCLR);
|
|
|
|
|
WREG_CRT(0x11, crtc11);
|
|
|
|
|
|
2020-05-15 10:32:29 +02:00
|
|
|
if (mdev->type == G200_ER)
|
|
|
|
|
WREG_ECRT(0x24, 0x5);
|
|
|
|
|
|
|
|
|
|
if (mdev->type == G200_EW3)
|
|
|
|
|
WREG_ECRT(0x34, 0x5);
|
|
|
|
|
|
|
|
|
|
misc = RREG8(MGA_MISC_IN);
|
2020-07-30 12:28:41 +02:00
|
|
|
misc |= MGAREG_MISC_IOADSEL;
|
2020-05-15 10:32:29 +02:00
|
|
|
WREG8(MGA_MISC_OUT, misc);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:22 +02:00
|
|
|
static void mgag200_set_mode_regs(struct mga_device *mdev,
|
|
|
|
|
const struct drm_display_mode *mode)
|
|
|
|
|
{
|
2022-06-01 13:25:21 +02:00
|
|
|
const struct mgag200_device_info *info = mdev->info;
|
2020-05-15 10:32:22 +02:00
|
|
|
unsigned int hdisplay, hsyncstart, hsyncend, htotal;
|
|
|
|
|
unsigned int vdisplay, vsyncstart, vsyncend, vtotal;
|
2020-05-15 10:32:23 +02:00
|
|
|
u8 misc, crtcext1, crtcext2, crtcext5;
|
2020-05-15 10:32:22 +02:00
|
|
|
|
|
|
|
|
hdisplay = mode->hdisplay / 8 - 1;
|
|
|
|
|
hsyncstart = mode->hsync_start / 8 - 1;
|
|
|
|
|
hsyncend = mode->hsync_end / 8 - 1;
|
|
|
|
|
htotal = mode->htotal / 8 - 1;
|
|
|
|
|
|
|
|
|
|
/* Work around hardware quirk */
|
|
|
|
|
if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04)
|
|
|
|
|
htotal++;
|
|
|
|
|
|
|
|
|
|
vdisplay = mode->vdisplay - 1;
|
|
|
|
|
vsyncstart = mode->vsync_start - 1;
|
|
|
|
|
vsyncend = mode->vsync_end - 1;
|
|
|
|
|
vtotal = mode->vtotal - 2;
|
|
|
|
|
|
2020-05-15 10:32:23 +02:00
|
|
|
misc = RREG8(MGA_MISC_IN);
|
|
|
|
|
|
2020-05-15 10:32:22 +02:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
2020-05-15 10:32:23 +02:00
|
|
|
misc |= MGAREG_MISC_HSYNCPOL;
|
|
|
|
|
else
|
|
|
|
|
misc &= ~MGAREG_MISC_HSYNCPOL;
|
|
|
|
|
|
2020-05-15 10:32:22 +02:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
2020-05-15 10:32:23 +02:00
|
|
|
misc |= MGAREG_MISC_VSYNCPOL;
|
|
|
|
|
else
|
|
|
|
|
misc &= ~MGAREG_MISC_VSYNCPOL;
|
2020-05-15 10:32:22 +02:00
|
|
|
|
|
|
|
|
crtcext1 = (((htotal - 4) & 0x100) >> 8) |
|
|
|
|
|
((hdisplay & 0x100) >> 7) |
|
|
|
|
|
((hsyncstart & 0x100) >> 6) |
|
|
|
|
|
(htotal & 0x40);
|
2022-06-01 13:25:21 +02:00
|
|
|
if (info->has_vidrst)
|
|
|
|
|
crtcext1 |= MGAREG_CRTCEXT1_VRSTEN |
|
|
|
|
|
MGAREG_CRTCEXT1_HRSTEN;
|
2020-05-15 10:32:22 +02:00
|
|
|
|
|
|
|
|
crtcext2 = ((vtotal & 0xc00) >> 10) |
|
|
|
|
|
((vdisplay & 0x400) >> 8) |
|
|
|
|
|
((vdisplay & 0xc00) >> 7) |
|
|
|
|
|
((vsyncstart & 0xc00) >> 5) |
|
|
|
|
|
((vdisplay & 0x400) >> 3);
|
|
|
|
|
crtcext5 = 0x00;
|
|
|
|
|
|
|
|
|
|
WREG_CRT(0, htotal - 4);
|
|
|
|
|
WREG_CRT(1, hdisplay);
|
|
|
|
|
WREG_CRT(2, hdisplay);
|
|
|
|
|
WREG_CRT(3, (htotal & 0x1F) | 0x80);
|
|
|
|
|
WREG_CRT(4, hsyncstart);
|
|
|
|
|
WREG_CRT(5, ((htotal & 0x20) << 2) | (hsyncend & 0x1F));
|
|
|
|
|
WREG_CRT(6, vtotal & 0xFF);
|
|
|
|
|
WREG_CRT(7, ((vtotal & 0x100) >> 8) |
|
|
|
|
|
((vdisplay & 0x100) >> 7) |
|
|
|
|
|
((vsyncstart & 0x100) >> 6) |
|
|
|
|
|
((vdisplay & 0x100) >> 5) |
|
|
|
|
|
((vdisplay & 0x100) >> 4) | /* linecomp */
|
|
|
|
|
((vtotal & 0x200) >> 4) |
|
|
|
|
|
((vdisplay & 0x200) >> 3) |
|
|
|
|
|
((vsyncstart & 0x200) >> 2));
|
|
|
|
|
WREG_CRT(9, ((vdisplay & 0x200) >> 4) |
|
|
|
|
|
((vdisplay & 0x200) >> 3));
|
|
|
|
|
WREG_CRT(16, vsyncstart & 0xFF);
|
|
|
|
|
WREG_CRT(17, (vsyncend & 0x0F) | 0x20);
|
|
|
|
|
WREG_CRT(18, vdisplay & 0xFF);
|
|
|
|
|
WREG_CRT(20, 0);
|
|
|
|
|
WREG_CRT(21, vdisplay & 0xFF);
|
|
|
|
|
WREG_CRT(22, (vtotal + 1) & 0xFF);
|
|
|
|
|
WREG_CRT(23, 0xc3);
|
|
|
|
|
WREG_CRT(24, vdisplay & 0xFF);
|
|
|
|
|
|
|
|
|
|
WREG_ECRT(0x01, crtcext1);
|
|
|
|
|
WREG_ECRT(0x02, crtcext2);
|
|
|
|
|
WREG_ECRT(0x05, crtcext5);
|
2020-05-15 10:32:23 +02:00
|
|
|
|
|
|
|
|
WREG8(MGA_MISC_OUT, misc);
|
2020-05-15 10:32:22 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-02 09:56:42 +02:00
|
|
|
static u8 mgag200_get_bpp_shift(const struct drm_format_info *format)
|
2020-05-15 10:32:25 +02:00
|
|
|
{
|
2021-07-02 09:56:42 +02:00
|
|
|
static const u8 bpp_shift[] = {0, 1, 0, 2};
|
|
|
|
|
|
|
|
|
|
return bpp_shift[format->cpp[0] - 1];
|
2020-05-15 10:32:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Calculates the HW offset value from the framebuffer's pitch. The
|
|
|
|
|
* offset is a multiple of the pixel size and depends on the display
|
|
|
|
|
* format.
|
|
|
|
|
*/
|
|
|
|
|
static u32 mgag200_calculate_offset(struct mga_device *mdev,
|
|
|
|
|
const struct drm_framebuffer *fb)
|
|
|
|
|
{
|
|
|
|
|
u32 offset = fb->pitches[0] / fb->format->cpp[0];
|
2021-07-02 09:56:42 +02:00
|
|
|
u8 bppshift = mgag200_get_bpp_shift(fb->format);
|
2020-05-15 10:32:25 +02:00
|
|
|
|
|
|
|
|
if (fb->format->cpp[0] * 8 == 24)
|
|
|
|
|
offset = (offset * 3) >> (4 - bppshift);
|
|
|
|
|
else
|
|
|
|
|
offset = offset >> (4 - bppshift);
|
|
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mgag200_set_offset(struct mga_device *mdev,
|
|
|
|
|
const struct drm_framebuffer *fb)
|
|
|
|
|
{
|
|
|
|
|
u8 crtc13, crtcext0;
|
|
|
|
|
u32 offset = mgag200_calculate_offset(mdev, fb);
|
|
|
|
|
|
|
|
|
|
RREG_ECRT(0, crtcext0);
|
|
|
|
|
|
|
|
|
|
crtc13 = offset & 0xff;
|
|
|
|
|
|
|
|
|
|
crtcext0 &= ~MGAREG_CRTCEXT0_OFFSET_MASK;
|
|
|
|
|
crtcext0 |= (offset >> 4) & MGAREG_CRTCEXT0_OFFSET_MASK;
|
|
|
|
|
|
|
|
|
|
WREG_CRT(0x13, crtc13);
|
|
|
|
|
WREG_ECRT(0x00, crtcext0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:26 +02:00
|
|
|
static void mgag200_set_format_regs(struct mga_device *mdev,
|
|
|
|
|
const struct drm_framebuffer *fb)
|
|
|
|
|
{
|
2020-06-05 15:58:02 +02:00
|
|
|
struct drm_device *dev = &mdev->base;
|
2020-05-15 10:32:26 +02:00
|
|
|
const struct drm_format_info *format = fb->format;
|
|
|
|
|
unsigned int bpp, bppshift, scale;
|
|
|
|
|
u8 crtcext3, xmulctrl;
|
|
|
|
|
|
|
|
|
|
bpp = format->cpp[0] * 8;
|
|
|
|
|
|
2021-07-02 09:56:42 +02:00
|
|
|
bppshift = mgag200_get_bpp_shift(format);
|
2020-05-15 10:32:26 +02:00
|
|
|
switch (bpp) {
|
|
|
|
|
case 24:
|
|
|
|
|
scale = ((1 << bppshift) * 3) - 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
scale = (1 << bppshift) - 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RREG_ECRT(3, crtcext3);
|
|
|
|
|
|
|
|
|
|
switch (bpp) {
|
|
|
|
|
case 8:
|
|
|
|
|
xmulctrl = MGA1064_MUL_CTL_8bits;
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
if (format->depth == 15)
|
|
|
|
|
xmulctrl = MGA1064_MUL_CTL_15bits;
|
|
|
|
|
else
|
|
|
|
|
xmulctrl = MGA1064_MUL_CTL_16bits;
|
|
|
|
|
break;
|
|
|
|
|
case 24:
|
|
|
|
|
xmulctrl = MGA1064_MUL_CTL_24bits;
|
|
|
|
|
break;
|
|
|
|
|
case 32:
|
|
|
|
|
xmulctrl = MGA1064_MUL_CTL_32_24bits;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* BUG: We should have caught this problem already. */
|
|
|
|
|
drm_WARN_ON(dev, "invalid format depth\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crtcext3 &= ~GENMASK(2, 0);
|
|
|
|
|
crtcext3 |= scale;
|
|
|
|
|
|
|
|
|
|
WREG_DAC(MGA1064_MUL_CTL, xmulctrl);
|
|
|
|
|
|
|
|
|
|
WREG_GFX(0, 0x00);
|
|
|
|
|
WREG_GFX(1, 0x00);
|
|
|
|
|
WREG_GFX(2, 0x00);
|
|
|
|
|
WREG_GFX(3, 0x00);
|
|
|
|
|
WREG_GFX(4, 0x00);
|
|
|
|
|
WREG_GFX(5, 0x40);
|
mgag200 fix memmapsl configuration in GCTL6 register
On some servers with MGA G200_SE_A (rev 42), booting with Legacy BIOS,
the hardware hangs when using kdump and kexec into the kdump kernel.
This happens when the uncompress code tries to write "Decompressing Linux"
to the VGA Console.
It can be reproduced by writing to the VGA console (0xB8000) after
booting to graphic mode, it generates the following error:
kernel:NMI: PCI system error (SERR) for reason a0 on CPU 0.
kernel:Dazed and confused, but trying to continue
The root cause is the configuration of the MGA GCTL6 register
According to the GCTL6 register documentation:
bit 0 is gcgrmode:
0: Enables alpha mode, and the character generator addressing system is
activated.
1: Enables graphics mode, and the character addressing system is not
used.
bit 1 is chainodd even:
0: The A0 signal of the memory address bus is used during system memory
addressing.
1: Allows A0 to be replaced by either the A16 signal of the system
address (ifmemmapsl is ‘00’), or by the hpgoddev (MISC<5>, odd/even
page select) field, described on page 3-294).
bit 3-2 are memmapsl:
Memory map select bits 1 and 0. VGA.
These bits select where the video memory is mapped, as shown below:
00 => A0000h - BFFFFh
01 => A0000h - AFFFFh
10 => B0000h - B7FFFh
11 => B8000h - BFFFFh
bit 7-4 are reserved.
Current code set it to 0x05 => memmapsl to b01 => 0xa0000 (graphic mode)
But on x86, the VGA console is at 0xb8000 (text mode)
In arch/x86/boot/compressed/misc.c debug strings are written to 0xb8000
As the driver doesn't use this mapping at 0xa0000, it is safe to set it to
0xb8000 instead, to avoid kernel hang on G200_SE_A rev42, with kexec/kdump.
Thus changing the value 0x05 to 0x0d
Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Lyude Paul <lyude@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20220119102905.1194787-1-jfalempe@redhat.com
2022-01-19 11:29:05 +01:00
|
|
|
/* GCTL6 should be 0x05, but we configure memmapsl to 0xb8000 (text mode),
|
|
|
|
|
* so that it doesn't hang when running kexec/kdump on G200_SE rev42.
|
|
|
|
|
*/
|
|
|
|
|
WREG_GFX(6, 0x0d);
|
2020-05-15 10:32:26 +02:00
|
|
|
WREG_GFX(7, 0x0f);
|
|
|
|
|
WREG_GFX(8, 0x0f);
|
|
|
|
|
|
|
|
|
|
WREG_ECRT(3, crtcext3);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:27 +02:00
|
|
|
static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev)
|
|
|
|
|
{
|
|
|
|
|
static uint32_t RESET_FLAG = 0x00200000; /* undocumented magic value */
|
|
|
|
|
u32 memctl;
|
|
|
|
|
|
|
|
|
|
memctl = RREG32(MGAREG_MEMCTL);
|
|
|
|
|
|
|
|
|
|
memctl |= RESET_FLAG;
|
|
|
|
|
WREG32(MGAREG_MEMCTL, memctl);
|
|
|
|
|
|
|
|
|
|
udelay(1000);
|
|
|
|
|
|
|
|
|
|
memctl &= ~RESET_FLAG;
|
|
|
|
|
WREG32(MGAREG_MEMCTL, memctl);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:28 +02:00
|
|
|
static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
|
|
|
|
|
const struct drm_display_mode *mode,
|
|
|
|
|
const struct drm_framebuffer *fb)
|
|
|
|
|
{
|
2022-06-01 13:25:14 +02:00
|
|
|
struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base);
|
2020-05-15 10:32:28 +02:00
|
|
|
unsigned int hiprilvl;
|
|
|
|
|
u8 crtcext6;
|
|
|
|
|
|
2022-06-01 13:25:14 +02:00
|
|
|
if (g200se->unique_rev_id >= 0x04) {
|
2020-05-15 10:32:28 +02:00
|
|
|
hiprilvl = 0;
|
2022-06-01 13:25:14 +02:00
|
|
|
} else if (g200se->unique_rev_id >= 0x02) {
|
2020-05-15 10:32:28 +02:00
|
|
|
unsigned int bpp;
|
|
|
|
|
unsigned long mb;
|
|
|
|
|
|
|
|
|
|
if (fb->format->cpp[0] * 8 > 16)
|
|
|
|
|
bpp = 32;
|
|
|
|
|
else if (fb->format->cpp[0] * 8 > 8)
|
|
|
|
|
bpp = 16;
|
|
|
|
|
else
|
|
|
|
|
bpp = 8;
|
|
|
|
|
|
|
|
|
|
mb = (mode->clock * bpp) / 1000;
|
|
|
|
|
if (mb > 3100)
|
|
|
|
|
hiprilvl = 0;
|
|
|
|
|
else if (mb > 2600)
|
|
|
|
|
hiprilvl = 1;
|
|
|
|
|
else if (mb > 1900)
|
|
|
|
|
hiprilvl = 2;
|
|
|
|
|
else if (mb > 1160)
|
|
|
|
|
hiprilvl = 3;
|
|
|
|
|
else if (mb > 440)
|
|
|
|
|
hiprilvl = 4;
|
|
|
|
|
else
|
|
|
|
|
hiprilvl = 5;
|
|
|
|
|
|
2022-06-01 13:25:14 +02:00
|
|
|
} else if (g200se->unique_rev_id >= 0x01) {
|
2020-05-15 10:32:28 +02:00
|
|
|
hiprilvl = 3;
|
|
|
|
|
} else {
|
|
|
|
|
hiprilvl = 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crtcext6 = hiprilvl; /* implicitly sets maxhipri to 0 */
|
|
|
|
|
|
|
|
|
|
WREG_ECRT(0x06, crtcext6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mgag200_g200ev_set_hiprilvl(struct mga_device *mdev)
|
|
|
|
|
{
|
|
|
|
|
WREG_ECRT(0x06, 0x00);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 10:24:08 +02:00
|
|
|
static void mgag200_enable_display(struct mga_device *mdev)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
2020-07-07 10:24:09 +02:00
|
|
|
u8 seq0, seq1, crtcext1;
|
|
|
|
|
|
|
|
|
|
RREG_SEQ(0x00, seq0);
|
|
|
|
|
seq0 |= MGAREG_SEQ0_SYNCRST |
|
|
|
|
|
MGAREG_SEQ0_ASYNCRST;
|
|
|
|
|
WREG_SEQ(0x00, seq0);
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2020-07-07 10:24:08 +02:00
|
|
|
/*
|
|
|
|
|
* TODO: replace busy waiting with vblank IRQ; put
|
|
|
|
|
* msleep(50) before changing SCROFF
|
|
|
|
|
*/
|
|
|
|
|
mga_wait_vsync(mdev);
|
|
|
|
|
mga_wait_busy(mdev);
|
|
|
|
|
|
|
|
|
|
RREG_SEQ(0x01, seq1);
|
|
|
|
|
seq1 &= ~MGAREG_SEQ1_SCROFF;
|
|
|
|
|
WREG_SEQ(0x01, seq1);
|
|
|
|
|
|
|
|
|
|
msleep(20);
|
|
|
|
|
|
|
|
|
|
RREG_ECRT(0x01, crtcext1);
|
|
|
|
|
crtcext1 &= ~MGAREG_CRTCEXT1_VSYNCOFF;
|
|
|
|
|
crtcext1 &= ~MGAREG_CRTCEXT1_HSYNCOFF;
|
|
|
|
|
WREG_ECRT(0x01, crtcext1);
|
|
|
|
|
}
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2020-07-07 10:24:08 +02:00
|
|
|
static void mgag200_disable_display(struct mga_device *mdev)
|
|
|
|
|
{
|
2020-07-07 10:24:09 +02:00
|
|
|
u8 seq0, seq1, crtcext1;
|
|
|
|
|
|
|
|
|
|
RREG_SEQ(0x00, seq0);
|
|
|
|
|
seq0 &= ~MGAREG_SEQ0_SYNCRST;
|
|
|
|
|
WREG_SEQ(0x00, seq0);
|
2020-07-07 10:24:08 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* TODO: replace busy waiting with vblank IRQ; put
|
|
|
|
|
* msleep(50) before changing SCROFF
|
|
|
|
|
*/
|
2012-04-17 15:01:25 +01:00
|
|
|
mga_wait_vsync(mdev);
|
|
|
|
|
mga_wait_busy(mdev);
|
2020-07-07 10:24:08 +02:00
|
|
|
|
|
|
|
|
RREG_SEQ(0x01, seq1);
|
|
|
|
|
seq1 |= MGAREG_SEQ1_SCROFF;
|
|
|
|
|
WREG_SEQ(0x01, seq1);
|
|
|
|
|
|
2012-04-17 15:01:25 +01:00
|
|
|
msleep(20);
|
2020-07-07 10:24:08 +02:00
|
|
|
|
|
|
|
|
RREG_ECRT(0x01, crtcext1);
|
|
|
|
|
crtcext1 |= MGAREG_CRTCEXT1_VSYNCOFF |
|
|
|
|
|
MGAREG_CRTCEXT1_HSYNCOFF;
|
|
|
|
|
WREG_ECRT(0x01, crtcext1);
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-07 11:03:15 +02:00
|
|
|
/*
|
|
|
|
|
* Connector
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-16 15:43:39 +02:00
|
|
|
static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
2022-05-16 15:43:37 +02:00
|
|
|
struct mga_device *mdev = to_mga_device(connector->dev);
|
2022-05-16 15:43:39 +02:00
|
|
|
int ret;
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2022-05-16 15:43:37 +02:00
|
|
|
/*
|
|
|
|
|
* Protect access to I/O registers from concurrent modesetting
|
|
|
|
|
* by acquiring the I/O-register lock.
|
|
|
|
|
*/
|
|
|
|
|
mutex_lock(&mdev->rmmio_lock);
|
2022-05-16 15:43:39 +02:00
|
|
|
ret = drm_connector_helper_get_modes_from_ddc(connector);
|
2022-05-16 15:43:37 +02:00
|
|
|
mutex_unlock(&mdev->rmmio_lock);
|
|
|
|
|
|
2012-04-17 15:01:25 +01:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 15:43:43 +02:00
|
|
|
static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = {
|
|
|
|
|
.get_modes = mgag200_vga_connector_helper_get_modes,
|
|
|
|
|
};
|
2013-06-27 13:38:59 -04:00
|
|
|
|
2022-05-16 15:43:43 +02:00
|
|
|
static const struct drm_connector_funcs mga_vga_connector_funcs = {
|
|
|
|
|
.reset = drm_atomic_helper_connector_reset,
|
|
|
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
|
|
|
|
.destroy = drm_connector_cleanup,
|
|
|
|
|
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
|
|
|
|
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
|
|
|
|
};
|
2013-06-27 13:38:59 -04:00
|
|
|
|
2022-05-16 15:43:43 +02:00
|
|
|
/*
|
|
|
|
|
* Simple Display Pipe
|
|
|
|
|
*/
|
2013-06-27 13:38:59 -04:00
|
|
|
|
2022-05-16 15:43:43 +02:00
|
|
|
static enum drm_mode_status
|
|
|
|
|
mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
|
|
|
|
|
const struct drm_display_mode *mode)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
2022-06-01 13:25:20 +02:00
|
|
|
struct mga_device *mdev = to_mga_device(pipe->crtc.dev);
|
|
|
|
|
const struct mgag200_device_info *info = mdev->info;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some devices have additional limits on the size of the
|
|
|
|
|
* display mode.
|
|
|
|
|
*/
|
|
|
|
|
if (mode->hdisplay > info->max_hdisplay)
|
|
|
|
|
return MODE_VIRTUAL_X;
|
|
|
|
|
if (mode->vdisplay > info->max_vdisplay)
|
|
|
|
|
return MODE_VIRTUAL_Y;
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2015-06-15 16:16:15 -04:00
|
|
|
if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
|
|
|
|
|
(mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
|
|
|
|
|
return MODE_H_ILLEGAL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-17 15:01:25 +01:00
|
|
|
if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
|
|
|
|
|
mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
|
|
|
|
|
mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
|
|
|
|
|
mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) {
|
|
|
|
|
return MODE_BAD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:33 +02:00
|
|
|
static void
|
|
|
|
|
mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb,
|
2022-02-04 09:05:41 -08:00
|
|
|
struct drm_rect *clip, const struct iosys_map *map)
|
2020-05-15 10:32:33 +02:00
|
|
|
{
|
2021-11-10 11:36:55 +01:00
|
|
|
void __iomem *dst = mdev->vram;
|
2021-02-08 12:55:35 +01:00
|
|
|
void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */
|
2020-05-15 10:32:33 +02:00
|
|
|
|
2021-11-10 11:36:55 +01:00
|
|
|
dst += drm_fb_clip_offset(fb->pitches[0], fb->format, clip);
|
|
|
|
|
drm_fb_memcpy_toio(dst, fb->pitches[0], vmap, fb, clip);
|
2020-05-15 10:32:33 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:32 +02:00
|
|
|
static void
|
|
|
|
|
mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
|
|
|
|
|
struct drm_crtc_state *crtc_state,
|
|
|
|
|
struct drm_plane_state *plane_state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_crtc *crtc = &pipe->crtc;
|
|
|
|
|
struct drm_device *dev = crtc->dev;
|
|
|
|
|
struct mga_device *mdev = to_mga_device(dev);
|
2021-07-14 16:22:40 +02:00
|
|
|
struct mgag200_pll *pixpll = &mdev->pixpll;
|
2020-05-15 10:32:32 +02:00
|
|
|
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
|
2021-07-14 16:22:40 +02:00
|
|
|
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
|
2020-05-15 10:32:32 +02:00
|
|
|
struct drm_framebuffer *fb = plane_state->fb;
|
2021-02-08 12:55:35 +01:00
|
|
|
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
|
2020-05-15 10:32:33 +02:00
|
|
|
struct drm_rect fullscreen = {
|
|
|
|
|
.x1 = 0,
|
|
|
|
|
.x2 = fb->width,
|
|
|
|
|
.y1 = 0,
|
|
|
|
|
.y2 = fb->height,
|
|
|
|
|
};
|
2020-05-15 10:32:32 +02:00
|
|
|
|
2022-05-02 16:25:14 +02:00
|
|
|
/*
|
|
|
|
|
* Concurrent operations could possibly trigger a call to
|
|
|
|
|
* drm_connector_helper_funcs.get_modes by trying to read the
|
|
|
|
|
* display modes. Protect access to I/O registers by acquiring
|
|
|
|
|
* the I/O-register lock.
|
|
|
|
|
*/
|
|
|
|
|
mutex_lock(&mdev->rmmio_lock);
|
|
|
|
|
|
2020-07-07 10:24:11 +02:00
|
|
|
if (mdev->type == G200_WB || mdev->type == G200_EW3)
|
|
|
|
|
mgag200_g200wb_hold_bmc(mdev);
|
2020-05-15 10:32:32 +02:00
|
|
|
|
|
|
|
|
mgag200_set_format_regs(mdev, fb);
|
|
|
|
|
mgag200_set_mode_regs(mdev, adjusted_mode);
|
2021-07-14 16:22:40 +02:00
|
|
|
|
|
|
|
|
pixpll->funcs->update(pixpll, &mgag200_crtc_state->pixpllc);
|
2020-05-15 10:32:32 +02:00
|
|
|
|
|
|
|
|
if (mdev->type == G200_ER)
|
|
|
|
|
mgag200_g200er_reset_tagfifo(mdev);
|
|
|
|
|
|
|
|
|
|
if (IS_G200_SE(mdev))
|
|
|
|
|
mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, fb);
|
|
|
|
|
else if (mdev->type == G200_EV)
|
|
|
|
|
mgag200_g200ev_set_hiprilvl(mdev);
|
|
|
|
|
|
2020-07-07 10:24:11 +02:00
|
|
|
if (mdev->type == G200_WB || mdev->type == G200_EW3)
|
|
|
|
|
mgag200_g200wb_release_bmc(mdev);
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
if (crtc_state->gamma_lut)
|
|
|
|
|
mgag200_crtc_set_gamma(mdev, fb->format, crtc_state->gamma_lut->data);
|
|
|
|
|
else
|
|
|
|
|
mgag200_crtc_set_gamma_linear(mdev, fb->format);
|
|
|
|
|
|
2020-07-07 10:24:11 +02:00
|
|
|
mgag200_enable_display(mdev);
|
2020-05-15 10:32:33 +02:00
|
|
|
|
2021-08-03 14:59:22 +02:00
|
|
|
mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]);
|
2022-05-02 16:25:14 +02:00
|
|
|
|
2022-05-04 15:40:25 +02:00
|
|
|
/* Always scanout image at VRAM offset 0 */
|
|
|
|
|
mgag200_set_startadd(mdev, (u32)0);
|
|
|
|
|
mgag200_set_offset(mdev, fb);
|
|
|
|
|
|
2022-05-02 16:25:14 +02:00
|
|
|
mutex_unlock(&mdev->rmmio_lock);
|
2020-05-15 10:32:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mgag200_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe)
|
|
|
|
|
{
|
|
|
|
|
struct drm_crtc *crtc = &pipe->crtc;
|
2020-07-07 10:24:08 +02:00
|
|
|
struct mga_device *mdev = to_mga_device(crtc->dev);
|
2020-05-15 10:32:32 +02:00
|
|
|
|
2020-07-07 10:24:08 +02:00
|
|
|
mgag200_disable_display(mdev);
|
2020-05-15 10:32:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
mgag200_simple_display_pipe_check(struct drm_simple_display_pipe *pipe,
|
|
|
|
|
struct drm_plane_state *plane_state,
|
|
|
|
|
struct drm_crtc_state *crtc_state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_plane *plane = plane_state->plane;
|
2021-07-14 16:22:40 +02:00
|
|
|
struct drm_device *dev = plane->dev;
|
|
|
|
|
struct mga_device *mdev = to_mga_device(dev);
|
|
|
|
|
struct mgag200_pll *pixpll = &mdev->pixpll;
|
|
|
|
|
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
|
2020-05-15 10:32:32 +02:00
|
|
|
struct drm_framebuffer *new_fb = plane_state->fb;
|
|
|
|
|
struct drm_framebuffer *fb = NULL;
|
2021-07-14 16:22:40 +02:00
|
|
|
int ret;
|
2020-05-15 10:32:32 +02:00
|
|
|
|
|
|
|
|
if (!new_fb)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (plane->state)
|
|
|
|
|
fb = plane->state->fb;
|
|
|
|
|
|
|
|
|
|
if (!fb || (fb->format != new_fb->format))
|
|
|
|
|
crtc_state->mode_changed = true; /* update PLL settings */
|
|
|
|
|
|
2021-07-14 16:22:40 +02:00
|
|
|
if (crtc_state->mode_changed) {
|
|
|
|
|
ret = pixpll->funcs->compute(pixpll, crtc_state->mode.clock,
|
|
|
|
|
&mgag200_crtc_state->pixpllc);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) {
|
|
|
|
|
if (crtc_state->gamma_lut->length !=
|
|
|
|
|
MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) {
|
|
|
|
|
drm_err(dev, "Wrong size for gamma_lut %zu\n",
|
|
|
|
|
crtc_state->gamma_lut->length);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-15 10:32:32 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
|
|
|
|
|
struct drm_plane_state *old_state)
|
|
|
|
|
{
|
|
|
|
|
struct drm_plane *plane = &pipe->plane;
|
2022-05-13 10:49:00 +02:00
|
|
|
struct drm_crtc *crtc = &pipe->crtc;
|
2020-05-15 10:32:32 +02:00
|
|
|
struct drm_device *dev = plane->dev;
|
|
|
|
|
struct mga_device *mdev = to_mga_device(dev);
|
|
|
|
|
struct drm_plane_state *state = plane->state;
|
2021-02-08 12:55:35 +01:00
|
|
|
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
|
2020-05-15 10:32:32 +02:00
|
|
|
struct drm_framebuffer *fb = state->fb;
|
2020-05-15 10:32:33 +02:00
|
|
|
struct drm_rect damage;
|
2022-05-04 15:40:25 +02:00
|
|
|
struct drm_atomic_helper_damage_iter iter;
|
2020-05-15 10:32:32 +02:00
|
|
|
|
|
|
|
|
if (!fb)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-05-02 16:25:14 +02:00
|
|
|
mutex_lock(&mdev->rmmio_lock);
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
if (crtc->state->color_mgmt_changed && crtc->state->gamma_lut)
|
|
|
|
|
mgag200_crtc_set_gamma(mdev, fb->format, crtc->state->gamma_lut->data);
|
|
|
|
|
|
2022-05-04 15:40:25 +02:00
|
|
|
drm_atomic_helper_damage_iter_init(&iter, old_state, state);
|
|
|
|
|
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
2021-08-03 14:59:22 +02:00
|
|
|
mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]);
|
2022-05-04 15:40:25 +02:00
|
|
|
}
|
|
|
|
|
/* Always scanout image at VRAM offset 0 */
|
|
|
|
|
mgag200_set_startadd(mdev, (u32)0);
|
|
|
|
|
mgag200_set_offset(mdev, fb);
|
2022-05-02 16:25:14 +02:00
|
|
|
|
|
|
|
|
mutex_unlock(&mdev->rmmio_lock);
|
2020-05-15 10:32:32 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-14 16:22:39 +02:00
|
|
|
static struct drm_crtc_state *
|
|
|
|
|
mgag200_simple_display_pipe_duplicate_crtc_state(struct drm_simple_display_pipe *pipe)
|
|
|
|
|
{
|
|
|
|
|
struct drm_crtc *crtc = &pipe->crtc;
|
|
|
|
|
struct drm_crtc_state *crtc_state = crtc->state;
|
2021-07-14 16:22:40 +02:00
|
|
|
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
|
2021-07-14 16:22:39 +02:00
|
|
|
struct mgag200_crtc_state *new_mgag200_crtc_state;
|
|
|
|
|
|
|
|
|
|
if (!crtc_state)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
new_mgag200_crtc_state = kzalloc(sizeof(*new_mgag200_crtc_state), GFP_KERNEL);
|
|
|
|
|
if (!new_mgag200_crtc_state)
|
|
|
|
|
return NULL;
|
|
|
|
|
__drm_atomic_helper_crtc_duplicate_state(crtc, &new_mgag200_crtc_state->base);
|
|
|
|
|
|
2021-07-14 16:22:40 +02:00
|
|
|
memcpy(&new_mgag200_crtc_state->pixpllc, &mgag200_crtc_state->pixpllc,
|
|
|
|
|
sizeof(new_mgag200_crtc_state->pixpllc));
|
|
|
|
|
|
2021-07-14 16:22:39 +02:00
|
|
|
return &new_mgag200_crtc_state->base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mgag200_simple_display_pipe_destroy_crtc_state(struct drm_simple_display_pipe *pipe,
|
|
|
|
|
struct drm_crtc_state *crtc_state)
|
|
|
|
|
{
|
|
|
|
|
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
|
|
|
|
|
|
|
|
|
|
__drm_atomic_helper_crtc_destroy_state(&mgag200_crtc_state->base);
|
|
|
|
|
kfree(mgag200_crtc_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mgag200_simple_display_pipe_reset_crtc(struct drm_simple_display_pipe *pipe)
|
|
|
|
|
{
|
|
|
|
|
struct drm_crtc *crtc = &pipe->crtc;
|
|
|
|
|
struct mgag200_crtc_state *mgag200_crtc_state;
|
|
|
|
|
|
|
|
|
|
if (crtc->state) {
|
|
|
|
|
mgag200_simple_display_pipe_destroy_crtc_state(pipe, crtc->state);
|
|
|
|
|
crtc->state = NULL; /* must be set to NULL here */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mgag200_crtc_state = kzalloc(sizeof(*mgag200_crtc_state), GFP_KERNEL);
|
|
|
|
|
if (!mgag200_crtc_state)
|
|
|
|
|
return;
|
|
|
|
|
__drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 10:32:32 +02:00
|
|
|
static const struct drm_simple_display_pipe_funcs
|
|
|
|
|
mgag200_simple_display_pipe_funcs = {
|
|
|
|
|
.mode_valid = mgag200_simple_display_pipe_mode_valid,
|
|
|
|
|
.enable = mgag200_simple_display_pipe_enable,
|
|
|
|
|
.disable = mgag200_simple_display_pipe_disable,
|
|
|
|
|
.check = mgag200_simple_display_pipe_check,
|
|
|
|
|
.update = mgag200_simple_display_pipe_update,
|
2021-07-14 16:22:39 +02:00
|
|
|
.reset_crtc = mgag200_simple_display_pipe_reset_crtc,
|
|
|
|
|
.duplicate_crtc_state = mgag200_simple_display_pipe_duplicate_crtc_state,
|
|
|
|
|
.destroy_crtc_state = mgag200_simple_display_pipe_destroy_crtc_state,
|
2021-02-08 12:55:35 +01:00
|
|
|
DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
|
2020-05-15 10:32:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const uint32_t mgag200_simple_display_pipe_formats[] = {
|
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
|
DRM_FORMAT_RGB888,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const uint64_t mgag200_simple_display_pipe_fmtmods[] = {
|
|
|
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
|
|
|
DRM_FORMAT_MOD_INVALID
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Mode config
|
|
|
|
|
*/
|
|
|
|
|
|
2022-05-16 15:43:43 +02:00
|
|
|
/* Calculates a mode's required memory bandwidth (in KiB/sec). */
|
|
|
|
|
static uint32_t mgag200_calculate_mode_bandwidth(const struct drm_display_mode *mode,
|
|
|
|
|
unsigned int bits_per_pixel)
|
|
|
|
|
{
|
|
|
|
|
uint32_t total_area, divisor;
|
|
|
|
|
uint64_t active_area, pixels_per_second, bandwidth;
|
|
|
|
|
uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
|
|
|
|
|
|
|
|
|
|
divisor = 1024;
|
|
|
|
|
|
|
|
|
|
if (!mode->htotal || !mode->vtotal || !mode->clock)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
active_area = mode->hdisplay * mode->vdisplay;
|
|
|
|
|
total_area = mode->htotal * mode->vtotal;
|
|
|
|
|
|
|
|
|
|
pixels_per_second = active_area * mode->clock * 1000;
|
|
|
|
|
do_div(pixels_per_second, total_area);
|
|
|
|
|
|
|
|
|
|
bandwidth = pixels_per_second * bytes_per_pixel * 100;
|
|
|
|
|
do_div(bandwidth, divisor);
|
|
|
|
|
|
|
|
|
|
return (uint32_t)bandwidth;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 15:43:42 +02:00
|
|
|
static enum drm_mode_status mgag200_mode_config_mode_valid(struct drm_device *dev,
|
|
|
|
|
const struct drm_display_mode *mode)
|
|
|
|
|
{
|
|
|
|
|
static const unsigned int max_bpp = 4; // DRM_FORMAT_XRGB8888
|
|
|
|
|
struct mga_device *mdev = to_mga_device(dev);
|
|
|
|
|
unsigned long fbsize, fbpages, max_fbpages;
|
2022-06-01 13:25:20 +02:00
|
|
|
const struct mgag200_device_info *info = mdev->info;
|
2022-05-16 15:43:42 +02:00
|
|
|
|
2022-06-01 13:25:16 +02:00
|
|
|
max_fbpages = mdev->vram_available >> PAGE_SHIFT;
|
2022-05-16 15:43:42 +02:00
|
|
|
|
|
|
|
|
fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
|
|
|
|
|
fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
|
|
|
|
|
|
|
|
|
|
if (fbpages > max_fbpages)
|
|
|
|
|
return MODE_MEM;
|
|
|
|
|
|
2022-06-01 13:25:20 +02:00
|
|
|
/*
|
|
|
|
|
* Test the mode's required memory bandwidth if the device
|
|
|
|
|
* specifies a maximum. Not all devices do though.
|
|
|
|
|
*/
|
|
|
|
|
if (info->max_mem_bandwidth) {
|
|
|
|
|
uint32_t mode_bandwidth = mgag200_calculate_mode_bandwidth(mode, max_bpp * 8);
|
|
|
|
|
|
|
|
|
|
if (mode_bandwidth > (info->max_mem_bandwidth * 1024))
|
2022-05-16 15:43:43 +02:00
|
|
|
return MODE_BAD;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 15:43:42 +02:00
|
|
|
return MODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-07 11:03:13 +02:00
|
|
|
static const struct drm_mode_config_funcs mgag200_mode_config_funcs = {
|
2022-05-16 15:43:42 +02:00
|
|
|
.fb_create = drm_gem_fb_create_with_dirty,
|
|
|
|
|
.mode_valid = mgag200_mode_config_mode_valid,
|
|
|
|
|
.atomic_check = drm_atomic_helper_check,
|
2020-05-15 10:32:32 +02:00
|
|
|
.atomic_commit = drm_atomic_helper_commit,
|
2020-05-07 11:03:13 +02:00
|
|
|
};
|
|
|
|
|
|
2022-06-01 13:25:16 +02:00
|
|
|
int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_available)
|
2012-04-17 15:01:25 +01:00
|
|
|
{
|
2020-06-05 15:58:02 +02:00
|
|
|
struct drm_device *dev = &mdev->base;
|
2022-05-16 15:43:41 +02:00
|
|
|
struct mga_i2c_chan *i2c = &mdev->i2c;
|
|
|
|
|
struct drm_connector *connector = &mdev->connector;
|
2020-05-15 10:32:32 +02:00
|
|
|
struct drm_simple_display_pipe *pipe = &mdev->display_pipe;
|
|
|
|
|
size_t format_count = ARRAY_SIZE(mgag200_simple_display_pipe_formats);
|
2020-02-28 09:18:27 +01:00
|
|
|
int ret;
|
2012-04-17 15:01:25 +01:00
|
|
|
|
2020-05-15 10:32:32 +02:00
|
|
|
mgag200_init_regs(mdev);
|
|
|
|
|
|
2022-06-01 13:25:16 +02:00
|
|
|
mdev->vram_available = vram_available;
|
|
|
|
|
|
2020-05-07 11:03:13 +02:00
|
|
|
ret = drmm_mode_config_init(dev);
|
|
|
|
|
if (ret) {
|
|
|
|
|
drm_err(dev, "drmm_mode_config_init() failed, error %d\n",
|
|
|
|
|
ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-07 11:03:11 +02:00
|
|
|
dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH;
|
|
|
|
|
dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT;
|
2022-06-01 13:25:13 +02:00
|
|
|
dev->mode_config.preferred_depth = 24;
|
2022-06-01 13:25:17 +02:00
|
|
|
dev->mode_config.fb_base = mdev->vram_res->start;
|
2020-05-07 11:03:13 +02:00
|
|
|
dev->mode_config.funcs = &mgag200_mode_config_funcs;
|
|
|
|
|
|
2022-05-16 15:43:41 +02:00
|
|
|
ret = mgag200_i2c_init(mdev, i2c);
|
2020-02-28 09:18:27 +01:00
|
|
|
if (ret) {
|
2022-05-16 15:43:41 +02:00
|
|
|
drm_err(dev, "failed to add DDC bus: %d\n", ret);
|
2020-02-28 09:18:27 +01:00
|
|
|
return ret;
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-16 15:43:41 +02:00
|
|
|
ret = drm_connector_init_with_ddc(dev, connector,
|
|
|
|
|
&mga_vga_connector_funcs,
|
|
|
|
|
DRM_MODE_CONNECTOR_VGA,
|
|
|
|
|
&i2c->adapter);
|
|
|
|
|
if (ret) {
|
|
|
|
|
drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
|
|
|
|
|
|
2021-07-14 16:22:37 +02:00
|
|
|
ret = mgag200_pixpll_init(&mdev->pixpll, mdev);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
2020-05-15 10:32:32 +02:00
|
|
|
ret = drm_simple_display_pipe_init(dev, pipe,
|
|
|
|
|
&mgag200_simple_display_pipe_funcs,
|
|
|
|
|
mgag200_simple_display_pipe_formats,
|
|
|
|
|
format_count,
|
|
|
|
|
mgag200_simple_display_pipe_fmtmods,
|
|
|
|
|
connector);
|
2020-05-07 11:03:15 +02:00
|
|
|
if (ret) {
|
|
|
|
|
drm_err(dev,
|
2020-05-15 10:32:32 +02:00
|
|
|
"drm_simple_display_pipe_init() failed, error %d\n",
|
2020-05-07 11:03:15 +02:00
|
|
|
ret);
|
|
|
|
|
return ret;
|
2012-04-17 15:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-04 15:40:24 +02:00
|
|
|
drm_plane_enable_fb_damage_clips(&pipe->plane);
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
|
2020-05-15 10:32:32 +02:00
|
|
|
drm_mode_crtc_set_gamma_size(&pipe->crtc, MGAG200_LUT_SIZE);
|
|
|
|
|
|
2022-05-13 10:49:00 +02:00
|
|
|
drm_crtc_enable_color_mgmt(&pipe->crtc, 0, false, MGAG200_LUT_SIZE);
|
|
|
|
|
|
2020-05-15 10:32:32 +02:00
|
|
|
drm_mode_config_reset(dev);
|
2012-04-17 15:01:25 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|