diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-04 05:52:08 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-04 05:52:08 +0300 |
commit | b44f2fd87919b5ae6e1756d4c7ba2cbba22238e1 (patch) | |
tree | 01ce17e44375c3f7707640bb44d6e012bab878c4 /drivers/gpu/drm/mgag200 | |
parent | 12b68040a5e468068fd7f4af1150eab8f6e96235 (diff) | |
parent | 5493ee1919eae4f49d62276cf5986b7f7c7aa8f6 (diff) | |
download | linux-b44f2fd87919b5ae6e1756d4c7ba2cbba22238e1.tar.xz |
Merge tag 'drm-next-2022-08-03' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"Highlights:
- New driver for logicvc - which is a display IP core.
- EDID parser rework to add new extensions
- fbcon scrolling improvements
- i915 has some more DG2 work but not enabled by default, but should
have enough features for userspace to work now.
Otherwise it's lots of work all over the place. Detailed summary:
New driver:
- logicvc
vfio:
- use aperture API
core:
- of: Add data-lane helpers and convert drivers
- connector: Remove deprecated ida_simple_get()
media:
- Add various RGB666 and RGB888 format constants
panel:
- Add HannStar HSD101PWW
- Add ETML0700Y5DHA
dma-buf:
- add sync-file API
- set dma mask for udmabuf devices
fbcon:
- Improve scrolling performance
- Sanitize input
fbdev:
- device unregistering fixes
- vesa: Support COMPILE_TEST
- Disable firmware-device registration when first native driver loads
aperture:
- fix segfault during hot-unplug
- export for use with other subsystems
client:
- use driver validated modes
dp:
- aux: make probing more reliable
- mst: Read extended DPCD capabilities during system resume
- Support waiting for HDP signal
- Port-validation fixes
edid:
- CEA data-block iterators
- struct drm_edid introduction
- implement HF-EEODB extension
gem:
- don't use fb format non-existing planes
probe-helper:
- use 640x480 as displayport fallback
scheduler:
- don't kill jobs in interrupt context
bridge:
- Add support for i.MX8qxp and i.MX8qm
- lots of fixes/cleanups
- Add TI-DLPC3433
- fy07024di26a30d: Optional GPIO reset
- ldb: Add reg and reg-name properties to bindings, Kconfig fixes
- lt9611: Fix display sensing;
- tc358767: DSI/DPI refactoring and DSI-to-eDP support, DSI lane handling
- tc358775: Fix clock settings
- ti-sn65dsi83: Allow GPIO to sleep
- adv7511: I2C fixes
- anx7625: Fix error handling; DPI fixes; Implement HDP timeout via callback
- fsl-ldb: Drop DE flip
- ti-sn65dsi86: Convert to atomic modesetting
amdgpu:
- use atomic fence helpers in DM
- fix VRAM address calculations
- export CRTC bpc via debugfs
- Initial devcoredump support
- Enable high priority gfx queue on asics which support it
- Adjust GART size on newer APUs for S/G display
- Soft reset for GFX 11 / SDMA 6
- Add gfxoff status query for vangogh
- Fix timestamps for cursor only commits
- Adjust GART size on newer APUs for S/G display
- fix buddy memory corruption
amdkfd:
- MMU notifier fixes
- P2P DMA support using dma-buf
- Add available memory IOCTL
- HMM profiler support
- Simplify GPUVM validation
- Unified memory for CWSR save/restore area
i915:
- General driver clean-up
- DG2 enabling (still under force probe)
- DG2 small BAR memory support
- HuC loading support
- DG2 workarounds
- DG2/ATS-M device IDs added
- Ponte Vecchio prep work and new blitter engines
- add Meteorlake support
- Fix sparse warnings
- DMC MMIO range checks
- Audio related fixes
- Runtime PM fixes
- PSR fixes
- Media freq factor and per-gt enhancements
- DSI fixes for ICL+
- Disable DMC flip queue handlers
- ADL_P voltage swing updates
- Use more the VBT for panel information
- Fix on Type-C ports with TBT mode
- Improve fastset and allow seamless M/N changes
- Accept more fixed modes with VRR/DMRRS panels
- Disable connector polling for a headless SKU
- ADL-S display PLL w/a
- Enable THP on Icelake and beyond
- Fix i915_gem_object_ggtt_pin_ww regression on old platforms
- Expose per tile media freq factor in sysfs
- Fix dma_resv fence handling in multi-batch execbuf
- Improve on suspend / resume time with VT-d enabled
- export CRTC bpc settings via debugfs
msm:
- gpu: a619 support
- gpu: Fix for unclocked GMU register access
- gpu: Devcore dump enhancements
- client utilization via fdinfo support
- fix fence rollover issue
- gem: Lockdep false-positive warning fix
- gem: Switch to pfn mappings
- WB support on sc7180
- dp: dropped custom bulk clock implementation
- fix link retraining on resolution change
- hdmi: dropped obsolete GPIO support
tegra:
- context isolation for host1x engines
- tegra234 soc support
mediatek:
- add vdosys0/1 for mt8195
- add MT8195 dp_intf driver
exynos:
- Fix resume function issue of exynos decon driver by calling
clk_disable_unprepare() properly if clk_prepare_enable() failed.
nouveau:
- set of misc fixes/cleanups
- display cleanups
gma500:
- Cleanup connector I2C handling
hyperv:
- Unify VRAM allocation of Gen1 and Gen2
meson:
- Support YUV422 output; Refcount fixes
mgag200:
- Support damage clipping
- Support gamma handling
- Protect concurrent HW access
- Fixes to connector
- Store model-specific limits in device-info structure
- fix PCI register init
panfrost:
- Valhall support
r128:
- Fix bit-shift overflow
rockchip:
- Locking fixes in error path
ssd130x:
- Fix built-in linkage
udl:
- Always advertize VGA connector
ast:
- Support multiple outputs
- fix black screen on resume
sun4i:
- HDMI PHY cleanups
vc4:
- Add support for BCM2711
vkms:
- Allocate output buffer with vmalloc()
mcde:
- Fix ref-count leak
mxsfb/lcdif:
- Support i.MX8MP LCD controller
stm/ltdc:
- Support dynamic Z order
- Support mirroring
ingenic:
- Fix display at maximum resolution"
* tag 'drm-next-2022-08-03' of git://anongit.freedesktop.org/drm/drm: (1480 commits)
drm/amd/display: Fix a compilation failure on PowerPC caused by FPU code
drm/amdgpu: enable support for psp 13.0.4 block
drm/amdgpu: add files for PSP 13.0.4
drm/amdgpu: add header files for MP 13.0.4
drm/amdgpu: correct RLC_RLCS_BOOTLOAD_STATUS offset and index
drm/amdgpu: send msg to IMU for the front-door loading
drm/amdkfd: use time_is_before_jiffies(a + b) to replace "jiffies - a > b"
drm/amdgpu: fix hive reference leak when reflecting psp topology info
drm/amd/pm: enable GFX ULV feature support for SMU13.0.0
drm/amd/pm: update driver if header for SMU 13.0.0
drm/amdgpu: move mes self test after drm sched re-started
drm/amdgpu: drop non-necessary call trace dump
drm/amdgpu: enable VCN cg and JPEG cg/pg
drm/amdgpu: vcn_4_0_2 video codec query
drm/amdgpu: add VCN_4_0_2 firmware support
drm/amdgpu: add VCN function in NBIO v7.7
drm/amdgpu: fix a vcn4 boot poll bug in emulation mode
drm/amd/amdgpu: add memory training support for PSP_V13
drm/amdkfd: remove an unnecessary amdgpu_bo_ref
drm/amd/pm: Add get_gfx_off_status interface for yellow carp
...
Diffstat (limited to 'drivers/gpu/drm/mgag200')
-rw-r--r-- | drivers/gpu/drm/mgag200/Makefile | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.c | 381 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.h | 148 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200.c | 201 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200eh.c | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200eh3.c | 51 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200er.c | 46 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200ev.c | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200ew3.c | 60 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200se.c | 130 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_g200wb.c | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_i2c.c | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_mm.c | 116 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_mode.c | 454 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_pll.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_reg.h | 2 |
16 files changed, 1141 insertions, 683 deletions
diff --git a/drivers/gpu/drm/mgag200/Makefile b/drivers/gpu/drm/mgag200/Makefile index d4f766522483..89558549c3af 100644 --- a/drivers/gpu/drm/mgag200/Makefile +++ b/drivers/gpu/drm/mgag200/Makefile @@ -1,4 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-only -mgag200-y := mgag200_drv.o mgag200_i2c.o mgag200_mm.o mgag200_mode.o mgag200_pll.o +mgag200-y := \ + mgag200_drv.o \ + mgag200_g200.o \ + mgag200_g200eh.o \ + mgag200_g200eh3.o \ + mgag200_g200er.o \ + mgag200_g200ev.o \ + mgag200_g200ew3.o \ + mgag200_g200se.o \ + mgag200_g200wb.o \ + mgag200_i2c.o \ + mgag200_mode.o \ + mgag200_pll.o obj-$(CONFIG_DRM_MGAG200) += mgag200.o diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 08839460606f..251a1bb648cc 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -8,7 +8,6 @@ #include <linux/module.h> #include <linux/pci.h> -#include <linux/vmalloc.h> #include <drm/drm_aperture.h> #include <drm/drm_drv.h> @@ -24,6 +23,65 @@ 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 */ @@ -46,263 +104,87 @@ static const struct drm_driver mgag200_driver = { * DRM device */ -static bool mgag200_has_sgram(struct mga_device *mdev) +resource_size_t mgag200_device_probe_vram(struct mga_device *mdev) { - struct drm_device *dev = &mdev->base; - struct pci_dev *pdev = to_pci_dev(dev->dev); - u32 option; - int ret; - - ret = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option); - if (drm_WARN(dev, ret, "failed to read PCI config dword: %d\n", ret)) - return false; - - return !!(option & PCI_MGA_OPTION_HARDPWMSK); + return mgag200_probe_vram(mdev->vram, resource_size(mdev->vram_res)); } -static int mgag200_regs_init(struct mga_device *mdev) +int mgag200_device_preinit(struct mga_device *mdev) { struct drm_device *dev = &mdev->base; struct pci_dev *pdev = to_pci_dev(dev->dev); - u32 option, option2; - u8 crtcext3; - int ret; - - ret = drmm_mutex_init(dev, &mdev->rmmio_lock); - if (ret) - return ret; - - switch (mdev->type) { - case G200_PCI: - case G200_AGP: - if (mgag200_has_sgram(mdev)) - option = 0x4049cd21; - else - option = 0x40499121; - option2 = 0x00008000; - break; - case G200_SE_A: - case G200_SE_B: - option = 0x40049120; - if (mgag200_has_sgram(mdev)) - option |= PCI_MGA_OPTION_HARDPWMSK; - option2 = 0x00008000; - break; - case G200_WB: - case G200_EW3: - option = 0x41049120; - option2 = 0x0000b000; - break; - case G200_EV: - option = 0x00000120; - option2 = 0x0000b000; - break; - case G200_EH: - case G200_EH3: - option = 0x00000120; - option2 = 0x0000b000; - break; - default: - option = 0; - option2 = 0; - } - - if (option) - pci_write_config_dword(pdev, PCI_MGA_OPTION, option); - if (option2) - pci_write_config_dword(pdev, PCI_MGA_OPTION2, option2); + resource_size_t start, len; + struct resource *res; /* BAR 1 contains registers */ - mdev->rmmio_base = pci_resource_start(pdev, 1); - mdev->rmmio_size = pci_resource_len(pdev, 1); - if (!devm_request_mem_region(dev->dev, mdev->rmmio_base, - mdev->rmmio_size, "mgadrmfb_mmio")) { - drm_err(dev, "can't reserve mmio registers\n"); - return -ENOMEM; + 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 == NULL) + if (!mdev->rmmio) return -ENOMEM; - RREG_ECRT(0x03, crtcext3); - crtcext3 |= MGAREG_CRTCEXT3_MGAMODE; - WREG_ECRT(0x03, crtcext3); - - return 0; -} - -static void mgag200_g200_interpret_bios(struct mga_device *mdev, - 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 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); + /* BAR 0 is VRAM */ - /* Extract the clock values */ + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); - switch (version) { - case 1: - tmp = pins[24] + (pins[25] << 8); - if (tmp) - mdev->model.g200.pclk_max = tmp * 10; - break; - case 2: - if (pins[41] != 0xff) - mdev->model.g200.pclk_max = (pins[41] + 100) * 1000; - break; - case 3: - if (pins[36] != 0xff) - mdev->model.g200.pclk_max = (pins[36] + 100) * 1000; - if (pins[52] & 0x20) - mdev->model.g200.ref_clk = 14318; - break; - case 4: - if (pins[39] != 0xff) - mdev->model.g200.pclk_max = pins[39] * 4 * 1000; - if (pins[92] & 0x01) - mdev->model.g200.ref_clk = 14318; - break; - case 5: - tmp = pins[4] ? 8000 : 6000; - if (pins[123] != 0xff) - mdev->model.g200.pclk_min = pins[123] * tmp; - if (pins[38] != 0xff) - mdev->model.g200.pclk_max = pins[38] * tmp; - if (pins[110] & 0x01) - mdev->model.g200.ref_clk = 14318; - break; - default: - break; + 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; } -} - -static void mgag200_g200_init_refclk(struct mga_device *mdev) -{ - 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; - - mdev->model.g200.pclk_min = 50000; - mdev->model.g200.pclk_max = 230000; - mdev->model.g200.ref_clk = 27050; - - rom = pci_map_rom(pdev, &size); - if (!rom) - return; + mdev->vram_res = res; - bios = vmalloc(size); - if (!bios) - goto out; - memcpy_fromio(bios, rom, size); + /* Don't fail on errors, but performance might be reduced. */ + devm_arch_io_reserve_memtype_wc(dev->dev, res->start, resource_size(res)); + devm_arch_phys_wc_add(dev->dev, res->start, resource_size(res)); - if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa) - mgag200_g200_interpret_bios(mdev, bios, size); - - drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n", - mdev->model.g200.pclk_min, mdev->model.g200.pclk_max, - mdev->model.g200.ref_clk); + mdev->vram = devm_ioremap(dev->dev, res->start, resource_size(res)); + if (!mdev->vram) + return -ENOMEM; - vfree(bios); -out: - pci_unmap_rom(pdev, rom); + return 0; } -static void mgag200_g200se_init_unique_id(struct mga_device *mdev) +int mgag200_device_init(struct mga_device *mdev, enum mga_type type, + const struct mgag200_device_info *info) { struct drm_device *dev = &mdev->base; - - /* stash G200 SE model number for later use */ - mdev->model.g200se.unique_rev_id = RREG32(0x1e24); - - drm_dbg(dev, "G200 SE unique revision id is 0x%x\n", - mdev->model.g200se.unique_rev_id); -} - -static struct mga_device * -mgag200_device_create(struct pci_dev *pdev, enum mga_type type, unsigned long flags) -{ - struct mga_device *mdev; - struct drm_device *dev; + u8 crtcext3, misc; int ret; - mdev = devm_drm_dev_alloc(&pdev->dev, &mgag200_driver, struct mga_device, base); - if (IS_ERR(mdev)) - return mdev; - dev = &mdev->base; - - pci_set_drvdata(pdev, dev); - - mdev->flags = flags; + mdev->info = info; mdev->type = type; - ret = mgag200_regs_init(mdev); + ret = drmm_mutex_init(dev, &mdev->rmmio_lock); if (ret) - return ERR_PTR(ret); + return ret; - if (mdev->type == G200_PCI || mdev->type == G200_AGP) - mgag200_g200_init_refclk(mdev); - else if (IS_G200_SE(mdev)) - mgag200_g200se_init_unique_id(mdev); + mutex_lock(&mdev->rmmio_lock); - ret = mgag200_mm_init(mdev); - if (ret) - return ERR_PTR(ret); + RREG_ECRT(0x03, crtcext3); + crtcext3 |= MGAREG_CRTCEXT3_MGAMODE; + WREG_ECRT(0x03, crtcext3); - ret = mgag200_modeset_init(mdev); - if (ret) - return ERR_PTR(ret); + WREG_ECRT(0x04, 0x00); - return mdev; + misc = RREG8(MGA_MISC_IN); + misc |= MGAREG_MISC_RAMMAPEN | + MGAREG_MISC_HIGH_PG_SEL; + WREG8(MGA_MISC_OUT, misc); + + mutex_unlock(&mdev->rmmio_lock); + + return 0; } /* @@ -312,8 +194,7 @@ mgag200_device_create(struct pci_dev *pdev, enum mga_type type, unsigned long fl 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 | MGAG200_FLAG_HW_BUG_NO_STARTADD}, + { 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 }, @@ -326,22 +207,10 @@ static const struct pci_device_id mgag200_pciidlist[] = { MODULE_DEVICE_TABLE(pci, mgag200_pciidlist); -static enum mga_type mgag200_type_from_driver_data(kernel_ulong_t driver_data) -{ - return (enum mga_type)(driver_data & MGAG200_TYPE_MASK); -} - -static unsigned long mgag200_flags_from_driver_data(kernel_ulong_t driver_data) -{ - return driver_data & MGAG200_FLAG_MASK; -} - static int mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - kernel_ulong_t driver_data = ent->driver_data; - enum mga_type type = mgag200_type_from_driver_data(driver_data); - unsigned long flags = mgag200_flags_from_driver_data(driver_data); + enum mga_type type = (enum mga_type)ent->driver_data; struct mga_device *mdev; struct drm_device *dev; int ret; @@ -354,7 +223,37 @@ mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) return ret; - mdev = mgag200_device_create(pdev, type, flags); + switch (type) { + case G200_PCI: + case G200_AGP: + mdev = mgag200_g200_device_create(pdev, &mgag200_driver, type); + 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, type); + break; + case G200_EV: + mdev = mgag200_g200ev_device_create(pdev, &mgag200_driver, type); + break; + case G200_EH: + mdev = mgag200_g200eh_device_create(pdev, &mgag200_driver, type); + break; + case G200_EH3: + mdev = mgag200_g200eh3_device_create(pdev, &mgag200_driver, type); + break; + case G200_ER: + mdev = mgag200_g200er_device_create(pdev, &mgag200_driver, type); + break; + case G200_EW3: + mdev = mgag200_g200ew3_device_create(pdev, &mgag200_driver, type); + 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; diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index a18384c41fc4..301c4ab46539 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -168,8 +168,6 @@ static inline struct mgag200_crtc_state *to_mgag200_crtc_state(struct drm_crtc_s return container_of(base, struct mgag200_crtc_state, base); } -#define to_mga_connector(x) container_of(x, struct mga_connector, base) - struct mga_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; @@ -177,17 +175,6 @@ struct mga_i2c_chan { int data, clock; }; -struct mga_connector { - struct drm_connector base; - struct mga_i2c_chan *i2c; -}; - -struct mga_mc { - resource_size_t vram_size; - resource_size_t vram_base; - resource_size_t vram_window; -}; - enum mga_type { G200_PCI, G200_AGP, @@ -201,44 +188,66 @@ enum mga_type { G200_EW3, }; -/* HW does not handle 'startadd' field correct. */ -#define MGAG200_FLAG_HW_BUG_NO_STARTADD (1ul << 8) +#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B) -#define MGAG200_TYPE_MASK (0x000000ff) -#define MGAG200_FLAG_MASK (0x00ffff00) +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; + + /* HW has external source (e.g., BMC) to synchronize with */ + bool has_vidrst: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 IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B) +#define MGAG200_DEVICE_INFO_INIT(_max_hdisplay, _max_vdisplay, _max_mem_bandwidth, \ + _has_vidrst, _i2c_data_bit, _i2c_clock_bit, \ + _bug_no_startadd) \ + { \ + .max_hdisplay = (_max_hdisplay), \ + .max_vdisplay = (_max_vdisplay), \ + .max_mem_bandwidth = (_max_mem_bandwidth), \ + .has_vidrst = (_has_vidrst), \ + .i2c = { \ + .data_bit = (_i2c_data_bit), \ + .clock_bit = (_i2c_clock_bit), \ + }, \ + .bug_no_startadd = (_bug_no_startadd), \ + } struct mga_device { - struct drm_device base; - unsigned long flags; + struct drm_device base; - struct mutex rmmio_lock; /* Protects access to rmmio */ - resource_size_t rmmio_base; - resource_size_t rmmio_size; - void __iomem *rmmio; + const struct mgag200_device_info *info; - struct mga_mc mc; + struct resource *rmmio_res; + void __iomem *rmmio; + struct mutex rmmio_lock; /* Protects access to rmmio */ + struct resource *vram_res; void __iomem *vram; - size_t vram_fb_available; + resource_size_t vram_available; enum mga_type type; - union { - struct { - long ref_clk; - long pclk_min; - long pclk_max; - } g200; - struct { - /* SE model number stored in reg 0x1e24 */ - u32 unique_rev_id; - } g200se; - } model; - - struct mga_connector connector; struct mgag200_pll pixpll; + struct mga_i2c_chan i2c; + struct drm_connector connector; struct drm_simple_display_pipe display_pipe; }; @@ -247,15 +256,64 @@ 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, enum mga_type type, + const struct mgag200_device_info *info); + + /* mgag200_<device type>.c */ +struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); +struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type); + /* mgag200_mode.c */ -int mgag200_modeset_init(struct mga_device *mdev); +resource_size_t mgag200_device_probe_vram(struct mga_device *mdev); +int mgag200_modeset_init(struct mga_device *mdev, resource_size_t vram_fb_available); /* mgag200_i2c.c */ -struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev); -void mgag200_i2c_destroy(struct mga_i2c_chan *i2c); - - /* mgag200_mm.c */ -int mgag200_mm_init(struct mga_device *mdev); +int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c); /* mgag200_pll.c */ int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev); diff --git a/drivers/gpu/drm/mgag200/mgag200_g200.c b/drivers/gpu/drm/mgag200/mgag200_g200.c new file mode 100644 index 000000000000..674385921b7f --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> +#include <linux/vmalloc.h> + +#include <drm/drm_drv.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); +} + +/* + * 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); +} + +struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + 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, type, &mgag200_g200_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh.c b/drivers/gpu/drm/mgag200/mgag200_g200eh.c new file mode 100644 index 000000000000..1b9a22728744 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200eh.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200eh_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 37500, false, 1, 0, false); + +struct mga_device *mgag200_g200eh_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + 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, type, &mgag200_g200eh_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c new file mode 100644 index 000000000000..438cda1b14c9 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200eh3_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false); + +struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum mga_type type) +{ + 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, type, &mgag200_g200eh3_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c new file mode 100644 index 000000000000..0790d4e6463d --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200er_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 1, 0, false); + +struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + 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, type, &mgag200_g200er_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c new file mode 100644 index 000000000000..5353422d0eef --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200ev_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 32700, false, 0, 1, false); + +struct mga_device *mgag200_g200ev_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + 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, type, &mgag200_g200ev_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c new file mode 100644 index 000000000000..3bfc1324cf78 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200ew3_device_info = + MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false); + +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, + enum mga_type type) +{ + 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, type, &mgag200_g200ew3_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_g200ew3_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c new file mode 100644 index 000000000000..0a3e66695e22 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.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); +} + +/* + * 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; +} + +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; + 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); + } + + ret = mgag200_device_init(mdev, type, info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_g200wb.c b/drivers/gpu/drm/mgag200/mgag200_g200wb.c new file mode 100644 index 000000000000..c8450ac8eaec --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_g200wb.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "mgag200_drv.h" + +/* + * DRM device + */ + +static const struct mgag200_device_info mgag200_g200wb_device_info = + MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false); + +struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv, + enum mga_type type) +{ + 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, type, &mgag200_g200wb_device_info); + if (ret) + return ERR_PTR(ret); + + vram_available = mgag200_device_probe_vram(mdev); + + ret = mgag200_modeset_init(mdev, vram_available); + if (ret) + return ERR_PTR(ret); + + return mdev; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c index ac8e34eef513..0c48bdf3e7f8 100644 --- a/drivers/gpu/drm/mgag200/mgag200_i2c.c +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -86,44 +86,25 @@ static int mga_gpio_getscl(void *data) return (mga_i2c_read_gpio(mdev) & i2c->clock) ? 1 : 0; } -struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) +static void mgag200_i2c_release(void *res) { - struct mga_device *mdev = to_mga_device(dev); - struct mga_i2c_chan *i2c; + struct mga_i2c_chan *i2c = res; + + i2c_del_adapter(&i2c->adapter); +} + +int mgag200_i2c_init(struct mga_device *mdev, struct mga_i2c_chan *i2c) +{ + struct drm_device *dev = &mdev->base; + const struct mgag200_device_info *info = mdev->info; int ret; - int data, clock; WREG_DAC(MGA1064_GEN_IO_CTL2, 1); WREG_DAC(MGA1064_GEN_IO_DATA, 0xff); WREG_DAC(MGA1064_GEN_IO_CTL, 0); - switch (mdev->type) { - case G200_SE_A: - case G200_SE_B: - case G200_EV: - case G200_WB: - case G200_EW3: - data = 1; - clock = 2; - break; - case G200_EH: - case G200_EH3: - case G200_ER: - data = 2; - clock = 1; - break; - default: - data = 2; - clock = 8; - break; - } - - i2c = kzalloc(sizeof(struct mga_i2c_chan), GFP_KERNEL); - if (!i2c) - return NULL; - - i2c->data = data; - i2c->clock = clock; + i2c->data = BIT(info->i2c.data_bit); + i2c->clock = BIT(info->i2c.clock_bit); i2c->adapter.owner = THIS_MODULE; i2c->adapter.class = I2C_CLASS_DDC; i2c->adapter.dev.parent = dev->dev; @@ -142,18 +123,8 @@ struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev) i2c->bit.getscl = mga_gpio_getscl; ret = i2c_bit_add_bus(&i2c->adapter); - if (ret) { - kfree(i2c); - i2c = NULL; - } - return i2c; -} + if (ret) + return ret; -void mgag200_i2c_destroy(struct mga_i2c_chan *i2c) -{ - if (!i2c) - return; - i2c_del_adapter(&i2c->adapter); - kfree(i2c); + return devm_add_action_or_reset(dev->dev, mgag200_i2c_release, i2c); } - diff --git a/drivers/gpu/drm/mgag200/mgag200_mm.c b/drivers/gpu/drm/mgag200/mgag200_mm.c deleted file mode 100644 index fa996d46feed..000000000000 --- a/drivers/gpu/drm/mgag200/mgag200_mm.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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/pci.h> - -#include <drm/drm_managed.h> - -#include "mgag200_drv.h" - -static size_t mgag200_probe_vram(struct mga_device *mdev, void __iomem *mem, - 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; - - if ((mdev->type == G200_EW3) && (vram_size >= 0x1000000)) - vram_size = vram_size - 0x400000; - - 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; -} - -int mgag200_mm_init(struct mga_device *mdev) -{ - struct drm_device *dev = &mdev->base; - struct pci_dev *pdev = to_pci_dev(dev->dev); - u8 misc; - resource_size_t start, len; - - WREG_ECRT(0x04, 0x00); - - misc = RREG8(MGA_MISC_IN); - misc |= MGAREG_MISC_RAMMAPEN | - MGAREG_MISC_HIGH_PG_SEL; - WREG8(MGA_MISC_OUT, misc); - - /* BAR 0 is VRAM */ - start = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - - if (!devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram")) { - drm_err(dev, "can't reserve VRAM\n"); - return -ENXIO; - } - - /* Don't fail on errors, but performance might be reduced. */ - devm_arch_io_reserve_memtype_wc(dev->dev, start, len); - devm_arch_phys_wc_add(dev->dev, start, len); - - mdev->vram = devm_ioremap(dev->dev, start, len); - if (!mdev->vram) - return -ENOMEM; - - mdev->mc.vram_size = mgag200_probe_vram(mdev, mdev->vram, len); - mdev->mc.vram_base = start; - mdev->mc.vram_window = len; - - mdev->vram_fb_available = mdev->mc.vram_size; - - return 0; -} diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index abde7655477d..225cca2ed60e 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -17,6 +17,7 @@ #include <drm/drm_damage_helper.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_plane_helper.h> @@ -32,57 +33,78 @@ * This file contains setup code for the CRTC. */ -static void mga_crtc_load_lut(struct drm_crtc *crtc) +static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, + const struct drm_format_info *format) { - struct drm_device *dev = crtc->dev; - struct mga_device *mdev = to_mga_device(dev); - struct drm_framebuffer *fb; - u16 *r_ptr, *g_ptr, *b_ptr; int i; - if (!crtc->enabled) - return; - - if (!mdev->display_pipe.plane.state) - return; + WREG8(DAC_INDEX + MGA1064_INDEX, 0); - fb = mdev->display_pipe.plane.state->fb; + switch (format->format) { + case DRM_FORMAT_RGB565: + /* Use better interpolation, to take 32 values from 0 to 255 */ + for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 8 + i / 4); + } + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i * 4 + i / 16); + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + } + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + for (i = 0; i < MGAG200_LUT_SIZE; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, i); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i); + WREG8(DAC_INDEX + MGA1064_COL_PAL, i); + } + break; + default: + drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; + } +} - r_ptr = crtc->gamma_store; - g_ptr = r_ptr + crtc->gamma_size; - b_ptr = g_ptr + crtc->gamma_size; +static void mgag200_crtc_set_gamma(struct mga_device *mdev, + const struct drm_format_info *format, + struct drm_color_lut *lut) +{ + int i; WREG8(DAC_INDEX + MGA1064_INDEX, 0); - if (fb && fb->format->cpp[0] * 8 == 16) { - int inc = (fb->format->depth == 15) ? 8 : 4; - u8 r, b; - for (i = 0; i < MGAG200_LUT_SIZE; i += inc) { - if (fb->format->depth == 16) { - if (i > (MGAG200_LUT_SIZE >> 1)) { - r = b = 0; - } else { - r = *r_ptr++ >> 8; - b = *b_ptr++ >> 8; - r_ptr++; - b_ptr++; - } - } else { - r = *r_ptr++ >> 8; - b = *b_ptr++ >> 8; - } - /* VGA registers */ - WREG8(DAC_INDEX + MGA1064_COL_PAL, r); - WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, b); + switch (format->format) { + case DRM_FORMAT_RGB565: + /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ + for (i = 0; i < MGAG200_LUT_SIZE / 8; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].red >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 8 + i / 4].blue >> 8); } - return; - } - for (i = 0; i < MGAG200_LUT_SIZE; i++) { - /* VGA registers */ - WREG8(DAC_INDEX + MGA1064_COL_PAL, *r_ptr++ >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8); - WREG8(DAC_INDEX + MGA1064_COL_PAL, *b_ptr++ >> 8); + /* Green has one more bit, so add padding with 0 for red and blue. */ + for (i = MGAG200_LUT_SIZE / 8; i < MGAG200_LUT_SIZE / 4; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i * 4 + i / 16].green >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, 0); + } + break; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + for (i = 0; i < MGAG200_LUT_SIZE; i++) { + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].red >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].green >> 8); + WREG8(DAC_INDEX + MGA1064_COL_PAL, lut[i].blue >> 8); + } + break; + default: + drm_warn_once(&mdev->base, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; } } @@ -223,6 +245,9 @@ static void mgag200_set_startadd(struct mga_device *mdev, 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. @@ -353,6 +378,7 @@ static void mgag200_init_regs(struct mga_device *mdev) static void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode) { + const struct mgag200_device_info *info = mdev->info; unsigned int hdisplay, hsyncstart, hsyncend, htotal; unsigned int vdisplay, vsyncstart, vsyncend, vtotal; u8 misc, crtcext1, crtcext2, crtcext5; @@ -387,9 +413,9 @@ static void mgag200_set_mode_regs(struct mga_device *mdev, ((hdisplay & 0x100) >> 7) | ((hsyncstart & 0x100) >> 6) | (htotal & 0x40); - if (mdev->type == G200_WB || mdev->type == G200_EW3) - crtcext1 |= BIT(7) | /* vrsten */ - BIT(3); /* hrsten */ + if (info->has_vidrst) + crtcext1 |= MGAREG_CRTCEXT1_VRSTEN | + MGAREG_CRTCEXT1_HRSTEN; crtcext2 = ((vtotal & 0xc00) >> 10) | ((vdisplay & 0x400) >> 8) | @@ -559,13 +585,13 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, const struct drm_display_mode *mode, const struct drm_framebuffer *fb) { - u32 unique_rev_id = mdev->model.g200se.unique_rev_id; + struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base); unsigned int hiprilvl; u8 crtcext6; - if (unique_rev_id >= 0x04) { + if (g200se->unique_rev_id >= 0x04) { hiprilvl = 0; - } else if (unique_rev_id >= 0x02) { + } else if (g200se->unique_rev_id >= 0x02) { unsigned int bpp; unsigned long mb; @@ -590,7 +616,7 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev, else hiprilvl = 5; - } else if (unique_rev_id >= 0x01) { + } else if (g200se->unique_rev_id >= 0x01) { hiprilvl = 3; } else { hiprilvl = 4; @@ -665,176 +691,34 @@ static void mgag200_disable_display(struct mga_device *mdev) * Connector */ -static int mga_vga_get_modes(struct drm_connector *connector) -{ - struct mga_connector *mga_connector = to_mga_connector(connector); - struct edid *edid; - int ret = 0; - - edid = drm_get_edid(connector, &mga_connector->i2c->adapter); - if (edid) { - drm_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - } - return ret; -} - -static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode, - 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); -} - -#define MODE_BANDWIDTH MODE_BAD - -static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) +static int mgag200_vga_connector_helper_get_modes(struct drm_connector *connector) { - struct drm_device *dev = connector->dev; - struct mga_device *mdev = to_mga_device(dev); - int bpp = 32; - - if (IS_G200_SE(mdev)) { - u32 unique_rev_id = mdev->model.g200se.unique_rev_id; - - if (unique_rev_id == 0x01) { - if (mode->hdisplay > 1600) - return MODE_VIRTUAL_X; - if (mode->vdisplay > 1200) - return MODE_VIRTUAL_Y; - if (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (24400 * 1024)) - return MODE_BANDWIDTH; - } else if (unique_rev_id == 0x02) { - if (mode->hdisplay > 1920) - return MODE_VIRTUAL_X; - if (mode->vdisplay > 1200) - return MODE_VIRTUAL_Y; - if (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (30100 * 1024)) - return MODE_BANDWIDTH; - } else { - if (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (55000 * 1024)) - return MODE_BANDWIDTH; - } - } else if (mdev->type == G200_WB) { - if (mode->hdisplay > 1280) - return MODE_VIRTUAL_X; - if (mode->vdisplay > 1024) - return MODE_VIRTUAL_Y; - if (mga_vga_calculate_mode_bandwidth(mode, bpp) > - (31877 * 1024)) - return MODE_BANDWIDTH; - } else if (mdev->type == G200_EV && - (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (32700 * 1024))) { - return MODE_BANDWIDTH; - } else if (mdev->type == G200_EH && - (mga_vga_calculate_mode_bandwidth(mode, bpp) - > (37500 * 1024))) { - return MODE_BANDWIDTH; - } else if (mdev->type == G200_ER && - (mga_vga_calculate_mode_bandwidth(mode, - bpp) > (55000 * 1024))) { - return MODE_BANDWIDTH; - } - - 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; - } - - /* Validate the mode input by the user */ - if (connector->cmdline_mode.specified) { - if (connector->cmdline_mode.bpp_specified) - bpp = connector->cmdline_mode.bpp; - } - - if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->vram_fb_available) { - if (connector->cmdline_mode.specified) - connector->cmdline_mode.specified = false; - return MODE_BAD; - } + struct mga_device *mdev = to_mga_device(connector->dev); + int ret; - return MODE_OK; -} + /* + * Protect access to I/O registers from concurrent modesetting + * by acquiring the I/O-register lock. + */ + mutex_lock(&mdev->rmmio_lock); + ret = drm_connector_helper_get_modes_from_ddc(connector); + mutex_unlock(&mdev->rmmio_lock); -static void mga_connector_destroy(struct drm_connector *connector) -{ - struct mga_connector *mga_connector = to_mga_connector(connector); - mgag200_i2c_destroy(mga_connector->i2c); - drm_connector_cleanup(connector); + return ret; } static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = { - .get_modes = mga_vga_get_modes, - .mode_valid = mga_vga_mode_valid, + .get_modes = mgag200_vga_connector_helper_get_modes, }; static const struct drm_connector_funcs mga_vga_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = mga_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int mgag200_vga_connector_init(struct mga_device *mdev) -{ - struct drm_device *dev = &mdev->base; - struct mga_connector *mconnector = &mdev->connector; - struct drm_connector *connector = &mconnector->base; - struct mga_i2c_chan *i2c; - int ret; - - i2c = mgag200_i2c_create(dev); - if (!i2c) - drm_warn(dev, "failed to add DDC bus\n"); - - ret = drm_connector_init_with_ddc(dev, connector, - &mga_vga_connector_funcs, - DRM_MODE_CONNECTOR_VGA, - &i2c->adapter); - if (ret) - goto err_mgag200_i2c_destroy; - drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); - - mconnector->i2c = i2c; - - return 0; - -err_mgag200_i2c_destroy: - mgag200_i2c_destroy(i2c); - return ret; -} - /* * Simple Display Pipe */ @@ -843,6 +727,30 @@ static enum drm_mode_status mgag200_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, const struct drm_display_mode *mode) { + struct mga_device *mdev = to_mga_device(pipe->crtc.dev); + const struct mgag200_device_info *info = mdev->info; + + /* + * Some devices have additional limits on the size of the + * display mode. + */ + if (mode->hdisplay > info->max_hdisplay) + return MODE_VIRTUAL_X; + if (mode->vdisplay > info->max_vdisplay) + return MODE_VIRTUAL_Y; + + 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; } @@ -855,10 +763,6 @@ mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb, dst += drm_fb_clip_offset(fb->pitches[0], fb->format, clip); drm_fb_memcpy_toio(dst, fb->pitches[0], vmap, fb, clip); - - /* Always scanout image at VRAM offset 0 */ - mgag200_set_startadd(mdev, (u32)0); - mgag200_set_offset(mdev, fb); } static void @@ -908,11 +812,19 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, if (mdev->type == G200_WB || mdev->type == G200_EW3) mgag200_g200wb_release_bmc(mdev); - mga_crtc_load_lut(crtc); + if (crtc_state->gamma_lut) + mgag200_crtc_set_gamma(mdev, fb->format, crtc_state->gamma_lut->data); + else + mgag200_crtc_set_gamma_linear(mdev, fb->format); + mgag200_enable_display(mdev); mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]); + /* Always scanout image at VRAM offset 0 */ + mgag200_set_startadd(mdev, (u32)0); + mgag200_set_offset(mdev, fb); + mutex_unlock(&mdev->rmmio_lock); } @@ -955,6 +867,14 @@ mgag200_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, return ret; } + if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) { + if (crtc_state->gamma_lut->length != + MGAG200_LUT_SIZE * sizeof(struct drm_color_lut)) { + drm_err(dev, "Wrong size for gamma_lut %zu\n", + crtc_state->gamma_lut->length); + return -EINVAL; + } + } return 0; } @@ -963,20 +883,30 @@ mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane *plane = &pipe->plane; + struct drm_crtc *crtc = &pipe->crtc; struct drm_device *dev = plane->dev; struct mga_device *mdev = to_mga_device(dev); struct drm_plane_state *state = plane->state; struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); struct drm_framebuffer *fb = state->fb; struct drm_rect damage; + struct drm_atomic_helper_damage_iter iter; if (!fb) return; mutex_lock(&mdev->rmmio_lock); - if (drm_atomic_helper_damage_merged(old_state, state, &damage)) + if (crtc->state->color_mgmt_changed && crtc->state->gamma_lut) + mgag200_crtc_set_gamma(mdev, fb->format, crtc->state->gamma_lut->data); + + drm_atomic_helper_damage_iter_init(&iter, old_state, state); + drm_atomic_for_each_plane_damage(&iter, &damage) { mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]); + } + /* Always scanout image at VRAM offset 0 */ + mgag200_set_startadd(mdev, (u32)0); + mgag200_set_offset(mdev, fb); mutex_unlock(&mdev->rmmio_lock); } @@ -1056,30 +986,81 @@ static const uint64_t mgag200_simple_display_pipe_fmtmods[] = { * Mode config */ -static const struct drm_mode_config_funcs mgag200_mode_config_funcs = { - .fb_create = drm_gem_fb_create_with_dirty, - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, -}; +/* 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; -static unsigned int mgag200_preferred_depth(struct mga_device *mdev) + 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) { - if (IS_G200_SE(mdev) && mdev->vram_fb_available < (2048*1024)) - return 16; - else - return 32; + 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; } -int mgag200_modeset_init(struct mga_device *mdev) +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_modeset_init(struct mga_device *mdev, resource_size_t vram_available) { struct drm_device *dev = &mdev->base; - struct drm_connector *connector = &mdev->connector.base; + struct mga_i2c_chan *i2c = &mdev->i2c; + struct drm_connector *connector = &mdev->connector; struct drm_simple_display_pipe *pipe = &mdev->display_pipe; size_t format_count = ARRAY_SIZE(mgag200_simple_display_pipe_formats); int ret; mgag200_init_regs(mdev); + mdev->vram_available = vram_available; + ret = drmm_mode_config_init(dev); if (ret) { drm_err(dev, "drmm_mode_config_init() failed, error %d\n", @@ -1089,21 +1070,26 @@ int mgag200_modeset_init(struct mga_device *mdev) dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH; dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT; - - dev->mode_config.preferred_depth = mgag200_preferred_depth(mdev); - - dev->mode_config.fb_base = mdev->mc.vram_base; - + dev->mode_config.preferred_depth = 24; + dev->mode_config.fb_base = mdev->vram_res->start; dev->mode_config.funcs = &mgag200_mode_config_funcs; - ret = mgag200_vga_connector_init(mdev); + ret = mgag200_i2c_init(mdev, i2c); if (ret) { - drm_err(dev, - "mgag200_vga_connector_init() failed, error %d\n", - ret); + drm_err(dev, "failed to add DDC bus: %d\n", ret); return ret; } + ret = drm_connector_init_with_ddc(dev, connector, + &mga_vga_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + &i2c->adapter); + if (ret) { + drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret); + return ret; + } + drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); + ret = mgag200_pixpll_init(&mdev->pixpll, mdev); if (ret) return ret; @@ -1121,9 +1107,13 @@ int mgag200_modeset_init(struct mga_device *mdev) return ret; } - /* FIXME: legacy gamma tables; convert to CRTC state */ + drm_plane_enable_fb_damage_clips(&pipe->plane); + + /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ drm_mode_crtc_set_gamma_size(&pipe->crtc, MGAG200_LUT_SIZE); + drm_crtc_enable_color_mgmt(&pipe->crtc, 0, false, MGAG200_LUT_SIZE); + drm_mode_config_reset(dev); return 0; diff --git a/drivers/gpu/drm/mgag200/mgag200_pll.c b/drivers/gpu/drm/mgag200/mgag200_pll.c index 52be08b744ad..8065ca5d8de9 100644 --- a/drivers/gpu/drm/mgag200/mgag200_pll.c +++ b/drivers/gpu/drm/mgag200/mgag200_pll.c @@ -13,6 +13,7 @@ static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock, { struct mga_device *mdev = pixpll->mdev; struct drm_device *dev = &mdev->base; + struct mgag200_g200_device *g200 = to_mgag200_g200_device(dev); const int post_div_max = 7; const int in_div_min = 1; const int in_div_max = 6; @@ -23,9 +24,9 @@ static int mgag200_pixpll_compute_g200(struct mgag200_pll *pixpll, long clock, long f_vco; long computed; long delta, tmp_delta; - long ref_clk = mdev->model.g200.ref_clk; - long p_clk_min = mdev->model.g200.pclk_min; - long p_clk_max = mdev->model.g200.pclk_max; + 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); @@ -951,6 +952,7 @@ static const struct mgag200_pll_funcs mgag200_pixpll_funcs_g200ew3 = { int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev) { struct drm_device *dev = &mdev->base; + struct mgag200_g200se_device *g200se; pixpll->mdev = mdev; @@ -961,7 +963,9 @@ int mgag200_pixpll_init(struct mgag200_pll *pixpll, struct mga_device *mdev) break; case G200_SE_A: case G200_SE_B: - if (mdev->model.g200se.unique_rev_id >= 0x04) + g200se = to_mgag200_g200se_device(dev); + + if (g200se->unique_rev_id >= 0x04) pixpll->funcs = &mgag200_pixpll_funcs_g200se_04; else pixpll->funcs = &mgag200_pixpll_funcs_g200se_00; diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h index 60e705283fe8..99a9ab7d9119 100644 --- a/drivers/gpu/drm/mgag200/mgag200_reg.h +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -252,8 +252,10 @@ #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) |