Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_MGAG200
tristate "Matrox G200"
depends on DRM && PCI
select DRM_CLIENT_SELECTION
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
select I2C
select I2C_ALGOBIT
help
This is a KMS driver for Matrox G200 chips. It supports the original
MGA G200 desktop chips and the server variants. It requires 0.3.0
of the modesetting userspace driver, and a version of mga driver
that will fail on KMS enabled devices.
config DRM_MGAG200_DISABLE_WRITECOMBINE
bool "Disable Write Combine mapping of VRAM"
depends on DRM_MGAG200 && PREEMPT_RT
help
The VRAM of the G200 is mapped with Write-Combine to improve
performances. This can interfere with real-time tasks; even if they
are running on other CPU cores than the graphics output.
Enable this option only if you run realtime tasks on a server with a
Matrox G200.
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
mgag200-y := \
mgag200_bmc.o \
mgag200_ddc.o \
mgag200_drv.o \
mgag200_g200.o \
mgag200_g200eh.o \
mgag200_g200eh3.o \
mgag200_g200eh5.o \
mgag200_g200er.o \
mgag200_g200ev.o \
mgag200_g200ew3.o \
mgag200_g200se.o \
mgag200_g200wb.o \
mgag200_mode.o \
mgag200_vga_bmc.o \
mgag200_vga.o
obj-$(CONFIG_DRM_MGAG200) += mgag200.o
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
void mgag200_bmc_stop_scanout(struct mga_device *mdev)
{
u8 tmp;
int ret;
/*
* 1 - The first step is to inform 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 any further scan request. This is
* 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 verify if there is an active scan.
* We are waiting for a 0 on remhsyncsts (<XSPAREREG<0>).
*/
ret = read_poll_timeout(RREG_DAC, tmp, !(tmp & 0x1),
1000, 300000, false,
MGA1064_SPAREREG);
if (ret == -ETIMEDOUT)
return;
/*
* 3b- This step occurs only if the remote BMC is actually
* scanning. We are waiting for the end of the frame which is
* a 1 on remvsyncsts (XSPAREREG<1>)
*/
(void)read_poll_timeout(RREG_DAC, tmp, (tmp & 0x2),
1000, 300000, false,
MGA1064_SPAREREG);
}
void mgag200_bmc_start_scanout(struct mga_device *mdev)
{
u8 tmp;
/* Assert rstlvl2 */
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
tmp = RREG8(DAC_DATA);
tmp |= 0x8;
WREG8(DAC_DATA, tmp);
udelay(10);
/* Deassert rstlvl2 */
tmp &= ~0x08;
WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
WREG8(DAC_DATA, tmp);
/* Remove mask of scan request */
WREG8(DAC_INDEX, MGA1064_SPAREREG);
tmp = RREG8(DAC_DATA);
tmp &= ~0x80;
WREG8(DAC_DATA, tmp);
/* 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);
}
@@ -0,0 +1,178 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*/
/*
* Authors: Dave Airlie <airlied@redhat.com>
*/
#include <linux/i2c-algo-bit.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <drm/drm_managed.h>
#include "mgag200_ddc.h"
#include "mgag200_drv.h"
struct mgag200_ddc {
struct mga_device *mdev;
int data;
int clock;
struct i2c_algo_bit_data bit;
struct i2c_adapter adapter;
};
static int mga_i2c_read_gpio(struct mga_device *mdev)
{
WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
return RREG8(DAC_DATA);
}
static void mga_i2c_set_gpio(struct mga_device *mdev, int mask, int val)
{
int tmp;
WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
tmp = (RREG8(DAC_DATA) & mask) | val;
WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
WREG_DAC(MGA1064_GEN_IO_DATA, 0);
}
static inline void mga_i2c_set(struct mga_device *mdev, int mask, int state)
{
if (state)
state = 0;
else
state = mask;
mga_i2c_set_gpio(mdev, ~mask, state);
}
static void mgag200_ddc_algo_bit_data_setsda(void *data, int state)
{
struct mgag200_ddc *ddc = data;
mga_i2c_set(ddc->mdev, ddc->data, state);
}
static void mgag200_ddc_algo_bit_data_setscl(void *data, int state)
{
struct mgag200_ddc *ddc = data;
mga_i2c_set(ddc->mdev, ddc->clock, state);
}
static int mgag200_ddc_algo_bit_data_getsda(void *data)
{
struct mgag200_ddc *ddc = data;
return (mga_i2c_read_gpio(ddc->mdev) & ddc->data) ? 1 : 0;
}
static int mgag200_ddc_algo_bit_data_getscl(void *data)
{
struct mgag200_ddc *ddc = data;
return (mga_i2c_read_gpio(ddc->mdev) & ddc->clock) ? 1 : 0;
}
static int mgag200_ddc_algo_bit_data_pre_xfer(struct i2c_adapter *adapter)
{
struct mgag200_ddc *ddc = i2c_get_adapdata(adapter);
struct mga_device *mdev = ddc->mdev;
/*
* Protect access to I/O registers from concurrent modesetting
* by acquiring the I/O-register lock.
*/
mutex_lock(&mdev->rmmio_lock);
return 0;
}
static void mgag200_ddc_algo_bit_data_post_xfer(struct i2c_adapter *adapter)
{
struct mgag200_ddc *ddc = i2c_get_adapdata(adapter);
struct mga_device *mdev = ddc->mdev;
mutex_unlock(&mdev->rmmio_lock);
}
static void mgag200_ddc_release(struct drm_device *dev, void *res)
{
struct mgag200_ddc *ddc = res;
i2c_del_adapter(&ddc->adapter);
}
struct i2c_adapter *mgag200_ddc_create(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
const struct mgag200_device_info *info = mdev->info;
struct mgag200_ddc *ddc;
struct i2c_algo_bit_data *bit;
struct i2c_adapter *adapter;
int ret;
ddc = drmm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
if (!ddc)
return ERR_PTR(-ENOMEM);
WREG_DAC(MGA1064_GEN_IO_CTL2, 1);
WREG_DAC(MGA1064_GEN_IO_DATA, 0xff);
WREG_DAC(MGA1064_GEN_IO_CTL, 0);
ddc->mdev = mdev;
ddc->data = BIT(info->i2c.data_bit);
ddc->clock = BIT(info->i2c.clock_bit);
bit = &ddc->bit;
bit->data = ddc;
bit->setsda = mgag200_ddc_algo_bit_data_setsda;
bit->setscl = mgag200_ddc_algo_bit_data_setscl;
bit->getsda = mgag200_ddc_algo_bit_data_getsda;
bit->getscl = mgag200_ddc_algo_bit_data_getscl;
bit->pre_xfer = mgag200_ddc_algo_bit_data_pre_xfer;
bit->post_xfer = mgag200_ddc_algo_bit_data_post_xfer;
bit->udelay = 10;
bit->timeout = usecs_to_jiffies(2200);
adapter = &ddc->adapter;
adapter->owner = THIS_MODULE;
adapter->algo_data = bit;
adapter->dev.parent = dev->dev;
snprintf(adapter->name, sizeof(adapter->name), "Matrox DDC bus");
i2c_set_adapdata(adapter, ddc);
ret = i2c_bit_add_bus(adapter);
if (ret)
return ERR_PTR(ret);
ret = drmm_add_action_or_reset(dev, mgag200_ddc_release, ddc);
if (ret)
return ERR_PTR(ret);
return adapter;
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __MGAG200_DDC_H__
#define __MGAG200_DDC_H__
struct i2c_adapter;
struct mga_device;
struct i2c_adapter *mgag200_ddc_create(struct mga_device *mdev);
#endif
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2012 Red Hat
*
* Authors: Matthew Garrett
* Dave Airlie
*/
#include <linux/aperture.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
#include <drm/drm_print.h>
#include "mgag200_drv.h"
static int mgag200_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, mgag200_modeset, int, 0400);
int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2)
{
struct device *dev = &pdev->dev;
int err;
err = pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
if (err != PCIBIOS_SUCCESSFUL) {
dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
return pcibios_err_to_errno(err);
}
err = pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2);
if (err != PCIBIOS_SUCCESSFUL) {
dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION2) failed: %d\n", err);
return pcibios_err_to_errno(err);
}
return 0;
}
resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size)
{
int offset;
int orig;
int test1, test2;
int orig1, orig2;
size_t vram_size;
/* Probe */
orig = ioread16(mem);
iowrite16(0, mem);
vram_size = size;
for (offset = 0x100000; offset < vram_size; offset += 0x4000) {
orig1 = ioread8(mem + offset);
orig2 = ioread8(mem + offset + 0x100);
iowrite16(0xaa55, mem + offset);
iowrite16(0xaa55, mem + offset + 0x100);
test1 = ioread16(mem + offset);
test2 = ioread16(mem);
iowrite16(orig1, mem + offset);
iowrite16(orig2, mem + offset + 0x100);
if (test1 != 0xaa55)
break;
if (test2)
break;
}
iowrite16(orig, mem);
return offset - 65536;
}
/*
* DRM driver
*/
DEFINE_DRM_GEM_FOPS(mgag200_driver_fops);
static const struct drm_driver mgag200_driver = {
.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
.fops = &mgag200_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
DRM_GEM_SHMEM_DRIVER_OPS,
DRM_FBDEV_SHMEM_DRIVER_OPS,
};
/*
* DRM device
*/
resource_size_t mgag200_device_probe_vram(struct mga_device *mdev)
{
return mgag200_probe_vram(mdev->vram, resource_size(mdev->vram_res));
}
int mgag200_device_preinit(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct pci_dev *pdev = to_pci_dev(dev->dev);
resource_size_t start, len;
struct resource *res;
/* BAR 1 contains registers */
start = pci_resource_start(pdev, 1);
len = pci_resource_len(pdev, 1);
res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_mmio");
if (!res) {
drm_err(dev, "devm_request_mem_region(MMIO) failed\n");
return -ENXIO;
}
mdev->rmmio_res = res;
mdev->rmmio = pcim_iomap(pdev, 1, 0);
if (!mdev->rmmio)
return -ENOMEM;
/* BAR 0 is VRAM */
start = pci_resource_start(pdev, 0);
len = pci_resource_len(pdev, 0);
res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram");
if (!res) {
drm_err(dev, "devm_request_mem_region(VRAM) failed\n");
return -ENXIO;
}
mdev->vram_res = res;
#if defined(CONFIG_DRM_MGAG200_DISABLE_WRITECOMBINE)
mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res));
if (!mdev->vram)
return -ENOMEM;
#else
mdev->vram = devm_ioremap_wc(dev->dev, res->start, resource_size(res));
if (!mdev->vram)
return -ENOMEM;
/* Don't fail on errors, but performance might be reduced. */
devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res));
#endif
return 0;
}
int mgag200_device_init(struct mga_device *mdev,
const struct mgag200_device_info *info,
const struct mgag200_device_funcs *funcs)
{
struct drm_device *dev = &mdev->base;
u8 crtcext3, misc;
int ret;
mdev->info = info;
mdev->funcs = funcs;
ret = drmm_mutex_init(dev, &mdev->rmmio_lock);
if (ret)
return ret;
mutex_lock(&mdev->rmmio_lock);
RREG_ECRT(0x03, crtcext3);
crtcext3 |= MGAREG_CRTCEXT3_MGAMODE;
WREG_ECRT(0x03, crtcext3);
WREG_ECRT(0x04, 0x00);
misc = RREG8(MGA_MISC_IN);
misc |= MGAREG_MISC_RAMMAPEN |
MGAREG_MISC_HIGH_PG_SEL;
WREG8(MGA_MISC_OUT, misc);
mutex_unlock(&mdev->rmmio_lock);
WREG32(MGAREG_IEN, 0);
return 0;
}
/*
* PCI driver
*/
static const struct pci_device_id mgag200_pciidlist[] = {
{ PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI },
{ PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP },
{ PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
{ PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
{ PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB },
{ PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH },
{ PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER },
{ PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 },
{ PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 },
{ PCI_VENDOR_ID_MATROX, 0x53a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH5 },
{0,}
};
MODULE_DEVICE_TABLE(pci, mgag200_pciidlist);
static int
mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
enum mga_type type = (enum mga_type)ent->driver_data;
struct mga_device *mdev;
struct drm_device *dev;
int ret;
ret = aperture_remove_conflicting_pci_devices(pdev, mgag200_driver.name);
if (ret)
return ret;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
switch (type) {
case G200_PCI:
case G200_AGP:
mdev = mgag200_g200_device_create(pdev, &mgag200_driver);
break;
case G200_SE_A:
case G200_SE_B:
mdev = mgag200_g200se_device_create(pdev, &mgag200_driver, type);
break;
case G200_WB:
mdev = mgag200_g200wb_device_create(pdev, &mgag200_driver);
break;
case G200_EV:
mdev = mgag200_g200ev_device_create(pdev, &mgag200_driver);
break;
case G200_EH:
mdev = mgag200_g200eh_device_create(pdev, &mgag200_driver);
break;
case G200_EH3:
mdev = mgag200_g200eh3_device_create(pdev, &mgag200_driver);
break;
case G200_EH5:
mdev = mgag200_g200eh5_device_create(pdev, &mgag200_driver);
break;
case G200_ER:
mdev = mgag200_g200er_device_create(pdev, &mgag200_driver);
break;
case G200_EW3:
mdev = mgag200_g200ew3_device_create(pdev, &mgag200_driver);
break;
default:
dev_err(&pdev->dev, "Device type %d is unsupported\n", type);
return -ENODEV;
}
if (IS_ERR(mdev))
return PTR_ERR(mdev);
dev = &mdev->base;
ret = drm_dev_register(dev, 0);
if (ret)
return ret;
/*
* FIXME: A 24-bit color depth does not work with 24 bpp on
* G200ER. Force 32 bpp.
*/
drm_client_setup_with_fourcc(dev, DRM_FORMAT_XRGB8888);
return 0;
}
static void mgag200_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_dev_unregister(dev);
drm_atomic_helper_shutdown(dev);
}
static void mgag200_pci_shutdown(struct pci_dev *pdev)
{
drm_atomic_helper_shutdown(pci_get_drvdata(pdev));
}
static struct pci_driver mgag200_pci_driver = {
.name = DRIVER_NAME,
.id_table = mgag200_pciidlist,
.probe = mgag200_pci_probe,
.remove = mgag200_pci_remove,
.shutdown = mgag200_pci_shutdown,
};
drm_module_pci_driver_if_modeset(mgag200_pci_driver, mgag200_modeset);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
@@ -0,0 +1,439 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2010 Matt Turner.
* Copyright 2012 Red Hat
*
* Authors: Matthew Garrett
* Matt Turner
* Dave Airlie
*/
#ifndef __MGAG200_DRV_H__
#define __MGAG200_DRV_H__
#include <video/vga.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_encoder.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_plane.h>
#include "mgag200_reg.h"
#define DRIVER_AUTHOR "Matthew Garrett"
#define DRIVER_NAME "mgag200"
#define DRIVER_DESC "MGA G200 SE"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0
#define RREG8(reg) ioread8(((void __iomem *)mdev->rmmio) + (reg))
#define WREG8(reg, v) iowrite8(v, ((void __iomem *)mdev->rmmio) + (reg))
#define RREG32(reg) ioread32(((void __iomem *)mdev->rmmio) + (reg))
#define WREG32(reg, v) iowrite32(v, ((void __iomem *)mdev->rmmio) + (reg))
#define MGA_BIOS_OFFSET 0x7ffc
#define ATTR_INDEX 0x1fc0
#define ATTR_DATA 0x1fc1
#define WREG_MISC(v) \
WREG8(MGA_MISC_OUT, v)
#define RREG_MISC(v) \
((v) = RREG8(MGA_MISC_IN))
#define WREG_MISC_MASKED(v, mask) \
do { \
u8 misc_; \
u8 mask_ = (mask); \
RREG_MISC(misc_); \
misc_ &= ~mask_; \
misc_ |= ((v) & mask_); \
WREG_MISC(misc_); \
} while (0)
#define WREG_ATTR(reg, v) \
do { \
RREG8(0x1fda); \
WREG8(ATTR_INDEX, reg); \
WREG8(ATTR_DATA, v); \
} while (0) \
#define RREG_SEQ(reg, v) \
do { \
WREG8(MGAREG_SEQ_INDEX, reg); \
v = RREG8(MGAREG_SEQ_DATA); \
} while (0) \
#define WREG_SEQ(reg, v) \
do { \
WREG8(MGAREG_SEQ_INDEX, reg); \
WREG8(MGAREG_SEQ_DATA, v); \
} while (0) \
#define RREG_CRT(reg, v) \
do { \
WREG8(MGAREG_CRTC_INDEX, reg); \
v = RREG8(MGAREG_CRTC_DATA); \
} while (0) \
#define WREG_CRT(reg, v) \
do { \
WREG8(MGAREG_CRTC_INDEX, reg); \
WREG8(MGAREG_CRTC_DATA, v); \
} while (0) \
#define RREG_ECRT(reg, v) \
do { \
WREG8(MGAREG_CRTCEXT_INDEX, reg); \
v = RREG8(MGAREG_CRTCEXT_DATA); \
} while (0) \
#define WREG_ECRT(reg, v) \
do { \
WREG8(MGAREG_CRTCEXT_INDEX, reg); \
WREG8(MGAREG_CRTCEXT_DATA, v); \
} while (0) \
#define GFX_INDEX 0x1fce
#define GFX_DATA 0x1fcf
#define WREG_GFX(reg, v) \
do { \
WREG8(GFX_INDEX, reg); \
WREG8(GFX_DATA, v); \
} while (0) \
#define DAC_INDEX 0x3c00
#define DAC_DATA 0x3c0a
#define RREG_DAC(reg) \
({ \
WREG8(DAC_INDEX, reg); \
RREG8(DAC_DATA); \
}) \
#define WREG_DAC(reg, v) \
do { \
WREG8(DAC_INDEX, reg); \
WREG8(DAC_DATA, v); \
} while (0) \
#define MGA_MISC_OUT 0x1fc2
#define MGA_MISC_IN 0x1fcc
/*
* TODO: This is a pretty large set of default values for all kinds of
* settings. It should be split and set in the various DRM helpers,
* such as the CRTC reset or atomic_enable helpers. The PLL values
* probably belong to each model's PLL code.
*/
#define MGAG200_DAC_DEFAULT(xvrefctrl, xpixclkctrl, xmiscctrl, xsyspllm, xsysplln, xsyspllp) \
/* 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: */ (xvrefctrl), \
/* 0x19: */ 0, \
/* 0x1a: */ (xpixclkctrl), \
/* 0x1b: */ 0xff, 0xbf, 0x20, \
/* 0x1e: */ (xmiscctrl), \
/* 0x1f: */ 0x20, \
/* 0x20: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
/* 0x28: */ 0x00, 0x00, 0x00, 0x00, \
/* 0x2c: */ (xsyspllm), \
/* 0x2d: */ (xsysplln), \
/* 0x2e: */ (xsyspllp), \
/* 0x2f: */ 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 \
#define MGAG200_LUT_SIZE 256
#define MGAG200_MAX_FB_HEIGHT 4096
#define MGAG200_MAX_FB_WIDTH 4096
struct mga_device;
/*
* Stores parameters for programming the PLLs
*
* Fref: reference frequency (A: 25.175 Mhz, B: 28.361, C: XX Mhz)
* Fo: output frequency
* Fvco = Fref * (N / M)
* Fo = Fvco / P
*
* S = [0..3]
*/
struct mgag200_pll_values {
unsigned int m;
unsigned int n;
unsigned int p;
unsigned int s;
};
struct mgag200_crtc_state {
struct drm_crtc_state base;
/* Primary-plane format; required for modesetting and color mgmt. */
const struct drm_format_info *format;
struct mgag200_pll_values pixpllc;
bool set_vidrst;
};
static inline struct mgag200_crtc_state *to_mgag200_crtc_state(struct drm_crtc_state *base)
{
return container_of(base, struct mgag200_crtc_state, base);
}
enum mga_type {
G200_PCI,
G200_AGP,
G200_SE_A,
G200_SE_B,
G200_WB,
G200_EV,
G200_EH,
G200_EH3,
G200_EH5,
G200_ER,
G200_EW3,
};
struct mgag200_device_info {
u16 max_hdisplay;
u16 max_vdisplay;
/*
* Maximum memory bandwidth (MiB/sec). Setting this to zero disables
* the rsp test during mode validation.
*/
unsigned long max_mem_bandwidth;
/* Synchronize scanout with BMC */
bool sync_bmc:1;
struct {
unsigned data_bit:3;
unsigned clock_bit:3;
} i2c;
/*
* HW does not handle 'startadd' register correctly. Always set
* it's value to 0.
*/
bool bug_no_startadd:1;
};
#define MGAG200_DEVICE_INFO_INIT(_max_hdisplay, _max_vdisplay, _max_mem_bandwidth, \
_sync_bmc, _i2c_data_bit, _i2c_clock_bit, \
_bug_no_startadd) \
{ \
.max_hdisplay = (_max_hdisplay), \
.max_vdisplay = (_max_vdisplay), \
.max_mem_bandwidth = (_max_mem_bandwidth), \
.sync_bmc = (_sync_bmc), \
.i2c = { \
.data_bit = (_i2c_data_bit), \
.clock_bit = (_i2c_clock_bit), \
}, \
.bug_no_startadd = (_bug_no_startadd), \
}
struct mgag200_device_funcs {
/*
* Validate that the given state can be programmed into PIXPLLC. On
* success, the calculated parameters should be stored in the CRTC's
* state in struct @mgag200_crtc_state.pixpllc.
*/
int (*pixpllc_atomic_check)(struct drm_crtc *crtc, struct drm_atomic_commit *new_state);
/*
* Program PIXPLLC from the CRTC state. The parameters should have been
* stored in struct @mgag200_crtc_state.pixpllc by the corresponding
* implementation of @pixpllc_atomic_check.
*/
void (*pixpllc_atomic_update)(struct drm_crtc *crtc, struct drm_atomic_commit *old_state);
};
struct mga_device {
struct drm_device base;
const struct mgag200_device_info *info;
const struct mgag200_device_funcs *funcs;
struct resource *rmmio_res;
void __iomem *rmmio;
struct mutex rmmio_lock; /* Protects access to rmmio */
struct resource *vram_res;
void __iomem *vram;
resource_size_t vram_available;
struct drm_plane primary_plane;
struct drm_crtc crtc;
struct {
struct {
struct drm_encoder encoder;
struct drm_connector connector;
} vga;
} output;
};
static inline struct mga_device *to_mga_device(struct drm_device *dev)
{
return container_of(dev, struct mga_device, base);
}
struct mgag200_g200_device {
struct mga_device base;
/* PLL constants */
long ref_clk;
long pclk_min;
long pclk_max;
};
static inline struct mgag200_g200_device *to_mgag200_g200_device(struct drm_device *dev)
{
return container_of(to_mga_device(dev), struct mgag200_g200_device, base);
}
struct mgag200_g200se_device {
struct mga_device base;
/* SE model number stored in reg 0x1e24 */
u32 unique_rev_id;
};
static inline struct mgag200_g200se_device *to_mgag200_g200se_device(struct drm_device *dev)
{
return container_of(to_mga_device(dev), struct mgag200_g200se_device, base);
}
/* mgag200_drv.c */
int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2);
resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size);
resource_size_t mgag200_device_probe_vram(struct mga_device *mdev);
int mgag200_device_preinit(struct mga_device *mdev);
int mgag200_device_init(struct mga_device *mdev,
const struct mgag200_device_info *info,
const struct mgag200_device_funcs *funcs);
/* mgag200_<device type>.c */
struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv);
struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
enum mga_type type);
void mgag200_g200wb_init_registers(struct mga_device *mdev);
void mgag200_g200wb_pixpllc_atomic_update(struct drm_crtc *crtc, struct drm_atomic_commit *old_state);
struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv);
struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv);
void mgag200_g200eh_init_registers(struct mga_device *mdev);
void mgag200_g200eh_pixpllc_atomic_update(struct drm_crtc *crtc, struct drm_atomic_commit *old_state);
struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev,
const struct drm_driver *drv);
struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev,
const struct drm_driver *drv);
struct mga_device *mgag200_g200eh5_device_create(struct pci_dev *pdev,
const struct drm_driver *drv);
struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev,
const struct drm_driver *drv);
struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev,
const struct drm_driver *drv);
/*
* mgag200_mode.c
*/
struct drm_crtc;
struct drm_crtc_state;
struct drm_display_mode;
struct drm_plane;
struct drm_atomic_commit;
struct drm_scanout_buffer;
extern const uint32_t mgag200_primary_plane_formats[];
extern const size_t mgag200_primary_plane_formats_size;
extern const uint64_t mgag200_primary_plane_fmtmods[];
int mgag200_primary_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *new_state);
void mgag200_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *old_state);
void mgag200_primary_plane_helper_atomic_enable(struct drm_plane *plane,
struct drm_atomic_commit *state);
void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane,
struct drm_atomic_commit *old_state);
int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
struct drm_scanout_buffer *sb);
#define MGAG200_PRIMARY_PLANE_HELPER_FUNCS \
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, \
.atomic_check = mgag200_primary_plane_helper_atomic_check, \
.atomic_update = mgag200_primary_plane_helper_atomic_update, \
.atomic_enable = mgag200_primary_plane_helper_atomic_enable, \
.atomic_disable = mgag200_primary_plane_helper_atomic_disable, \
.get_scanout_buffer = mgag200_primary_plane_helper_get_scanout_buffer
#define MGAG200_PRIMARY_PLANE_FUNCS \
.update_plane = drm_atomic_helper_update_plane, \
.disable_plane = drm_atomic_helper_disable_plane, \
.destroy = drm_plane_cleanup, \
DRM_GEM_SHADOW_PLANE_FUNCS
void mgag200_crtc_fill_gamma(struct mga_device *mdev, const struct drm_format_info *format);
void mgag200_crtc_load_gamma(struct mga_device *mdev,
const struct drm_format_info *format,
struct drm_color_lut *lut);
enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode);
int mgag200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *new_state);
void mgag200_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *old_state);
void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *old_state);
void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *old_state);
#define MGAG200_CRTC_HELPER_FUNCS \
.mode_valid = mgag200_crtc_helper_mode_valid, \
.atomic_check = mgag200_crtc_helper_atomic_check, \
.atomic_flush = mgag200_crtc_helper_atomic_flush, \
.atomic_enable = mgag200_crtc_helper_atomic_enable, \
.atomic_disable = mgag200_crtc_helper_atomic_disable
void mgag200_crtc_reset(struct drm_crtc *crtc);
struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc);
void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state);
#define MGAG200_CRTC_FUNCS \
.reset = mgag200_crtc_reset, \
.destroy = drm_crtc_cleanup, \
.set_config = drm_atomic_helper_set_config, \
.page_flip = drm_atomic_helper_page_flip, \
.atomic_duplicate_state = mgag200_crtc_atomic_duplicate_state, \
.atomic_destroy_state = mgag200_crtc_atomic_destroy_state
void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode,
bool set_vidrst);
void mgag200_set_format_regs(struct mga_device *mdev, const struct drm_format_info *format);
void mgag200_enable_display(struct mga_device *mdev);
void mgag200_init_registers(struct mga_device *mdev);
int mgag200_mode_config_init(struct mga_device *mdev, resource_size_t vram_available);
/* mgag200_vga_bmc.c */
int mgag200_vga_bmc_output_init(struct mga_device *mdev);
/* mgag200_vga.c */
int mgag200_vga_output_init(struct mga_device *mdev);
/* mgag200_bmc.c */
void mgag200_bmc_stop_scanout(struct mga_device *mdev);
void mgag200_bmc_start_scanout(struct mga_device *mdev);
#endif /* __MGAG200_DRV_H__ */
@@ -0,0 +1,408 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
static int mgag200_g200_init_pci_options(struct pci_dev *pdev)
{
struct device *dev = &pdev->dev;
bool has_sgram;
u32 option;
int err;
err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
if (err != PCIBIOS_SUCCESSFUL) {
dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
return pcibios_err_to_errno(err);
}
has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK);
if (has_sgram)
option = 0x4049cd21;
else
option = 0x40499121;
return mgag200_init_pci_options(pdev, option, 0x00008000);
}
static void mgag200_g200_init_registers(struct mgag200_g200_device *g200)
{
static const u8 dacvalue[] = {
MGAG200_DAC_DEFAULT(0x00, 0xc9, 0x1f,
0x04, 0x2d, 0x19)
};
struct mga_device *mdev = &g200->base;
size_t i;
for (i = 0; i < ARRAY_SIZE(dacvalue); ++i) {
if ((i <= 0x17) ||
(i == 0x1b) ||
(i == 0x1c) ||
((i >= 0x1f) && (i <= 0x29)) ||
((i >= 0x30) && (i <= 0x37)))
continue;
WREG_DAC(i, dacvalue[i]);
}
mgag200_init_registers(mdev);
}
/*
* PIXPLLC
*/
static int mgag200_g200_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *new_state)
{
static const int post_div_max = 7;
static const int in_div_min = 1;
static const int in_div_max = 6;
static const int feed_div_min = 7;
static const int feed_div_max = 127;
struct drm_device *dev = crtc->dev;
struct mgag200_g200_device *g200 = to_mgag200_g200_device(dev);
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
u8 testp, testm, testn;
u8 n = 0, m = 0, p, s;
long f_vco;
long computed;
long delta, tmp_delta;
long ref_clk = g200->ref_clk;
long p_clk_min = g200->pclk_min;
long p_clk_max = g200->pclk_max;
if (clock > p_clk_max) {
drm_err(dev, "Pixel Clock %ld too high\n", clock);
return -EINVAL;
}
if (clock < p_clk_min >> 3)
clock = p_clk_min >> 3;
f_vco = clock;
for (testp = 0;
testp <= post_div_max && f_vco < p_clk_min;
testp = (testp << 1) + 1, f_vco <<= 1)
;
p = testp + 1;
delta = clock;
for (testm = in_div_min; testm <= in_div_max; testm++) {
for (testn = feed_div_min; testn <= feed_div_max; testn++) {
computed = ref_clk * (testn + 1) / (testm + 1);
if (computed < f_vco)
tmp_delta = f_vco - computed;
else
tmp_delta = computed - f_vco;
if (tmp_delta < delta) {
delta = tmp_delta;
m = testm + 1;
n = testn + 1;
}
}
}
f_vco = ref_clk * n / m;
if (f_vco < 100000)
s = 0;
else if (f_vco < 140000)
s = 1;
else if (f_vco < 180000)
s = 2;
else
s = 3;
drm_dbg_kms(dev, "clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n",
clock, f_vco, m, n, p, s);
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_g200_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static const struct drm_crtc_helper_funcs mgag200_g200_crtc_helper_funcs = {
MGAG200_CRTC_HELPER_FUNCS,
};
static const struct drm_crtc_funcs mgag200_g200_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM Device
*/
static const struct mgag200_device_info mgag200_g200_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 3, false);
static void mgag200_g200_interpret_bios(struct mgag200_g200_device *g200,
const unsigned char *bios, size_t size)
{
static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'};
static const unsigned int expected_length[6] = {
0, 64, 64, 64, 128, 128
};
struct mga_device *mdev = &g200->base;
struct drm_device *dev = &mdev->base;
const unsigned char *pins;
unsigned int pins_len, version;
int offset;
int tmp;
/* Test for MATROX string. */
if (size < 45 + sizeof(matrox))
return;
if (memcmp(&bios[45], matrox, sizeof(matrox)) != 0)
return;
/* Get the PInS offset. */
if (size < MGA_BIOS_OFFSET + 2)
return;
offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET];
/* Get PInS data structure. */
if (size < offset + 6)
return;
pins = bios + offset;
if (pins[0] == 0x2e && pins[1] == 0x41) {
version = pins[5];
pins_len = pins[2];
} else {
version = 1;
pins_len = pins[0] + (pins[1] << 8);
}
if (version < 1 || version > 5) {
drm_warn(dev, "Unknown BIOS PInS version: %d\n", version);
return;
}
if (pins_len != expected_length[version]) {
drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n",
pins_len, expected_length[version]);
return;
}
if (size < offset + pins_len)
return;
drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n", version, pins_len);
/* Extract the clock values */
switch (version) {
case 1:
tmp = pins[24] + (pins[25] << 8);
if (tmp)
g200->pclk_max = tmp * 10;
break;
case 2:
if (pins[41] != 0xff)
g200->pclk_max = (pins[41] + 100) * 1000;
break;
case 3:
if (pins[36] != 0xff)
g200->pclk_max = (pins[36] + 100) * 1000;
if (pins[52] & 0x20)
g200->ref_clk = 14318;
break;
case 4:
if (pins[39] != 0xff)
g200->pclk_max = pins[39] * 4 * 1000;
if (pins[92] & 0x01)
g200->ref_clk = 14318;
break;
case 5:
tmp = pins[4] ? 8000 : 6000;
if (pins[123] != 0xff)
g200->pclk_min = pins[123] * tmp;
if (pins[38] != 0xff)
g200->pclk_max = pins[38] * tmp;
if (pins[110] & 0x01)
g200->ref_clk = 14318;
break;
default:
break;
}
}
static void mgag200_g200_init_refclk(struct mgag200_g200_device *g200)
{
struct mga_device *mdev = &g200->base;
struct drm_device *dev = &mdev->base;
struct pci_dev *pdev = to_pci_dev(dev->dev);
unsigned char __iomem *rom;
unsigned char *bios;
size_t size;
g200->pclk_min = 50000;
g200->pclk_max = 230000;
g200->ref_clk = 27050;
rom = pci_map_rom(pdev, &size);
if (!rom)
return;
bios = vmalloc(size);
if (!bios)
goto out;
memcpy_fromio(bios, rom, size);
if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa)
mgag200_g200_interpret_bios(g200, bios, size);
drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n",
g200->pclk_min, g200->pclk_max, g200->ref_clk);
vfree(bios);
out:
pci_unmap_rom(pdev, rom);
}
static const struct mgag200_device_funcs mgag200_g200_device_funcs = {
.pixpllc_atomic_check = mgag200_g200_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200_pixpllc_atomic_update,
};
struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv)
{
struct mgag200_g200_device *g200;
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
g200 = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200_device, base.base);
if (IS_ERR(g200))
return ERR_CAST(g200);
mdev = &g200->base;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_g200_init_pci_options(pdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
mgag200_g200_init_refclk(g200);
ret = mgag200_device_init(mdev, &mgag200_g200_device_info,
&mgag200_g200_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200_init_registers(g200);
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
void mgag200_g200eh_init_registers(struct mga_device *mdev)
{
static const u8 dacvalue[] = {
MGAG200_DAC_DEFAULT(0x00, 0xc9,
MGA1064_MISC_CTL_VGA8 | MGA1064_MISC_CTL_DAC_RAM_CS,
0x00, 0x00, 0x00)
};
size_t i;
for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
if ((i <= 0x17) ||
(i == 0x1b) ||
(i == 0x1c) ||
((i >= 0x1f) && (i <= 0x29)) ||
((i >= 0x30) && (i <= 0x37)) ||
((i >= 0x44) && (i <= 0x4e)))
continue;
WREG_DAC(i, dacvalue[i]);
}
mgag200_init_registers(mdev);
}
/*
* PIXPLLC
*/
static int mgag200_g200eh_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 33333;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 16; testp > 0; testp >>= 1) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testm = 1; testm < 33; testm++) {
for (testn = 17; testn < 257; testn++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
void mgag200_g200eh_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
int i, j, tmpcount, vcount;
bool pll_locked = false;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
for (i = 0; i <= 32 && pll_locked == false; i++) {
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG_DAC(MGA1064_EH_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_EH_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_EH_PIX_PLLC_P, xpixpllcp);
udelay(500);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
vcount = RREG8(MGAREG_VCOUNT);
for (j = 0; j < 30 && pll_locked == false; j++) {
tmpcount = RREG8(MGAREG_VCOUNT);
if (tmpcount < vcount)
vcount = 0;
if ((tmpcount - vcount) > 2)
pll_locked = true;
else
udelay(5);
}
}
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200eh_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200eh_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static const struct drm_crtc_helper_funcs mgag200_g200eh_crtc_helper_funcs = {
MGAG200_CRTC_HELPER_FUNCS,
};
static const struct drm_crtc_funcs mgag200_g200eh_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200eh_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200eh_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200eh_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200eh_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200eh_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200eh_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 37500, false, 1, 0, false);
static const struct mgag200_device_funcs mgag200_g200eh_device_funcs = {
.pixpllc_atomic_check = mgag200_g200eh_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200eh_pixpllc_atomic_update,
};
struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200eh_device_info,
&mgag200_g200eh_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200eh_init_registers(mdev);
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200eh_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
/*
* PIXPLLC
*/
static int mgag200_g200eh3_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 3000000;
static const unsigned int vcomin = 1500000;
static const unsigned int pllreffreq = 25000;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
testp = 0;
for (testm = 150; testm >= 6; testm--) {
if (clock * testm > vcomax)
continue;
if (clock * testm < vcomin)
continue;
for (testn = 120; testn >= 60; testn--) {
computed = (pllreffreq * testn) / testm;
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn + 1;
m = testm + 1;
p = testp + 1;
}
if (delta == 0)
break;
}
if (delta == 0)
break;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200eh3_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200eh3_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static const struct drm_crtc_helper_funcs mgag200_g200eh3_crtc_helper_funcs = {
MGAG200_CRTC_HELPER_FUNCS,
};
static const struct drm_crtc_funcs mgag200_g200eh3_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200eh3_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200eh3_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200eh3_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200eh3_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200eh3_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200eh3_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false);
static const struct mgag200_device_funcs mgag200_g200eh3_device_funcs = {
.pixpllc_atomic_check = mgag200_g200eh3_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200eh_pixpllc_atomic_update, // same as G200EH
};
struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev,
const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200eh3_device_info,
&mgag200_g200eh3_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200eh_init_registers(mdev); // same as G200EH
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200eh3_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/limits.h>
#include <linux/pci.h>
#include <linux/units.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
/*
* PIXPLLC
*/
static int mgag200_g200eh5_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
const unsigned long long VCO_MAX = 10 * GIGA; // Hz
const unsigned long long VCO_MIN = 2500 * MEGA; // Hz
const unsigned long long PLL_FREQ_REF = 25 * MEGA; // Hz
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned long long fdelta = ULLONG_MAX;
u16 mult_max = (u16)(VCO_MAX / PLL_FREQ_REF); // 400 (0x190)
u16 mult_min = (u16)(VCO_MIN / PLL_FREQ_REF); // 100 (0x64)
u64 ftmp_delta;
u64 computed_fo;
u16 test_m;
u8 test_div_a;
u8 test_div_b;
u64 fo_hz;
u8 uc_m = 0;
u8 uc_n = 0;
u8 uc_p = 0;
fo_hz = (u64)clock * HZ_PER_KHZ;
for (test_m = mult_min; test_m <= mult_max; test_m++) { // This gives 100 <= M <= 400
for (test_div_a = 8; test_div_a > 0; test_div_a--) { // This gives 1 <= A <= 8
for (test_div_b = 1; test_div_b <= test_div_a; test_div_b++) {
// This gives 1 <= B <= A
computed_fo = (PLL_FREQ_REF * test_m) /
(4 * test_div_a * test_div_b);
if (computed_fo > fo_hz)
ftmp_delta = computed_fo - fo_hz;
else
ftmp_delta = fo_hz - computed_fo;
if (ftmp_delta < fdelta) {
fdelta = ftmp_delta;
uc_m = (u8)(0xFF & test_m);
uc_n = (u8)((0x7 & (test_div_a - 1))
| (0x70 & (0x7 & (test_div_b - 1)) << 4));
uc_p = (u8)(1 & (test_m >> 8));
}
if (fdelta == 0)
break;
}
if (fdelta == 0)
break;
}
if (fdelta == 0)
break;
}
pixpllc->m = uc_m + 1;
pixpllc->n = uc_n + 1;
pixpllc->p = uc_p + 1;
pixpllc->s = 0;
return 0;
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200eh5_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200eh5_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static const struct drm_crtc_helper_funcs mgag200_g200eh5_crtc_helper_funcs = {
MGAG200_CRTC_HELPER_FUNCS,
};
static const struct drm_crtc_funcs mgag200_g200eh5_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200eh5_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200eh5_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200eh5_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200eh5_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200eh5_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200eh5_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false);
static const struct mgag200_device_funcs mgag200_g200eh5_device_funcs = {
.pixpllc_atomic_check = mgag200_g200eh5_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200eh_pixpllc_atomic_update, // same as G200EH
};
struct mga_device *mgag200_g200eh5_device_create(struct pci_dev *pdev,
const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200eh5_device_info,
&mgag200_g200eh5_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200eh_init_registers(mdev); // same as G200EH
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200eh5_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,313 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
static void mgag200_g200er_init_registers(struct mga_device *mdev)
{
static const u8 dacvalue[] = {
MGAG200_DAC_DEFAULT(0x00, 0xc9, 0x1f, 0x00, 0x00, 0x00)
};
size_t i;
for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
if ((i <= 0x17) ||
(i == 0x1b) ||
(i == 0x1c) ||
((i >= 0x1f) && (i <= 0x29)) ||
((i >= 0x30) && (i <= 0x37)))
continue;
WREG_DAC(i, dacvalue[i]);
}
WREG_DAC(0x90, 0); /* G200ER specific */
mgag200_init_registers(mdev);
WREG_ECRT(0x24, 0x5); /* G200ER specific */
}
static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev)
{
static const 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);
}
/*
* PIXPLLC
*/
static int mgag200_g200er_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 1488000;
static const unsigned int vcomin = 1056000;
static const unsigned int pllreffreq = 48000;
static const unsigned int m_div_val[] = { 1, 2, 4, 8 };
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta;
int testr, testn, testm, testo;
unsigned int p, m, n, s;
unsigned int computed, vco;
m = n = p = s = 0;
delta = 0xffffffff;
for (testr = 0; testr < 4; testr++) {
if (delta == 0)
break;
for (testn = 5; testn < 129; testn++) {
if (delta == 0)
break;
for (testm = 3; testm >= 0; testm--) {
if (delta == 0)
break;
for (testo = 5; testo < 33; testo++) {
vco = pllreffreq * (testn + 1) /
(testr + 1);
if (vco < vcomin)
continue;
if (vco > vcomax)
continue;
computed = vco / (m_div_val[testm] * (testo + 1));
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = (testm | (testo << 3)) + 1;
n = testn + 1;
p = testr + 1;
s = testr;
}
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_g200er_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_REMHEADCTL_CLKDIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= (0x3<<2) | 0xc0;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG_DAC(MGA1064_ER_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_ER_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_ER_PIX_PLLC_P, xpixpllcp);
udelay(50);
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200er_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200er_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static void mgag200_g200er_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
const struct drm_format_info *format = mgag200_crtc_state->format;
mgag200_set_format_regs(mdev, format);
mgag200_set_mode_regs(mdev, adjusted_mode, mgag200_crtc_state->set_vidrst);
if (funcs->pixpllc_atomic_update)
funcs->pixpllc_atomic_update(crtc, old_state);
mgag200_g200er_reset_tagfifo(mdev);
if (crtc_state->gamma_lut)
mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data);
else
mgag200_crtc_fill_gamma(mdev, format);
mgag200_enable_display(mdev);
}
static const struct drm_crtc_helper_funcs mgag200_g200er_crtc_helper_funcs = {
.mode_valid = mgag200_crtc_helper_mode_valid,
.atomic_check = mgag200_crtc_helper_atomic_check,
.atomic_flush = mgag200_crtc_helper_atomic_flush,
.atomic_enable = mgag200_g200er_crtc_helper_atomic_enable,
.atomic_disable = mgag200_crtc_helper_atomic_disable
};
static const struct drm_crtc_funcs mgag200_g200er_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200er_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200er_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200er_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200er_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200er_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200er_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 1, 0, false);
static const struct mgag200_device_funcs mgag200_g200er_device_funcs = {
.pixpllc_atomic_check = mgag200_g200er_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200er_pixpllc_atomic_update,
};
struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200er_device_info,
&mgag200_g200er_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200er_init_registers(mdev);
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200er_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,318 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
static void mgag200_g200ev_init_registers(struct mga_device *mdev)
{
static const u8 dacvalue[] = {
MGAG200_DAC_DEFAULT(0x00,
MGA1064_PIX_CLK_CTL_SEL_PLL,
MGA1064_MISC_CTL_VGA8 | MGA1064_MISC_CTL_DAC_RAM_CS,
0x00, 0x00, 0x00)
};
size_t i;
for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
if ((i <= 0x17) ||
(i == 0x1b) ||
(i == 0x1c) ||
((i >= 0x1f) && (i <= 0x29)) ||
((i >= 0x30) && (i <= 0x37)) ||
((i >= 0x44) && (i <= 0x4e)))
continue;
WREG_DAC(i, dacvalue[i]);
}
mgag200_init_registers(mdev);
}
static void mgag200_g200ev_set_hiprilvl(struct mga_device *mdev)
{
WREG_ECRT(0x06, 0x00);
}
/*
* PIXPLLC
*/
static int mgag200_g200ev_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 550000;
static const unsigned int vcomin = 150000;
static const unsigned int pllreffreq = 50000;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 16; testp > 0; testp--) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testn = 1; testn < 257; testn++) {
for (testm = 1; testm < 17; testm++) {
computed = (pllreffreq * testn) /
(testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_g200ev_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
tmp = RREG8(DAC_DATA);
WREG8(DAC_DATA, tmp & ~0x40);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
WREG_DAC(MGA1064_EV_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_EV_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_EV_PIX_PLLC_P, xpixpllcp);
udelay(50);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
WREG8(DAC_DATA, tmp);
udelay(500);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
tmp = RREG8(DAC_DATA);
WREG8(DAC_DATA, tmp | 0x40);
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= (0x3 << 2);
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200ev_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200ev_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static void mgag200_g200ev_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
const struct drm_format_info *format = mgag200_crtc_state->format;
mgag200_set_format_regs(mdev, format);
mgag200_set_mode_regs(mdev, adjusted_mode, mgag200_crtc_state->set_vidrst);
if (funcs->pixpllc_atomic_update)
funcs->pixpllc_atomic_update(crtc, old_state);
mgag200_g200ev_set_hiprilvl(mdev);
if (crtc_state->gamma_lut)
mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data);
else
mgag200_crtc_fill_gamma(mdev, format);
mgag200_enable_display(mdev);
}
static const struct drm_crtc_helper_funcs mgag200_g200ev_crtc_helper_funcs = {
.mode_valid = mgag200_crtc_helper_mode_valid,
.atomic_check = mgag200_crtc_helper_atomic_check,
.atomic_flush = mgag200_crtc_helper_atomic_flush,
.atomic_enable = mgag200_g200ev_crtc_helper_atomic_enable,
.atomic_disable = mgag200_crtc_helper_atomic_disable
};
static const struct drm_crtc_funcs mgag200_g200ev_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200ev_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200ev_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200ev_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200ev_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200ev_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200ev_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 32700, false, 0, 1, false);
static const struct mgag200_device_funcs mgag200_g200ev_device_funcs = {
.pixpllc_atomic_check = mgag200_g200ev_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200ev_pixpllc_atomic_update,
};
struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200ev_device_info,
&mgag200_g200ev_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200ev_init_registers(mdev);
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200ev_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
static void mgag200_g200ew3_init_registers(struct mga_device *mdev)
{
mgag200_g200wb_init_registers(mdev); // same as G200WB
WREG_ECRT(0x34, 0x5); // G200EW3 specific
}
/*
* PIXPLLC
*/
static int mgag200_g200ew3_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 800000;
static const unsigned int vcomin = 400000;
static const unsigned int pllreffreq = 25000;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn, testp2;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 1; testp < 8; testp++) {
for (testp2 = 1; testp2 < 8; testp2++) {
if (testp < testp2)
continue;
if ((clock * testp * testp2) > vcomax)
continue;
if ((clock * testp * testp2) < vcomin)
continue;
for (testm = 1; testm < 26; testm++) {
for (testn = 32; testn < 2048 ; testn++) {
computed = (pllreffreq * testn) / (testm * testp * testp2);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm + 1;
n = testn + 1;
p = testp + 1;
s = testp2;
}
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200ew3_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200ew3_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static const struct drm_crtc_helper_funcs mgag200_g200ew3_crtc_helper_funcs = {
MGAG200_CRTC_HELPER_FUNCS,
};
static const struct drm_crtc_funcs mgag200_g200ew3_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200ew3_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200ew3_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200ew3_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200ew3_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200ew3_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200ew3_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false);
static const struct mgag200_device_funcs mgag200_g200ew3_device_funcs = {
.pixpllc_atomic_check = mgag200_g200ew3_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200wb_pixpllc_atomic_update, // same as G200WB
};
static resource_size_t mgag200_g200ew3_device_probe_vram(struct mga_device *mdev)
{
resource_size_t vram_size = resource_size(mdev->vram_res);
if (vram_size >= 0x1000000)
vram_size = vram_size - 0x400000;
return mgag200_probe_vram(mdev->vram, vram_size);
}
struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev,
const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200ew3_device_info,
&mgag200_g200ew3_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200ew3_init_registers(mdev);
vram_available = mgag200_g200ew3_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200ew3_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,525 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
static int mgag200_g200se_init_pci_options(struct pci_dev *pdev)
{
struct device *dev = &pdev->dev;
bool has_sgram;
u32 option;
int err;
err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
if (err != PCIBIOS_SUCCESSFUL) {
dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
return pcibios_err_to_errno(err);
}
has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK);
option = 0x40049120;
if (has_sgram)
option |= PCI_MGA_OPTION_HARDPWMSK;
return mgag200_init_pci_options(pdev, option, 0x00008000);
}
static void mgag200_g200se_init_registers(struct mgag200_g200se_device *g200se)
{
static const u8 dacvalue[] = {
MGAG200_DAC_DEFAULT(0x03,
MGA1064_PIX_CLK_CTL_SEL_PLL,
MGA1064_MISC_CTL_DAC_EN |
MGA1064_MISC_CTL_VGA8 |
MGA1064_MISC_CTL_DAC_RAM_CS,
0x00, 0x00, 0x00)
};
struct mga_device *mdev = &g200se->base;
size_t i;
for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
if ((i <= 0x17) ||
(i == 0x1b) ||
(i == 0x1c) ||
((i >= 0x1f) && (i <= 0x29)) ||
((i == 0x2c) || (i == 0x2d) || (i == 0x2e)) ||
((i >= 0x30) && (i <= 0x37)))
continue;
WREG_DAC(i, dacvalue[i]);
}
mgag200_init_registers(mdev);
}
static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
const struct drm_display_mode *mode,
const struct drm_format_info *format)
{
struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base);
unsigned int hiprilvl;
u8 crtcext6;
if (g200se->unique_rev_id >= 0x04) {
hiprilvl = 0;
} else if (g200se->unique_rev_id >= 0x02) {
unsigned int bpp;
unsigned long mb;
switch (format->format) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_RGB888:
bpp = 32;
break;
case DRM_FORMAT_RGB565:
case DRM_FORMAT_XRGB1555:
bpp = 16;
break;
case DRM_FORMAT_C8:
bpp = 8;
break;
}
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;
} else if (g200se->unique_rev_id >= 0x01) {
hiprilvl = 3;
} else {
hiprilvl = 4;
}
crtcext6 = hiprilvl; /* implicitly sets maxhipri to 0 */
WREG_ECRT(0x06, crtcext6);
}
/*
* PIXPLLC
*/
static int mgag200_g200se_00_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 320000;
static const unsigned int vcomin = 160000;
static const unsigned int pllreffreq = 25000;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
permitteddelta = clock * 5 / 1000;
for (testp = 8; testp > 0; testp /= 2) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testn = 17; testn < 256; testn++) {
for (testm = 1; testm < 32; testm++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm;
n = testn;
p = testp;
}
}
}
}
if (delta > permitteddelta) {
pr_warn("PLL delta too large\n");
return -EINVAL;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_g200se_00_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = pixpllcm | ((pixpllcn & BIT(8)) >> 1);
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
}
static int mgag200_g200se_04_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 1600000;
static const unsigned int vcomin = 800000;
static const unsigned int pllreffreq = 25000;
static const unsigned int pvalues_e4[] = {16, 14, 12, 10, 8, 6, 4, 2, 1};
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta, permitteddelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
unsigned int fvv;
unsigned int i;
m = n = p = s = 0;
delta = 0xffffffff;
if (clock < 25000)
clock = 25000;
clock = clock * 2;
/* Permited delta is 0.5% as VESA Specification */
permitteddelta = clock * 5 / 1000;
for (i = 0 ; i < ARRAY_SIZE(pvalues_e4); i++) {
testp = pvalues_e4[i];
if ((clock * testp) > vcomax)
continue;
if ((clock * testp) < vcomin)
continue;
for (testn = 50; testn <= 256; testn++) {
for (testm = 1; testm <= 32; testm++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
m = testm;
n = testn;
p = testp;
}
}
}
}
fvv = pllreffreq * n / m;
fvv = (fvv - 800000) / 50000;
if (fvv > 15)
fvv = 15;
s = fvv << 1;
if (delta > permitteddelta) {
pr_warn("PLL delta too large\n");
return -EINVAL;
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
static void mgag200_g200se_04_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
// For G200SE A, BIT(7) should be set unconditionally.
xpixpllcm = BIT(7) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
WREG_DAC(0x1a, 0x09);
msleep(20);
WREG_DAC(0x1a, 0x01);
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200se_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200se_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static void mgag200_g200se_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
const struct drm_format_info *format = mgag200_crtc_state->format;
mgag200_set_format_regs(mdev, format);
mgag200_set_mode_regs(mdev, adjusted_mode, mgag200_crtc_state->set_vidrst);
if (funcs->pixpllc_atomic_update)
funcs->pixpllc_atomic_update(crtc, old_state);
mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, format);
if (crtc_state->gamma_lut)
mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data);
else
mgag200_crtc_fill_gamma(mdev, format);
mgag200_enable_display(mdev);
}
static const struct drm_crtc_helper_funcs mgag200_g200se_crtc_helper_funcs = {
.mode_valid = mgag200_crtc_helper_mode_valid,
.atomic_check = mgag200_crtc_helper_atomic_check,
.atomic_flush = mgag200_crtc_helper_atomic_flush,
.atomic_enable = mgag200_g200se_crtc_helper_atomic_enable,
.atomic_disable = mgag200_crtc_helper_atomic_disable
};
static const struct drm_crtc_funcs mgag200_g200se_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200se_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200se_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200se_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200se_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200se_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200se_a_01_device_info =
MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, true);
static const struct mgag200_device_info mgag200_g200se_a_02_device_info =
MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, true);
static const struct mgag200_device_info mgag200_g200se_a_03_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false);
static const struct mgag200_device_info mgag200_g200se_b_01_device_info =
MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, false);
static const struct mgag200_device_info mgag200_g200se_b_02_device_info =
MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, false);
static const struct mgag200_device_info mgag200_g200se_b_03_device_info =
MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false);
static int mgag200_g200se_init_unique_rev_id(struct mgag200_g200se_device *g200se)
{
struct mga_device *mdev = &g200se->base;
struct drm_device *dev = &mdev->base;
/* stash G200 SE model number for later use */
g200se->unique_rev_id = RREG32(0x1e24);
if (!g200se->unique_rev_id)
return -ENODEV;
drm_dbg(dev, "G200 SE unique revision id is 0x%x\n", g200se->unique_rev_id);
return 0;
}
static const struct mgag200_device_funcs mgag200_g200se_00_device_funcs = {
.pixpllc_atomic_check = mgag200_g200se_00_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200se_00_pixpllc_atomic_update,
};
static const struct mgag200_device_funcs mgag200_g200se_04_device_funcs = {
.pixpllc_atomic_check = mgag200_g200se_04_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200se_04_pixpllc_atomic_update,
};
struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
enum mga_type type)
{
struct mgag200_g200se_device *g200se;
const struct mgag200_device_info *info;
const struct mgag200_device_funcs *funcs;
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
g200se = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200se_device, base.base);
if (IS_ERR(g200se))
return ERR_CAST(g200se);
mdev = &g200se->base;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_g200se_init_pci_options(pdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200se_init_unique_rev_id(g200se);
if (ret)
return ERR_PTR(ret);
switch (type) {
case G200_SE_A:
if (g200se->unique_rev_id >= 0x03)
info = &mgag200_g200se_a_03_device_info;
else if (g200se->unique_rev_id >= 0x02)
info = &mgag200_g200se_a_02_device_info;
else
info = &mgag200_g200se_a_01_device_info;
break;
case G200_SE_B:
if (g200se->unique_rev_id >= 0x03)
info = &mgag200_g200se_b_03_device_info;
else if (g200se->unique_rev_id >= 0x02)
info = &mgag200_g200se_b_02_device_info;
else
info = &mgag200_g200se_b_01_device_info;
break;
default:
return ERR_PTR(-EINVAL);
}
if (g200se->unique_rev_id >= 0x04)
funcs = &mgag200_g200se_04_device_funcs;
else
funcs = &mgag200_g200se_00_device_funcs;
ret = mgag200_device_init(mdev, info, funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200se_init_registers(g200se);
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200se_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,327 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.h>
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
void mgag200_g200wb_init_registers(struct mga_device *mdev)
{
static const u8 dacvalue[] = {
MGAG200_DAC_DEFAULT(0x07, 0xc9, 0x1f, 0x00, 0x00, 0x00)
};
size_t i;
for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
if ((i <= 0x17) ||
(i == 0x1b) ||
(i == 0x1c) ||
((i >= 0x1f) && (i <= 0x29)) ||
((i >= 0x30) && (i <= 0x37)) ||
((i >= 0x44) && (i <= 0x4e)))
continue;
WREG_DAC(i, dacvalue[i]);
}
mgag200_init_registers(mdev);
}
/*
* PIXPLLC
*/
static int mgag200_g200wb_pixpllc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *new_state)
{
static const unsigned int vcomax = 550000;
static const unsigned int vcomin = 150000;
static const unsigned int pllreffreq = 48000;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
long clock = new_crtc_state->mode.clock;
struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
unsigned int delta, tmpdelta;
unsigned int testp, testm, testn;
unsigned int p, m, n, s;
unsigned int computed;
m = n = p = s = 0;
delta = 0xffffffff;
for (testp = 1; testp < 9; testp++) {
if (clock * testp > vcomax)
continue;
if (clock * testp < vcomin)
continue;
for (testm = 1; testm < 17; testm++) {
for (testn = 1; testn < 151; testn++) {
computed = (pllreffreq * testn) / (testm * testp);
if (computed > clock)
tmpdelta = computed - clock;
else
tmpdelta = clock - computed;
if (tmpdelta < delta) {
delta = tmpdelta;
n = testn;
m = testm;
p = testp;
s = 0;
}
}
}
}
pixpllc->m = m;
pixpllc->n = n;
pixpllc->p = p;
pixpllc->s = s;
return 0;
}
void mgag200_g200wb_pixpllc_atomic_update(struct drm_crtc *crtc,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
bool pll_locked = false;
unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp;
int i, j, tmpcount, vcount;
pixpllcm = pixpllc->m - 1;
pixpllcn = pixpllc->n - 1;
pixpllcp = pixpllc->p - 1;
pixpllcs = pixpllc->s;
xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm;
xpixpllcn = pixpllcn;
xpixpllcp = ((pixpllcn & GENMASK(10, 9)) >> 3) | (pixpllcs << 3) | pixpllcp;
WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
for (i = 0; i <= 32 && pll_locked == false; i++) {
if (i > 0) {
WREG8(MGAREG_CRTC_INDEX, 0x1e);
tmp = RREG8(MGAREG_CRTC_DATA);
if (tmp < 0xff)
WREG8(MGAREG_CRTC_DATA, tmp+1);
}
/* set pixclkdis to 1 */
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_REMHEADCTL_CLKDIS;
WREG8(DAC_DATA, tmp);
/* select PLL Set C */
tmp = RREG8(MGAREG_MEM_MISC_READ);
tmp |= 0x3 << 2;
WREG8(MGAREG_MEM_MISC_WRITE, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80;
WREG8(DAC_DATA, tmp);
udelay(500);
/* reset the PLL */
WREG8(DAC_INDEX, MGA1064_VREF_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~0x04;
WREG8(DAC_DATA, tmp);
udelay(50);
/* program pixel pll register */
WREG_DAC(MGA1064_WB_PIX_PLLC_N, xpixpllcn);
WREG_DAC(MGA1064_WB_PIX_PLLC_M, xpixpllcm);
WREG_DAC(MGA1064_WB_PIX_PLLC_P, xpixpllcp);
udelay(50);
/* turn pll on */
WREG8(DAC_INDEX, MGA1064_VREF_CTL);
tmp = RREG8(DAC_DATA);
tmp |= 0x04;
WREG_DAC(MGA1064_VREF_CTL, tmp);
udelay(500);
/* select the pixel pll */
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
WREG8(DAC_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK;
tmp |= MGA1064_REMHEADCTL_CLKSL_PLL;
WREG8(DAC_DATA, tmp);
/* reset dotclock rate bit */
WREG8(MGAREG_SEQ_INDEX, 1);
tmp = RREG8(MGAREG_SEQ_DATA);
tmp &= ~0x8;
WREG8(MGAREG_SEQ_DATA, tmp);
WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
WREG8(DAC_DATA, tmp);
vcount = RREG8(MGAREG_VCOUNT);
for (j = 0; j < 30 && pll_locked == false; j++) {
tmpcount = RREG8(MGAREG_VCOUNT);
if (tmpcount < vcount)
vcount = 0;
if ((tmpcount - vcount) > 2)
pll_locked = true;
else
udelay(5);
}
}
WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
tmp = RREG8(DAC_DATA);
tmp &= ~MGA1064_REMHEADCTL_CLKDIS;
WREG_DAC(MGA1064_REMHEADCTL, tmp);
}
/*
* Mode-setting pipeline
*/
static const struct drm_plane_helper_funcs mgag200_g200wb_primary_plane_helper_funcs = {
MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
};
static const struct drm_plane_funcs mgag200_g200wb_primary_plane_funcs = {
MGAG200_PRIMARY_PLANE_FUNCS,
};
static const struct drm_crtc_helper_funcs mgag200_g200wb_crtc_helper_funcs = {
MGAG200_CRTC_HELPER_FUNCS,
};
static const struct drm_crtc_funcs mgag200_g200wb_crtc_funcs = {
MGAG200_CRTC_FUNCS,
};
static int mgag200_g200wb_pipeline_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_plane *primary_plane = &mdev->primary_plane;
struct drm_crtc *crtc = &mdev->crtc;
int ret;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&mgag200_g200wb_primary_plane_funcs,
mgag200_primary_plane_formats,
mgag200_primary_plane_formats_size,
mgag200_primary_plane_fmtmods,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
return ret;
}
drm_plane_helper_add(primary_plane, &mgag200_g200wb_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&mgag200_g200wb_crtc_funcs, NULL);
if (ret) {
drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
return ret;
}
drm_crtc_helper_add(crtc, &mgag200_g200wb_crtc_helper_funcs);
/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
ret = mgag200_vga_bmc_output_init(mdev);
if (ret)
return ret;
return 0;
}
/*
* DRM device
*/
static const struct mgag200_device_info mgag200_g200wb_device_info =
MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false);
static const struct mgag200_device_funcs mgag200_g200wb_device_funcs = {
.pixpllc_atomic_check = mgag200_g200wb_pixpllc_atomic_check,
.pixpllc_atomic_update = mgag200_g200wb_pixpllc_atomic_update,
};
struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv)
{
struct mga_device *mdev;
struct drm_device *dev;
resource_size_t vram_available;
int ret;
mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
if (IS_ERR(mdev))
return mdev;
dev = &mdev->base;
pci_set_drvdata(pdev, dev);
ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_preinit(mdev);
if (ret)
return ERR_PTR(ret);
ret = mgag200_device_init(mdev, &mgag200_g200wb_device_info,
&mgag200_g200wb_device_funcs);
if (ret)
return ERR_PTR(ret);
mgag200_g200wb_init_registers(mdev);
vram_available = mgag200_device_probe_vram(mdev);
ret = mgag200_mode_config_init(mdev, vram_available);
if (ret)
return ERR_PTR(ret);
ret = mgag200_g200wb_pipeline_init(mdev);
if (ret)
return ERR_PTR(ret);
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
return mdev;
}
@@ -0,0 +1,831 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2010 Matt Turner.
* Copyright 2012 Red Hat
*
* Authors: Matthew Garrett
* Matt Turner
* Dave Airlie
*/
#include <linux/delay.h>
#include <linux/iosys-map.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_panic.h>
#include <drm/drm_print.h>
#include "mgag200_ddc.h"
#include "mgag200_drv.h"
/*
* This file contains setup code for the CRTC.
*/
static void mgag200_set_gamma_lut(struct drm_crtc *crtc, unsigned int index,
u16 red, u16 green, u16 blue)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
u8 i8 = index & 0xff;
u8 r8 = red >> 8;
u8 g8 = green >> 8;
u8 b8 = blue >> 8;
if (drm_WARN_ON_ONCE(dev, index != i8))
return; /* driver bug */
WREG8(DAC_INDEX + MGA1064_INDEX, i8);
WREG8(DAC_INDEX + MGA1064_COL_PAL, r8);
WREG8(DAC_INDEX + MGA1064_COL_PAL, g8);
WREG8(DAC_INDEX + MGA1064_COL_PAL, b8);
}
void mgag200_crtc_fill_gamma(struct mga_device *mdev,
const struct drm_format_info *format)
{
struct drm_crtc *crtc = &mdev->crtc;
switch (format->format) {
case DRM_FORMAT_C8:
drm_crtc_fill_palette_8(crtc, mgag200_set_gamma_lut);
break;
case DRM_FORMAT_XRGB1555:
drm_crtc_fill_gamma_555(crtc, mgag200_set_gamma_lut);
break;
case DRM_FORMAT_RGB565:
drm_crtc_fill_gamma_565(crtc, mgag200_set_gamma_lut);
break;
case DRM_FORMAT_RGB888:
case DRM_FORMAT_XRGB8888:
drm_crtc_fill_gamma_888(crtc, mgag200_set_gamma_lut);
break;
default:
drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
&format->format);
break;
}
}
void mgag200_crtc_load_gamma(struct mga_device *mdev,
const struct drm_format_info *format,
struct drm_color_lut *lut)
{
struct drm_crtc *crtc = &mdev->crtc;
switch (format->format) {
case DRM_FORMAT_C8:
drm_crtc_load_palette_8(crtc, lut, mgag200_set_gamma_lut);
break;
case DRM_FORMAT_XRGB1555:
drm_crtc_load_gamma_555_from_888(crtc, lut, mgag200_set_gamma_lut);
break;
case DRM_FORMAT_RGB565:
drm_crtc_load_gamma_565_from_888(crtc, lut, mgag200_set_gamma_lut);
break;
case DRM_FORMAT_RGB888:
case DRM_FORMAT_XRGB8888:
drm_crtc_load_gamma_888(crtc, lut, mgag200_set_gamma_lut);
break;
default:
drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n",
&format->format);
break;
}
}
static inline void mga_wait_vsync(struct mga_device *mdev)
{
unsigned long timeout = jiffies + HZ/10;
unsigned int status = 0;
do {
status = RREG32(MGAREG_STATUS);
} while ((status & 0x08) && time_before(jiffies, timeout));
timeout = jiffies + HZ/10;
status = 0;
do {
status = RREG32(MGAREG_STATUS);
} while (!(status & 0x08) && time_before(jiffies, timeout));
}
static inline void mga_wait_busy(struct mga_device *mdev)
{
unsigned long timeout = jiffies + HZ;
unsigned int status = 0;
do {
status = RREG8(MGAREG_STATUS + 2);
} while ((status & 0x01) && time_before(jiffies, timeout));
}
/*
* 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.
*/
static void mgag200_set_startadd(struct mga_device *mdev,
unsigned long offset)
{
struct drm_device *dev = &mdev->base;
u32 startadd;
u8 crtcc, crtcd, crtcext0;
startadd = offset / 8;
if (startadd > 0)
drm_WARN_ON_ONCE(dev, mdev->info->bug_no_startadd);
/*
* 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);
}
/*
* Set the opmode for the hardware swapper for Big-Endian processor
* support for the frame buffer aperture and DMAWIN space.
*/
static void mgag200_set_datasiz(struct mga_device *mdev, u32 format)
{
#if defined(__BIG_ENDIAN)
u32 opmode = RREG32(MGAREG_OPMODE);
opmode &= ~(GENMASK(17, 16) | GENMASK(9, 8) | GENMASK(3, 2));
/* Big-endian byte-swapping */
switch (format) {
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
opmode |= 0x10100;
break;
case DRM_FORMAT_XRGB8888:
opmode |= 0x20200;
break;
}
WREG32(MGAREG_OPMODE, opmode);
#endif
}
void mgag200_init_registers(struct mga_device *mdev)
{
u8 crtc11, misc;
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);
RREG_CRT(0x11, crtc11);
crtc11 &= ~(MGAREG_CRTC11_CRTCPROTECT |
MGAREG_CRTC11_VINTEN |
MGAREG_CRTC11_VINTCLR);
WREG_CRT(0x11, crtc11);
misc = RREG8(MGA_MISC_IN);
misc |= MGAREG_MISC_IOADSEL;
WREG8(MGA_MISC_OUT, misc);
}
void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode,
bool set_vidrst)
{
unsigned int hdispend, hsyncstr, hsyncend, htotal, hblkstr, hblkend;
unsigned int vdispend, vsyncstr, vsyncend, vtotal, vblkstr, vblkend;
unsigned int linecomp;
u8 misc, crtcext1, crtcext2, crtcext5;
hdispend = mode->crtc_hdisplay / 8 - 1;
hsyncstr = mode->crtc_hsync_start / 8 - 1;
hsyncend = mode->crtc_hsync_end / 8 - 1;
htotal = mode->crtc_htotal / 8 - 1;
/* Work around hardware quirk */
if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04)
htotal++;
hblkstr = mode->crtc_hblank_start / 8 - 1;
hblkend = htotal;
vdispend = mode->crtc_vdisplay - 1;
vsyncstr = mode->crtc_vsync_start - 1;
vsyncend = mode->crtc_vsync_end - 1;
vtotal = mode->crtc_vtotal - 2;
vblkstr = mode->crtc_vblank_start - 1;
vblkend = vtotal + 1;
linecomp = vdispend;
misc = RREG8(MGA_MISC_IN);
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
misc |= MGAREG_MISC_HSYNCPOL;
else
misc &= ~MGAREG_MISC_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
misc |= MGAREG_MISC_VSYNCPOL;
else
misc &= ~MGAREG_MISC_VSYNCPOL;
crtcext1 = (((htotal - 4) & 0x100) >> 8) |
((hblkstr & 0x100) >> 7) |
((hsyncstr & 0x100) >> 6) |
(hblkend & 0x40);
if (set_vidrst)
crtcext1 |= MGAREG_CRTCEXT1_VRSTEN |
MGAREG_CRTCEXT1_HRSTEN;
crtcext2 = ((vtotal & 0xc00) >> 10) |
((vdispend & 0x400) >> 8) |
((vblkstr & 0xc00) >> 7) |
((vsyncstr & 0xc00) >> 5) |
((linecomp & 0x400) >> 3);
crtcext5 = 0x00;
WREG_CRT(0x00, htotal - 4);
WREG_CRT(0x01, hdispend);
WREG_CRT(0x02, hblkstr);
WREG_CRT(0x03, (hblkend & 0x1f) | 0x80);
WREG_CRT(0x04, hsyncstr);
WREG_CRT(0x05, ((hblkend & 0x20) << 2) | (hsyncend & 0x1f));
WREG_CRT(0x06, vtotal & 0xff);
WREG_CRT(0x07, ((vtotal & 0x100) >> 8) |
((vdispend & 0x100) >> 7) |
((vsyncstr & 0x100) >> 6) |
((vblkstr & 0x100) >> 5) |
((linecomp & 0x100) >> 4) |
((vtotal & 0x200) >> 4) |
((vdispend & 0x200) >> 3) |
((vsyncstr & 0x200) >> 2));
WREG_CRT(0x09, ((vblkstr & 0x200) >> 4) |
((linecomp & 0x200) >> 3));
WREG_CRT(0x10, vsyncstr & 0xff);
WREG_CRT(0x11, (vsyncend & 0x0f) | 0x20);
WREG_CRT(0x12, vdispend & 0xff);
WREG_CRT(0x14, 0);
WREG_CRT(0x15, vblkstr & 0xff);
WREG_CRT(0x16, vblkend & 0xff);
WREG_CRT(0x17, 0xc3);
WREG_CRT(0x18, linecomp & 0xff);
WREG_ECRT(0x01, crtcext1);
WREG_ECRT(0x02, crtcext2);
WREG_ECRT(0x05, crtcext5);
WREG8(MGA_MISC_OUT, misc);
}
static void mgag200_set_offset(struct mga_device *mdev,
const struct drm_framebuffer *fb)
{
u8 crtc13, crtcext0;
u32 offset = fb->pitches[0] / 16;
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);
}
void mgag200_set_format_regs(struct mga_device *mdev, const struct drm_format_info *format)
{
u8 xmulctrl = 0;
u8 crtcext3;
RREG_ECRT(3, crtcext3);
switch (format->format) {
case DRM_FORMAT_C8:
crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
crtcext3 |= 0x0;
break;
case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
crtcext3 |= 0x01;
break;
case DRM_FORMAT_RGB888:
crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
crtcext3 |= 0x02;
break;
case DRM_FORMAT_XRGB8888:
crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
crtcext3 |= 0x03;
break;
}
switch (format->format) {
case DRM_FORMAT_C8:
xmulctrl = MGA1064_MUL_CTL_8bits;
break;
case DRM_FORMAT_XRGB1555:
xmulctrl = MGA1064_MUL_CTL_15bits;
break;
case DRM_FORMAT_RGB565:
xmulctrl = MGA1064_MUL_CTL_16bits;
break;
case DRM_FORMAT_RGB888:
xmulctrl = MGA1064_MUL_CTL_24bits;
break;
case DRM_FORMAT_XRGB8888:
xmulctrl = MGA1064_MUL_CTL_32_24bits;
break;
}
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);
/* 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);
WREG_GFX(7, 0x0f);
WREG_GFX(8, 0x0f);
WREG_ECRT(3, crtcext3);
}
void mgag200_enable_display(struct mga_device *mdev)
{
u8 seq0, crtcext1;
RREG_SEQ(0x00, seq0);
seq0 |= MGAREG_SEQ0_SYNCRST |
MGAREG_SEQ0_ASYNCRST;
WREG_SEQ(0x00, seq0);
/*
* TODO: replace busy waiting with vblank IRQ; put
* msleep(50) before changing SCROFF
*/
mga_wait_vsync(mdev);
mga_wait_busy(mdev);
RREG_ECRT(0x01, crtcext1);
crtcext1 &= ~MGAREG_CRTCEXT1_VSYNCOFF;
crtcext1 &= ~MGAREG_CRTCEXT1_HSYNCOFF;
WREG_ECRT(0x01, crtcext1);
}
static void mgag200_disable_display(struct mga_device *mdev)
{
u8 seq0, crtcext1;
RREG_SEQ(0x00, seq0);
seq0 &= ~MGAREG_SEQ0_SYNCRST;
WREG_SEQ(0x00, seq0);
/*
* TODO: replace busy waiting with vblank IRQ; put
* msleep(50) before changing SCROFF
*/
mga_wait_vsync(mdev);
mga_wait_busy(mdev);
RREG_ECRT(0x01, crtcext1);
crtcext1 |= MGAREG_CRTCEXT1_VSYNCOFF |
MGAREG_CRTCEXT1_HSYNCOFF;
WREG_ECRT(0x01, crtcext1);
}
static void mgag200_handle_damage(struct mga_device *mdev, const struct iosys_map *vmap,
struct drm_framebuffer *fb, struct drm_rect *clip)
{
struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(mdev->vram);
iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
drm_fb_memcpy(&dst, fb->pitches, vmap, fb, clip);
}
/*
* Primary plane
*/
const uint32_t mgag200_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB888,
DRM_FORMAT_C8,
};
const size_t mgag200_primary_plane_formats_size = ARRAY_SIZE(mgag200_primary_plane_formats);
const uint64_t mgag200_primary_plane_fmtmods[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
int mgag200_primary_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *new_state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
struct drm_framebuffer *new_fb = new_plane_state->fb;
struct drm_framebuffer *fb = NULL;
struct drm_crtc *new_crtc = new_plane_state->crtc;
struct drm_crtc_state *new_crtc_state = NULL;
struct mgag200_crtc_state *new_mgag200_crtc_state;
int ret;
if (new_crtc)
new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_crtc);
ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
false, true);
if (ret)
return ret;
else if (!new_plane_state->visible)
return 0;
if (plane->state)
fb = plane->state->fb;
if (!fb || (fb->format != new_fb->format))
new_crtc_state->mode_changed = true; /* update PLL settings */
new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
new_mgag200_crtc_state->format = new_fb->format;
return 0;
}
void mgag200_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = plane->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_plane_state *plane_state = plane->state;
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
mgag200_set_datasiz(mdev, fb->format->format);
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
mgag200_handle_damage(mdev, shadow_plane_state->data, fb, &damage);
}
/* Always scanout image at VRAM offset 0 */
mgag200_set_startadd(mdev, (u32)0);
mgag200_set_offset(mdev, fb);
}
void mgag200_primary_plane_helper_atomic_enable(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_device *dev = plane->dev;
struct mga_device *mdev = to_mga_device(dev);
u8 seq1;
RREG_SEQ(0x01, seq1);
seq1 &= ~MGAREG_SEQ1_SCROFF;
WREG_SEQ(0x01, seq1);
msleep(20);
}
void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane,
struct drm_atomic_commit *old_state)
{
struct drm_device *dev = plane->dev;
struct mga_device *mdev = to_mga_device(dev);
u8 seq1;
RREG_SEQ(0x01, seq1);
seq1 |= MGAREG_SEQ1_SCROFF;
WREG_SEQ(0x01, seq1);
msleep(20);
}
int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
struct drm_scanout_buffer *sb)
{
struct mga_device *mdev = to_mga_device(plane->dev);
struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(mdev->vram);
if (plane->state && plane->state->fb) {
sb->format = plane->state->fb->format;
sb->width = plane->state->fb->width;
sb->height = plane->state->fb->height;
sb->pitch[0] = plane->state->fb->pitches[0];
sb->map[0] = map;
return 0;
}
return -ENODEV;
}
/*
* CRTC
*/
enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct mga_device *mdev = to_mga_device(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;
if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
(mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
return MODE_H_ILLEGAL;
}
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;
}
int mgag200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *new_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
struct drm_property_blob *new_gamma_lut = new_crtc_state->gamma_lut;
int ret;
if (!new_crtc_state->enable)
return 0;
ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
if (ret)
return ret;
if (new_crtc_state->mode_changed) {
if (funcs->pixpllc_atomic_check) {
ret = funcs->pixpllc_atomic_check(crtc, new_state);
if (ret)
return ret;
}
}
if (new_crtc_state->color_mgmt_changed && new_gamma_lut) {
if (new_gamma_lut->length != MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) {
drm_dbg(dev, "Wrong size for gamma_lut %zu\n", new_gamma_lut->length);
return -EINVAL;
}
}
return 0;
}
void mgag200_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *old_state)
{
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
if (crtc_state->enable && crtc_state->color_mgmt_changed) {
const struct drm_format_info *format = mgag200_crtc_state->format;
if (crtc_state->gamma_lut)
mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data);
else
mgag200_crtc_fill_gamma(mdev, format);
}
}
void mgag200_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *old_state)
{
struct drm_device *dev = crtc->dev;
struct mga_device *mdev = to_mga_device(dev);
const struct mgag200_device_funcs *funcs = mdev->funcs;
struct drm_crtc_state *crtc_state = crtc->state;
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
const struct drm_format_info *format = mgag200_crtc_state->format;
mgag200_set_format_regs(mdev, format);
mgag200_set_mode_regs(mdev, adjusted_mode, mgag200_crtc_state->set_vidrst);
if (funcs->pixpllc_atomic_update)
funcs->pixpllc_atomic_update(crtc, old_state);
if (crtc_state->gamma_lut)
mgag200_crtc_load_gamma(mdev, format, crtc_state->gamma_lut->data);
else
mgag200_crtc_fill_gamma(mdev, format);
mgag200_enable_display(mdev);
}
void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *old_state)
{
struct mga_device *mdev = to_mga_device(crtc->dev);
mgag200_disable_display(mdev);
}
void mgag200_crtc_reset(struct drm_crtc *crtc)
{
struct mgag200_crtc_state *mgag200_crtc_state;
if (crtc->state)
crtc->funcs->atomic_destroy_state(crtc, crtc->state);
mgag200_crtc_state = kzalloc_obj(*mgag200_crtc_state);
if (mgag200_crtc_state)
__drm_atomic_helper_crtc_reset(crtc, &mgag200_crtc_state->base);
else
__drm_atomic_helper_crtc_reset(crtc, NULL);
}
struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
{
struct drm_crtc_state *crtc_state = crtc->state;
struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
struct mgag200_crtc_state *new_mgag200_crtc_state;
if (!crtc_state)
return NULL;
new_mgag200_crtc_state = kzalloc_obj(*new_mgag200_crtc_state);
if (!new_mgag200_crtc_state)
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc, &new_mgag200_crtc_state->base);
new_mgag200_crtc_state->format = mgag200_crtc_state->format;
memcpy(&new_mgag200_crtc_state->pixpllc, &mgag200_crtc_state->pixpllc,
sizeof(new_mgag200_crtc_state->pixpllc));
new_mgag200_crtc_state->set_vidrst = mgag200_crtc_state->set_vidrst;
return &new_mgag200_crtc_state->base;
}
void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, 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);
}
/*
* Mode config
*/
static void mgag200_mode_config_helper_atomic_commit_tail(struct drm_atomic_commit *state)
{
struct mga_device *mdev = to_mga_device(state->dev);
/*
* 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);
drm_atomic_helper_commit_tail(state);
mutex_unlock(&mdev->rmmio_lock);
}
static const struct drm_mode_config_helper_funcs mgag200_mode_config_helper_funcs = {
.atomic_commit_tail = mgag200_mode_config_helper_atomic_commit_tail,
};
/* 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;
}
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;
const struct mgag200_device_info *info = mdev->info;
max_fbpages = mdev->vram_available >> PAGE_SHIFT;
fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
if (fbpages > max_fbpages)
return MODE_MEM;
/*
* 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))
return MODE_BAD;
}
return MODE_OK;
}
static const struct drm_mode_config_funcs mgag200_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.mode_valid = mgag200_mode_config_mode_valid,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
int mgag200_mode_config_init(struct mga_device *mdev, resource_size_t vram_available)
{
struct drm_device *dev = &mdev->base;
int ret;
mdev->vram_available = vram_available;
ret = drmm_mode_config_init(dev);
if (ret) {
drm_err(dev, "drmm_mode_config_init() failed: %d\n", ret);
return ret;
}
dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH;
dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT;
dev->mode_config.preferred_depth = 24;
dev->mode_config.funcs = &mgag200_mode_config_funcs;
dev->mode_config.helper_private = &mgag200_mode_config_helper_funcs;
return 0;
}
@@ -0,0 +1,699 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* MGA Millennium (MGA2064W) functions
* MGA Mystique (MGA1064SG) functions
*
* Copyright 1996 The XFree86 Project, Inc.
*
* Authors
* Dirk Hohndel
* hohndel@XFree86.Org
* David Dawes
* dawes@XFree86.Org
* Contributors:
* Guy DESBIEF, Aix-en-provence, France
* g.desbief@aix.pacwan.net
* MGA1064SG Mystique register file
*/
#ifndef _MGA_REG_H_
#define _MGA_REG_H_
#include <linux/bits.h>
#define MGAREG_DWGCTL 0x1c00
#define MGAREG_MACCESS 0x1c04
/* the following is a mystique only register */
#define MGAREG_MCTLWTST 0x1c08
#define MGAREG_ZORG 0x1c0c
#define MGAREG_PAT0 0x1c10
#define MGAREG_PAT1 0x1c14
#define MGAREG_PLNWT 0x1c1c
#define MGAREG_BCOL 0x1c20
#define MGAREG_FCOL 0x1c24
#define MGAREG_SRC0 0x1c30
#define MGAREG_SRC1 0x1c34
#define MGAREG_SRC2 0x1c38
#define MGAREG_SRC3 0x1c3c
#define MGAREG_XYSTRT 0x1c40
#define MGAREG_XYEND 0x1c44
#define MGAREG_SHIFT 0x1c50
/* the following is a mystique only register */
#define MGAREG_DMAPAD 0x1c54
#define MGAREG_SGN 0x1c58
#define MGAREG_LEN 0x1c5c
#define MGAREG_AR0 0x1c60
#define MGAREG_AR1 0x1c64
#define MGAREG_AR2 0x1c68
#define MGAREG_AR3 0x1c6c
#define MGAREG_AR4 0x1c70
#define MGAREG_AR5 0x1c74
#define MGAREG_AR6 0x1c78
#define MGAREG_CXBNDRY 0x1c80
#define MGAREG_FXBNDRY 0x1c84
#define MGAREG_YDSTLEN 0x1c88
#define MGAREG_PITCH 0x1c8c
#define MGAREG_YDST 0x1c90
#define MGAREG_YDSTORG 0x1c94
#define MGAREG_YTOP 0x1c98
#define MGAREG_YBOT 0x1c9c
#define MGAREG_CXLEFT 0x1ca0
#define MGAREG_CXRIGHT 0x1ca4
#define MGAREG_FXLEFT 0x1ca8
#define MGAREG_FXRIGHT 0x1cac
#define MGAREG_XDST 0x1cb0
#define MGAREG_DR0 0x1cc0
#define MGAREG_DR1 0x1cc4
#define MGAREG_DR2 0x1cc8
#define MGAREG_DR3 0x1ccc
#define MGAREG_DR4 0x1cd0
#define MGAREG_DR5 0x1cd4
#define MGAREG_DR6 0x1cd8
#define MGAREG_DR7 0x1cdc
#define MGAREG_DR8 0x1ce0
#define MGAREG_DR9 0x1ce4
#define MGAREG_DR10 0x1ce8
#define MGAREG_DR11 0x1cec
#define MGAREG_DR12 0x1cf0
#define MGAREG_DR13 0x1cf4
#define MGAREG_DR14 0x1cf8
#define MGAREG_DR15 0x1cfc
#define MGAREG_SRCORG 0x2cb4
#define MGAREG_DSTORG 0x2cb8
/* add or this to one of the previous "power registers" to start
the drawing engine */
#define MGAREG_EXEC 0x0100
#define MGAREG_FIFOSTATUS 0x1e10
#define MGAREG_STATUS 0x1e14
#define MGAREG_STATUS_VLINEPEN BIT(5)
#define MGAREG_CACHEFLUSH 0x1fff
#define MGAREG_ICLEAR 0x1e18
#define MGAREG_ICLEAR_VLINEICLR BIT(5)
#define MGAREG_IEN 0x1e1c
#define MGAREG_IEN_VLINEIEN BIT(5)
#define MGAREG_VCOUNT 0x1e20
#define MGAREG_Reset 0x1e40
#define MGAREG_OPMODE 0x1e54
/* Warp Registers */
#define MGAREG_WIADDR 0x1dc0
#define MGAREG_WIADDR2 0x1dd8
#define MGAREG_WGETMSB 0x1dc8
#define MGAREG_WVRTXSZ 0x1dcc
#define MGAREG_WACCEPTSEQ 0x1dd4
#define MGAREG_WMISC 0x1e70
#define MGAREG_MEMCTL 0x2e08
/* OPMODE register additives */
#define MGAOPM_DMA_GENERAL (0x00 << 2)
#define MGAOPM_DMA_BLIT (0x01 << 2)
#define MGAOPM_DMA_VECTOR (0x10 << 2)
/* MACCESS register additives */
#define MGAMAC_PW8 0x00
#define MGAMAC_PW16 0x01
#define MGAMAC_PW24 0x03 /* not a typo */
#define MGAMAC_PW32 0x02 /* not a typo */
#define MGAMAC_BYPASS332 0x10000000
#define MGAMAC_NODITHER 0x40000000
#define MGAMAC_DIT555 0x80000000
/* DWGCTL register additives */
/* Lines */
#define MGADWG_LINE_OPEN 0x00
#define MGADWG_AUTOLINE_OPEN 0x01
#define MGADWG_LINE_CLOSE 0x02
#define MGADWG_AUTOLINE_CLOSE 0x03
/* Trapezoids */
#define MGADWG_TRAP 0x04
#define MGADWG_TEXTURE_TRAP 0x06
/* BitBlts */
#define MGADWG_BITBLT 0x08
#define MGADWG_FBITBLT 0x0c
#define MGADWG_ILOAD 0x09
#define MGADWG_ILOAD_SCALE 0x0d
#define MGADWG_ILOAD_FILTER 0x0f
#define MGADWG_ILOAD_HIQH 0x07
#define MGADWG_ILOAD_HIQHV 0x0e
#define MGADWG_IDUMP 0x0a
/* atype access to WRAM */
#define MGADWG_RPL ( 0x00 << 4 )
#define MGADWG_RSTR ( 0x01 << 4 )
#define MGADWG_ZI ( 0x03 << 4 )
#define MGADWG_BLK ( 0x04 << 4 )
#define MGADWG_I ( 0x07 << 4 )
/* specifies whether bit blits are linear or xy */
#define MGADWG_LINEAR ( 0x01 << 7 )
/* z drawing mode. use MGADWG_NOZCMP for always */
#define MGADWG_NOZCMP ( 0x00 << 8 )
#define MGADWG_ZE ( 0x02 << 8 )
#define MGADWG_ZNE ( 0x03 << 8 )
#define MGADWG_ZLT ( 0x04 << 8 )
#define MGADWG_ZLTE ( 0x05 << 8 )
#define MGADWG_GT ( 0x06 << 8 )
#define MGADWG_GTE ( 0x07 << 8 )
/* use this to force colour expansion circuitry to do its stuff */
#define MGADWG_SOLID ( 0x01 << 11 )
/* ar register at zero */
#define MGADWG_ARZERO ( 0x01 << 12 )
#define MGADWG_SGNZERO ( 0x01 << 13 )
#define MGADWG_SHIFTZERO ( 0x01 << 14 )
/* See table on 4-43 for bop ALU operations */
/* See table on 4-44 for translucidity masks */
#define MGADWG_BMONOLEF ( 0x00 << 25 )
#define MGADWG_BMONOWF ( 0x04 << 25 )
#define MGADWG_BPLAN ( 0x01 << 25 )
/* note that if bfcol is specified and you're doing a bitblt, it causes
a fbitblt to be performed, so check that you obey the fbitblt rules */
#define MGADWG_BFCOL ( 0x02 << 25 )
#define MGADWG_BUYUV ( 0x0e << 25 )
#define MGADWG_BU32BGR ( 0x03 << 25 )
#define MGADWG_BU32RGB ( 0x07 << 25 )
#define MGADWG_BU24BGR ( 0x0b << 25 )
#define MGADWG_BU24RGB ( 0x0f << 25 )
#define MGADWG_PATTERN ( 0x01 << 29 )
#define MGADWG_TRANSC ( 0x01 << 30 )
#define MGAREG_MISC_WRITE 0x3c2
#define MGAREG_MISC_READ 0x3cc
#define MGAREG_MEM_MISC_WRITE 0x1fc2
#define MGAREG_MEM_MISC_READ 0x1fcc
#define MGAREG_MISC_IOADSEL (0x1 << 0)
#define MGAREG_MISC_RAMMAPEN (0x1 << 1)
#define MGAREG_MISC_CLKSEL_MASK GENMASK(3, 2)
#define MGAREG_MISC_CLKSEL_VGA25 (0x0 << 2)
#define MGAREG_MISC_CLKSEL_VGA28 (0x1 << 2)
#define MGAREG_MISC_CLKSEL_MGA (0x3 << 2)
#define MGAREG_MISC_VIDEO_DIS (0x1 << 4)
#define MGAREG_MISC_HIGH_PG_SEL (0x1 << 5)
#define MGAREG_MISC_HSYNCPOL BIT(6)
#define MGAREG_MISC_VSYNCPOL BIT(7)
/* MMIO VGA registers */
#define MGAREG_SEQ_INDEX 0x1fc4
#define MGAREG_SEQ_DATA 0x1fc5
#define MGAREG_SEQ0_ASYNCRST BIT(0)
#define MGAREG_SEQ0_SYNCRST BIT(1)
#define MGAREG_SEQ1_SCROFF BIT(5)
#define MGAREG_CRTC_INDEX 0x1fd4
#define MGAREG_CRTC_DATA 0x1fd5
#define MGAREG_CRTC11_VINTCLR BIT(4)
#define MGAREG_CRTC11_VINTEN BIT(5)
#define MGAREG_CRTC11_CRTCPROTECT BIT(7)
#define MGAREG_CRTCEXT_INDEX 0x1fde
#define MGAREG_CRTCEXT_DATA 0x1fdf
#define MGAREG_CRTCEXT0_OFFSET_MASK GENMASK(5, 4)
#define MGAREG_CRTCEXT1_VRSTEN BIT(7)
#define MGAREG_CRTCEXT1_VSYNCOFF BIT(5)
#define MGAREG_CRTCEXT1_HSYNCOFF BIT(4)
#define MGAREG_CRTCEXT1_HRSTEN BIT(3)
#define MGAREG_CRTCEXT3_MGAMODE BIT(7)
#define MGAREG_CRTCEXT3_SCALE_MASK GENMASK(2, 0)
/* Cursor X and Y position */
#define MGA_CURPOSXL 0x3c0c
#define MGA_CURPOSXH 0x3c0d
#define MGA_CURPOSYL 0x3c0e
#define MGA_CURPOSYH 0x3c0f
/* MGA bits for registers PCI_OPTION_REG */
#define MGA1064_OPT_SYS_CLK_PCI ( 0x00 << 0 )
#define MGA1064_OPT_SYS_CLK_PLL ( 0x01 << 0 )
#define MGA1064_OPT_SYS_CLK_EXT ( 0x02 << 0 )
#define MGA1064_OPT_SYS_CLK_MSK ( 0x03 << 0 )
#define MGA1064_OPT_SYS_CLK_DIS ( 0x01 << 2 )
#define MGA1064_OPT_G_CLK_DIV_1 ( 0x01 << 3 )
#define MGA1064_OPT_M_CLK_DIV_1 ( 0x01 << 4 )
#define MGA1064_OPT_SYS_PLL_PDN ( 0x01 << 5 )
#define MGA1064_OPT_VGA_ION ( 0x01 << 8 )
/* MGA registers in PCI config space */
#define PCI_MGA_INDEX 0x44
#define PCI_MGA_DATA 0x48
#define PCI_MGA_OPTION 0x40
#define PCI_MGA_OPTION2 0x50
#define PCI_MGA_OPTION3 0x54
#define PCI_MGA_OPTION_HARDPWMSK BIT(14)
#define RAMDAC_OFFSET 0x3c00
/* TVP3026 direct registers */
#define TVP3026_INDEX 0x00
#define TVP3026_WADR_PAL 0x00
#define TVP3026_COL_PAL 0x01
#define TVP3026_PIX_RD_MSK 0x02
#define TVP3026_RADR_PAL 0x03
#define TVP3026_CUR_COL_ADDR 0x04
#define TVP3026_CUR_COL_DATA 0x05
#define TVP3026_DATA 0x0a
#define TVP3026_CUR_RAM 0x0b
#define TVP3026_CUR_XLOW 0x0c
#define TVP3026_CUR_XHI 0x0d
#define TVP3026_CUR_YLOW 0x0e
#define TVP3026_CUR_YHI 0x0f
/* TVP3026 indirect registers */
#define TVP3026_SILICON_REV 0x01
#define TVP3026_CURSOR_CTL 0x06
#define TVP3026_LATCH_CTL 0x0f
#define TVP3026_TRUE_COLOR_CTL 0x18
#define TVP3026_MUX_CTL 0x19
#define TVP3026_CLK_SEL 0x1a
#define TVP3026_PAL_PAGE 0x1c
#define TVP3026_GEN_CTL 0x1d
#define TVP3026_MISC_CTL 0x1e
#define TVP3026_GEN_IO_CTL 0x2a
#define TVP3026_GEN_IO_DATA 0x2b
#define TVP3026_PLL_ADDR 0x2c
#define TVP3026_PIX_CLK_DATA 0x2d
#define TVP3026_MEM_CLK_DATA 0x2e
#define TVP3026_LOAD_CLK_DATA 0x2f
#define TVP3026_KEY_RED_LOW 0x32
#define TVP3026_KEY_RED_HI 0x33
#define TVP3026_KEY_GREEN_LOW 0x34
#define TVP3026_KEY_GREEN_HI 0x35
#define TVP3026_KEY_BLUE_LOW 0x36
#define TVP3026_KEY_BLUE_HI 0x37
#define TVP3026_KEY_CTL 0x38
#define TVP3026_MCLK_CTL 0x39
#define TVP3026_SENSE_TEST 0x3a
#define TVP3026_TEST_DATA 0x3b
#define TVP3026_CRC_LSB 0x3c
#define TVP3026_CRC_MSB 0x3d
#define TVP3026_CRC_CTL 0x3e
#define TVP3026_ID 0x3f
#define TVP3026_RESET 0xff
/* MGA1064 DAC Register file */
/* MGA1064 direct registers */
#define MGA1064_INDEX 0x00
#define MGA1064_WADR_PAL 0x00
#define MGA1064_SPAREREG 0x00
#define MGA1064_COL_PAL 0x01
#define MGA1064_PIX_RD_MSK 0x02
#define MGA1064_RADR_PAL 0x03
#define MGA1064_DATA 0x0a
#define MGA1064_CUR_XLOW 0x0c
#define MGA1064_CUR_XHI 0x0d
#define MGA1064_CUR_YLOW 0x0e
#define MGA1064_CUR_YHI 0x0f
/* MGA1064 indirect registers */
#define MGA1064_DVI_PIPE_CTL 0x03
#define MGA1064_CURSOR_BASE_ADR_LOW 0x04
#define MGA1064_CURSOR_BASE_ADR_HI 0x05
#define MGA1064_CURSOR_CTL 0x06
#define MGA1064_CURSOR_COL0_RED 0x08
#define MGA1064_CURSOR_COL0_GREEN 0x09
#define MGA1064_CURSOR_COL0_BLUE 0x0a
#define MGA1064_CURSOR_COL1_RED 0x0c
#define MGA1064_CURSOR_COL1_GREEN 0x0d
#define MGA1064_CURSOR_COL1_BLUE 0x0e
#define MGA1064_CURSOR_COL2_RED 0x010
#define MGA1064_CURSOR_COL2_GREEN 0x011
#define MGA1064_CURSOR_COL2_BLUE 0x012
#define MGA1064_VREF_CTL 0x018
#define MGA1064_MUL_CTL 0x19
#define MGA1064_MUL_CTL_8bits 0x0
#define MGA1064_MUL_CTL_15bits 0x01
#define MGA1064_MUL_CTL_16bits 0x02
#define MGA1064_MUL_CTL_24bits 0x03
#define MGA1064_MUL_CTL_32bits 0x04
#define MGA1064_MUL_CTL_2G8V16bits 0x05
#define MGA1064_MUL_CTL_G16V16bits 0x06
#define MGA1064_MUL_CTL_32_24bits 0x07
#define MGA1064_PIX_CLK_CTL 0x1a
#define MGA1064_PIX_CLK_CTL_CLK_DIS ( 0x01 << 2 )
#define MGA1064_PIX_CLK_CTL_CLK_POW_DOWN ( 0x01 << 3 )
#define MGA1064_PIX_CLK_CTL_SEL_PCI ( 0x00 << 0 )
#define MGA1064_PIX_CLK_CTL_SEL_PLL ( 0x01 << 0 )
#define MGA1064_PIX_CLK_CTL_SEL_EXT ( 0x02 << 0 )
#define MGA1064_PIX_CLK_CTL_SEL_MSK ( 0x03 << 0 )
#define MGA1064_GEN_CTL 0x1d
#define MGA1064_GEN_CTL_SYNC_ON_GREEN_DIS (0x01 << 5)
#define MGA1064_MISC_CTL 0x1e
#define MGA1064_MISC_CTL_DAC_EN ( 0x01 << 0 )
#define MGA1064_MISC_CTL_VGA ( 0x01 << 1 )
#define MGA1064_MISC_CTL_DIS_CON ( 0x03 << 1 )
#define MGA1064_MISC_CTL_MAFC ( 0x02 << 1 )
#define MGA1064_MISC_CTL_VGA8 ( 0x01 << 3 )
#define MGA1064_MISC_CTL_DAC_RAM_CS ( 0x01 << 4 )
#define MGA1064_GEN_IO_CTL2 0x29
#define MGA1064_GEN_IO_CTL 0x2a
#define MGA1064_GEN_IO_DATA 0x2b
#define MGA1064_SYS_PLL_M 0x2c
#define MGA1064_SYS_PLL_N 0x2d
#define MGA1064_SYS_PLL_P 0x2e
#define MGA1064_SYS_PLL_STAT 0x2f
#define MGA1064_REMHEADCTL 0x30
#define MGA1064_REMHEADCTL_CLKDIS ( 0x01 << 0 )
#define MGA1064_REMHEADCTL_CLKSL_OFF ( 0x00 << 1 )
#define MGA1064_REMHEADCTL_CLKSL_PLL ( 0x01 << 1 )
#define MGA1064_REMHEADCTL_CLKSL_PCI ( 0x02 << 1 )
#define MGA1064_REMHEADCTL_CLKSL_MSK ( 0x03 << 1 )
#define MGA1064_REMHEADCTL2 0x31
#define MGA1064_ZOOM_CTL 0x38
#define MGA1064_SENSE_TST 0x3a
#define MGA1064_CRC_LSB 0x3c
#define MGA1064_CRC_MSB 0x3d
#define MGA1064_CRC_CTL 0x3e
#define MGA1064_COL_KEY_MSK_LSB 0x40
#define MGA1064_COL_KEY_MSK_MSB 0x41
#define MGA1064_COL_KEY_LSB 0x42
#define MGA1064_COL_KEY_MSB 0x43
#define MGA1064_PIX_PLLA_M 0x44
#define MGA1064_PIX_PLLA_N 0x45
#define MGA1064_PIX_PLLA_P 0x46
#define MGA1064_PIX_PLLB_M 0x48
#define MGA1064_PIX_PLLB_N 0x49
#define MGA1064_PIX_PLLB_P 0x4a
#define MGA1064_PIX_PLLC_M 0x4c
#define MGA1064_PIX_PLLC_N 0x4d
#define MGA1064_PIX_PLLC_P 0x4e
#define MGA1064_PIX_PLL_STAT 0x4f
/*Added for G450 dual head*/
#define MGA1064_VID_PLL_STAT 0x8c
#define MGA1064_VID_PLL_P 0x8D
#define MGA1064_VID_PLL_M 0x8E
#define MGA1064_VID_PLL_N 0x8F
/* Modified PLL for G200 Winbond (G200WB) */
#define MGA1064_WB_PIX_PLLC_M 0xb7
#define MGA1064_WB_PIX_PLLC_N 0xb6
#define MGA1064_WB_PIX_PLLC_P 0xb8
/* Modified PLL for G200 Maxim (G200EV) */
#define MGA1064_EV_PIX_PLLC_M 0xb6
#define MGA1064_EV_PIX_PLLC_N 0xb7
#define MGA1064_EV_PIX_PLLC_P 0xb8
/* Modified PLL for G200 EH */
#define MGA1064_EH_PIX_PLLC_M 0xb6
#define MGA1064_EH_PIX_PLLC_N 0xb7
#define MGA1064_EH_PIX_PLLC_P 0xb8
/* Modified PLL for G200 Maxim (G200ER) */
#define MGA1064_ER_PIX_PLLC_M 0xb7
#define MGA1064_ER_PIX_PLLC_N 0xb6
#define MGA1064_ER_PIX_PLLC_P 0xb8
#define MGA1064_DISP_CTL 0x8a
#define MGA1064_DISP_CTL_DAC1OUTSEL_MASK 0x01
#define MGA1064_DISP_CTL_DAC1OUTSEL_DIS 0x00
#define MGA1064_DISP_CTL_DAC1OUTSEL_EN 0x01
#define MGA1064_DISP_CTL_DAC2OUTSEL_MASK (0x03 << 2)
#define MGA1064_DISP_CTL_DAC2OUTSEL_DIS 0x00
#define MGA1064_DISP_CTL_DAC2OUTSEL_CRTC1 (0x01 << 2)
#define MGA1064_DISP_CTL_DAC2OUTSEL_CRTC2 (0x02 << 2)
#define MGA1064_DISP_CTL_DAC2OUTSEL_TVE (0x03 << 2)
#define MGA1064_DISP_CTL_PANOUTSEL_MASK (0x03 << 5)
#define MGA1064_DISP_CTL_PANOUTSEL_DIS 0x00
#define MGA1064_DISP_CTL_PANOUTSEL_CRTC1 (0x01 << 5)
#define MGA1064_DISP_CTL_PANOUTSEL_CRTC2RGB (0x02 << 5)
#define MGA1064_DISP_CTL_PANOUTSEL_CRTC2656 (0x03 << 5)
#define MGA1064_SYNC_CTL 0x8b
#define MGA1064_PWR_CTL 0xa0
#define MGA1064_PWR_CTL_DAC2_EN (0x01 << 0)
#define MGA1064_PWR_CTL_VID_PLL_EN (0x01 << 1)
#define MGA1064_PWR_CTL_PANEL_EN (0x01 << 2)
#define MGA1064_PWR_CTL_RFIFO_EN (0x01 << 3)
#define MGA1064_PWR_CTL_CFIFO_EN (0x01 << 4)
#define MGA1064_PAN_CTL 0xa2
/* Using crtc2 */
#define MGAREG2_C2CTL 0x10
#define MGAREG2_C2HPARAM 0x14
#define MGAREG2_C2HSYNC 0x18
#define MGAREG2_C2VPARAM 0x1c
#define MGAREG2_C2VSYNC 0x20
#define MGAREG2_C2STARTADD0 0x28
#define MGAREG2_C2OFFSET 0x40
#define MGAREG2_C2DATACTL 0x4c
#define MGAREG_C2CTL 0x3c10
#define MGAREG_C2CTL_C2_EN 0x01
#define MGAREG_C2_HIPRILVL_M (0x07 << 4)
#define MGAREG_C2_MAXHIPRI_M (0x07 << 8)
#define MGAREG_C2CTL_PIXCLKSEL_MASK (0x03 << 1)
#define MGAREG_C2CTL_PIXCLKSELH_MASK (0x01 << 14)
#define MGAREG_C2CTL_PIXCLKSEL_PCICLK 0x00
#define MGAREG_C2CTL_PIXCLKSEL_VDOCLK (0x01 << 1)
#define MGAREG_C2CTL_PIXCLKSEL_PIXELPLL (0x02 << 1)
#define MGAREG_C2CTL_PIXCLKSEL_VIDEOPLL (0x03 << 1)
#define MGAREG_C2CTL_PIXCLKSEL_VDCLK (0x01 << 14)
#define MGAREG_C2CTL_PIXCLKSEL_CRISTAL (0x01 << 1) | (0x01 << 14)
#define MGAREG_C2CTL_PIXCLKSEL_SYSTEMPLL (0x02 << 1) | (0x01 << 14)
#define MGAREG_C2CTL_PIXCLKDIS_MASK (0x01 << 3)
#define MGAREG_C2CTL_PIXCLKDIS_DISABLE (0x01 << 3)
#define MGAREG_C2CTL_CRTCDACSEL_MASK (0x01 << 20)
#define MGAREG_C2CTL_CRTCDACSEL_CRTC1 0x00
#define MGAREG_C2CTL_CRTCDACSEL_CRTC2 (0x01 << 20)
#define MGAREG_C2HPARAM 0x3c14
#define MGAREG_C2HSYNC 0x3c18
#define MGAREG_C2VPARAM 0x3c1c
#define MGAREG_C2VSYNC 0x3c20
#define MGAREG_C2STARTADD0 0x3c28
#define MGAREG_C2OFFSET 0x3c40
#define MGAREG_C2DATACTL 0x3c4c
/* video register */
#define MGAREG_BESA1C3ORG 0x3d60
#define MGAREG_BESA1CORG 0x3d10
#define MGAREG_BESA1ORG 0x3d00
#define MGAREG_BESCTL 0x3d20
#define MGAREG_BESGLOBCTL 0x3dc0
#define MGAREG_BESHCOORD 0x3d28
#define MGAREG_BESHISCAL 0x3d30
#define MGAREG_BESHSRCEND 0x3d3c
#define MGAREG_BESHSRCLST 0x3d50
#define MGAREG_BESHSRCST 0x3d38
#define MGAREG_BESLUMACTL 0x3d40
#define MGAREG_BESPITCH 0x3d24
#define MGAREG_BESV1SRCLST 0x3d54
#define MGAREG_BESV1WGHT 0x3d48
#define MGAREG_BESVCOORD 0x3d2c
#define MGAREG_BESVISCAL 0x3d34
/* texture engine registers */
#define MGAREG_TMR0 0x2c00
#define MGAREG_TMR1 0x2c04
#define MGAREG_TMR2 0x2c08
#define MGAREG_TMR3 0x2c0c
#define MGAREG_TMR4 0x2c10
#define MGAREG_TMR5 0x2c14
#define MGAREG_TMR6 0x2c18
#define MGAREG_TMR7 0x2c1c
#define MGAREG_TMR8 0x2c20
#define MGAREG_TEXORG 0x2c24
#define MGAREG_TEXWIDTH 0x2c28
#define MGAREG_TEXHEIGHT 0x2c2c
#define MGAREG_TEXCTL 0x2c30
# define MGA_TW4 (0x00000000)
# define MGA_TW8 (0x00000001)
# define MGA_TW15 (0x00000002)
# define MGA_TW16 (0x00000003)
# define MGA_TW12 (0x00000004)
# define MGA_TW32 (0x00000006)
# define MGA_TW8A (0x00000007)
# define MGA_TW8AL (0x00000008)
# define MGA_TW422 (0x0000000A)
# define MGA_TW422UYVY (0x0000000B)
# define MGA_PITCHLIN (0x00000100)
# define MGA_NOPERSPECTIVE (0x00200000)
# define MGA_TAKEY (0x02000000)
# define MGA_TAMASK (0x04000000)
# define MGA_CLAMPUV (0x18000000)
# define MGA_TEXMODULATE (0x20000000)
#define MGAREG_TEXCTL2 0x2c3c
# define MGA_G400_TC2_MAGIC (0x00008000)
# define MGA_TC2_DECALBLEND (0x00000001)
# define MGA_TC2_IDECAL (0x00000002)
# define MGA_TC2_DECALDIS (0x00000004)
# define MGA_TC2_CKSTRANSDIS (0x00000010)
# define MGA_TC2_BORDEREN (0x00000020)
# define MGA_TC2_SPECEN (0x00000040)
# define MGA_TC2_DUALTEX (0x00000080)
# define MGA_TC2_TABLEFOG (0x00000100)
# define MGA_TC2_BUMPMAP (0x00000200)
# define MGA_TC2_SELECT_TMU1 (0x80000000)
#define MGAREG_TEXTRANS 0x2c34
#define MGAREG_TEXTRANSHIGH 0x2c38
#define MGAREG_TEXFILTER 0x2c58
# define MGA_MIN_NRST (0x00000000)
# define MGA_MIN_BILIN (0x00000002)
# define MGA_MIN_ANISO (0x0000000D)
# define MGA_MAG_NRST (0x00000000)
# define MGA_MAG_BILIN (0x00000020)
# define MGA_FILTERALPHA (0x00100000)
#define MGAREG_ALPHASTART 0x2c70
#define MGAREG_ALPHAXINC 0x2c74
#define MGAREG_ALPHAYINC 0x2c78
#define MGAREG_ALPHACTRL 0x2c7c
# define MGA_SRC_ZERO (0x00000000)
# define MGA_SRC_ONE (0x00000001)
# define MGA_SRC_DST_COLOR (0x00000002)
# define MGA_SRC_ONE_MINUS_DST_COLOR (0x00000003)
# define MGA_SRC_ALPHA (0x00000004)
# define MGA_SRC_ONE_MINUS_SRC_ALPHA (0x00000005)
# define MGA_SRC_DST_ALPHA (0x00000006)
# define MGA_SRC_ONE_MINUS_DST_ALPHA (0x00000007)
# define MGA_SRC_SRC_ALPHA_SATURATE (0x00000008)
# define MGA_SRC_BLEND_MASK (0x0000000f)
# define MGA_DST_ZERO (0x00000000)
# define MGA_DST_ONE (0x00000010)
# define MGA_DST_SRC_COLOR (0x00000020)
# define MGA_DST_ONE_MINUS_SRC_COLOR (0x00000030)
# define MGA_DST_SRC_ALPHA (0x00000040)
# define MGA_DST_ONE_MINUS_SRC_ALPHA (0x00000050)
# define MGA_DST_DST_ALPHA (0x00000060)
# define MGA_DST_ONE_MINUS_DST_ALPHA (0x00000070)
# define MGA_DST_BLEND_MASK (0x00000070)
# define MGA_ALPHACHANNEL (0x00000100)
# define MGA_VIDEOALPHA (0x00000200)
# define MGA_DIFFUSEDALPHA (0x01000000)
# define MGA_MODULATEDALPHA (0x02000000)
#define MGAREG_TDUALSTAGE0 (0x2CF8)
#define MGAREG_TDUALSTAGE1 (0x2CFC)
# define MGA_TDS_COLOR_ARG2_DIFFUSE (0x00000000)
# define MGA_TDS_COLOR_ARG2_SPECULAR (0x00000001)
# define MGA_TDS_COLOR_ARG2_FCOL (0x00000002)
# define MGA_TDS_COLOR_ARG2_PREVSTAGE (0x00000003)
# define MGA_TDS_COLOR_ALPHA_DIFFUSE (0x00000000)
# define MGA_TDS_COLOR_ALPHA_FCOL (0x00000004)
# define MGA_TDS_COLOR_ALPHA_CURRTEX (0x00000008)
# define MGA_TDS_COLOR_ALPHA_PREVTEX (0x0000000c)
# define MGA_TDS_COLOR_ALPHA_PREVSTAGE (0x00000010)
# define MGA_TDS_COLOR_ARG1_REPLICATEALPHA (0x00000020)
# define MGA_TDS_COLOR_ARG1_INV (0x00000040)
# define MGA_TDS_COLOR_ARG2_REPLICATEALPHA (0x00000080)
# define MGA_TDS_COLOR_ARG2_INV (0x00000100)
# define MGA_TDS_COLOR_ALPHA1INV (0x00000200)
# define MGA_TDS_COLOR_ALPHA2INV (0x00000400)
# define MGA_TDS_COLOR_ARG1MUL_ALPHA1 (0x00000800)
# define MGA_TDS_COLOR_ARG2MUL_ALPHA2 (0x00001000)
# define MGA_TDS_COLOR_ARG1ADD_MULOUT (0x00002000)
# define MGA_TDS_COLOR_ARG2ADD_MULOUT (0x00004000)
# define MGA_TDS_COLOR_MODBRIGHT_2X (0x00008000)
# define MGA_TDS_COLOR_MODBRIGHT_4X (0x00010000)
# define MGA_TDS_COLOR_ADD_SUB (0x00000000)
# define MGA_TDS_COLOR_ADD_ADD (0x00020000)
# define MGA_TDS_COLOR_ADD2X (0x00040000)
# define MGA_TDS_COLOR_ADDBIAS (0x00080000)
# define MGA_TDS_COLOR_BLEND (0x00100000)
# define MGA_TDS_COLOR_SEL_ARG1 (0x00000000)
# define MGA_TDS_COLOR_SEL_ARG2 (0x00200000)
# define MGA_TDS_COLOR_SEL_ADD (0x00400000)
# define MGA_TDS_COLOR_SEL_MUL (0x00600000)
# define MGA_TDS_ALPHA_ARG1_INV (0x00800000)
# define MGA_TDS_ALPHA_ARG2_DIFFUSE (0x00000000)
# define MGA_TDS_ALPHA_ARG2_FCOL (0x01000000)
# define MGA_TDS_ALPHA_ARG2_PREVTEX (0x02000000)
# define MGA_TDS_ALPHA_ARG2_PREVSTAGE (0x03000000)
# define MGA_TDS_ALPHA_ARG2_INV (0x04000000)
# define MGA_TDS_ALPHA_ADD (0x08000000)
# define MGA_TDS_ALPHA_ADDBIAS (0x10000000)
# define MGA_TDS_ALPHA_ADD2X (0x20000000)
# define MGA_TDS_ALPHA_SEL_ARG1 (0x00000000)
# define MGA_TDS_ALPHA_SEL_ARG2 (0x40000000)
# define MGA_TDS_ALPHA_SEL_ADD (0x80000000)
# define MGA_TDS_ALPHA_SEL_MUL (0xc0000000)
#define MGAREG_DWGSYNC 0x2c4c
#define MGAREG_AGP_PLL 0x1e4c
#define MGA_AGP2XPLL_ENABLE 0x1
#define MGA_AGP2XPLL_DISABLE 0x0
#endif
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <drm/drm_atomic_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_ddc.h"
#include "mgag200_drv.h"
static const struct drm_encoder_funcs mgag200_dac_encoder_funcs = {
.destroy = drm_encoder_cleanup
};
static const struct drm_connector_helper_funcs mgag200_vga_connector_helper_funcs = {
.get_modes = drm_connector_helper_get_modes,
.detect_ctx = drm_connector_helper_detect_from_ddc
};
static const struct drm_connector_funcs mgag200_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
};
int mgag200_vga_output_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_crtc *crtc = &mdev->crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
struct i2c_adapter *ddc;
int ret;
encoder = &mdev->output.vga.encoder;
ret = drm_encoder_init(dev, encoder, &mgag200_dac_encoder_funcs,
DRM_MODE_ENCODER_DAC, NULL);
if (ret) {
drm_err(dev, "drm_encoder_init() failed: %d\n", ret);
return ret;
}
encoder->possible_crtcs = drm_crtc_mask(crtc);
ddc = mgag200_ddc_create(mdev);
if (IS_ERR(ddc)) {
ret = PTR_ERR(ddc);
drm_err(dev, "failed to add DDC bus: %d\n", ret);
return ret;
}
connector = &mdev->output.vga.connector;
ret = drm_connector_init_with_ddc(dev, connector,
&mgag200_vga_connector_funcs,
DRM_MODE_CONNECTOR_VGA, ddc);
if (ret) {
drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret);
return ret;
}
drm_connector_helper_add(connector, &mgag200_vga_connector_helper_funcs);
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
ret = drm_connector_attach_encoder(connector, encoder);
if (ret) {
drm_err(dev, "drm_connector_attach_encoder() failed: %d\n", ret);
return ret;
}
return 0;
}
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_ddc.h"
#include "mgag200_drv.h"
static void mgag200_vga_bmc_encoder_atomic_disable(struct drm_encoder *encoder,
struct drm_atomic_commit *state)
{
struct mga_device *mdev = to_mga_device(encoder->dev);
if (mdev->info->sync_bmc)
mgag200_bmc_stop_scanout(mdev);
}
static void mgag200_vga_bmc_encoder_atomic_enable(struct drm_encoder *encoder,
struct drm_atomic_commit *state)
{
struct mga_device *mdev = to_mga_device(encoder->dev);
if (mdev->info->sync_bmc)
mgag200_bmc_start_scanout(mdev);
}
static int mgag200_vga_bmc_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *new_crtc_state,
struct drm_connector_state *new_connector_state)
{
struct mga_device *mdev = to_mga_device(encoder->dev);
struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
new_mgag200_crtc_state->set_vidrst = mdev->info->sync_bmc;
return 0;
}
static const struct drm_encoder_helper_funcs mgag200_dac_encoder_helper_funcs = {
.atomic_disable = mgag200_vga_bmc_encoder_atomic_disable,
.atomic_enable = mgag200_vga_bmc_encoder_atomic_enable,
.atomic_check = mgag200_vga_bmc_encoder_atomic_check,
};
static const struct drm_encoder_funcs mgag200_dac_encoder_funcs = {
.destroy = drm_encoder_cleanup
};
static int mgag200_vga_bmc_connector_helper_get_modes(struct drm_connector *connector)
{
struct mga_device *mdev = to_mga_device(connector->dev);
const struct mgag200_device_info *minfo = mdev->info;
int count;
count = drm_connector_helper_get_modes(connector);
if (!count) {
/*
* There's no EDID data without a connected monitor. Set BMC-
* compatible modes in this case. The XGA default resolution
* should work well for all BMCs.
*/
count = drm_add_modes_noedid(connector, minfo->max_hdisplay, minfo->max_vdisplay);
if (count)
drm_set_preferred_mode(connector, 1024, 768);
}
return count;
}
/*
* There's no monitor connected if the DDC did not return an EDID. Still
* return 'connected' as there's always a BMC. Incrementing the connector's
* epoch counter triggers an update of the related properties.
*/
static int mgag200_vga_bmc_connector_helper_detect_ctx(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx,
bool force)
{
enum drm_connector_status old_status, status;
if (connector->edid_blob_ptr)
old_status = connector_status_connected;
else
old_status = connector_status_disconnected;
status = drm_connector_helper_detect_from_ddc(connector, ctx, force);
if (status != old_status)
++connector->epoch_counter;
return connector_status_connected;
}
static const struct drm_connector_helper_funcs mgag200_vga_connector_helper_funcs = {
.get_modes = mgag200_vga_bmc_connector_helper_get_modes,
.detect_ctx = mgag200_vga_bmc_connector_helper_detect_ctx,
};
static const struct drm_connector_funcs mgag200_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
};
int mgag200_vga_bmc_output_init(struct mga_device *mdev)
{
struct drm_device *dev = &mdev->base;
struct drm_crtc *crtc = &mdev->crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
struct i2c_adapter *ddc;
int ret;
encoder = &mdev->output.vga.encoder;
ret = drm_encoder_init(dev, encoder, &mgag200_dac_encoder_funcs,
DRM_MODE_ENCODER_DAC, NULL);
if (ret) {
drm_err(dev, "drm_encoder_init() failed: %d\n", ret);
return ret;
}
drm_encoder_helper_add(encoder, &mgag200_dac_encoder_helper_funcs);
encoder->possible_crtcs = drm_crtc_mask(crtc);
ddc = mgag200_ddc_create(mdev);
if (IS_ERR(ddc)) {
ret = PTR_ERR(ddc);
drm_err(dev, "failed to add DDC bus: %d\n", ret);
return ret;
}
connector = &mdev->output.vga.connector;
ret = drm_connector_init_with_ddc(dev, connector,
&mgag200_vga_connector_funcs,
DRM_MODE_CONNECTOR_VGA, ddc);
if (ret) {
drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret);
return ret;
}
drm_connector_helper_add(connector, &mgag200_vga_connector_helper_funcs);
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
ret = drm_connector_attach_encoder(connector, encoder);
if (ret) {
drm_err(dev, "drm_connector_attach_encoder() failed: %d\n", ret);
return ret;
}
return 0;
}