1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/gpu/drm/tiny/bochs.c
Linus Torvalds 28eb75e178 drm for 6.13-rc1
core:
 - split DSC helpers from DP helpers
 - clang build fixes for drm/mm test
 - drop simple pipeline support for gem vram
 - document submission error signaling
 - move drm_rect to drm core module from kms helper
 - add default client setup to most drivers
 - move to video aperture helpers instead of drm ones
 
 tests:
 - new framebuffer tests
 
 ttm:
 - remove swapped and pinned BOs from TTM lru
 
 panic:
 - fix uninit spinlock
 - add ABGR2101010 support
 
 bridge:
 - add TI TDP158 support
 - use standard PM OPS
 
 dma-fence:
 - use read_trylock instead of read_lock to help lockdep
 
 scheduler:
 - add errno to sched start to report different errors
 - add locking to drm_sched_entity_modify_sched
 - improve documentation
 
 xe:
 - add drm_line_printer
 - lots of refactoring
 - Enable Xe2 + PES disaggregation
 - add new ARL PCI ID
 - SRIOV development work
 - fix exec unnecessary implicit fence
 - define and parse OA sync props
 - forcewake refactoring
 
 i915:
 - Enable BMG/LNL ultra joiner
 - Enable 10bpx + CCS scanout on ICL+, fp16/CCS on TGL+
 - use DSB for plane/color mgmt
 - Arrow lake PCI IDs
 - lots of i915/xe display refactoring
 - enable PXP GuC autoteardown
 - Pantherlake (PTL) Xe3 LPD display enablement
 - Allow fastset HDR infoframe changes
 - write DP source OUI for non-eDP sinks
 - share PCI IDs between i915 and xe
 
 amdgpu:
 - SDMA queue reset support
 - SMU 13.0.6, JPEG 4.0.3 updates
 - Initial runtime repartitioning support
 - rework IP structs for multiple IP instances
 - Fetch EDID from _DDC if available
 - SMU13 zero rpm user control
 - lots of fixes/cleanups
 
 amdkfd:
 - Increase event FIFO size
 - add topology cap flag for per queue reset
 
 msm:
 - DPU:
 - SA8775P support
 - (disabled by default) MSM8917, MSM8937, MSM8953 and MSM8996 support
 - Enable large framebuffer support
 - Drop MSM8998 and SDM845
 - DP:
 - SA8775P support
 - GPU:
 - a7xx preemption support
 - Adreno A663 support
 
 ast:
 - warn about unsupported TX chips
 
 ivpu:
 - add coredump
 - add pantherlake support
 
 rockchip:
 - 4K@60Hz display enablement
 - generate pll programming tables
 
 panthor:
 - add timestamp query API
 - add realtime group priority
 - add fdinfo support
 
 etnaviv:
 - improve handling of DMA address limits
 - improve GPU hangcheck
 
 exynos:
 - Decon Exynos7870 support
 
 mediatek:
 - add OF graph support
 
 omap:
 - locking fixes
 
 bochs:
 - convert to gem/shmem from simpledrm
 
 v3d:
 - support big/super pages
 - add gemfs
 
 vc4:
 - BCM2712 support refactoring
 - add YUV444 format support
 
 udmabuf:
 - folio related fixes
 
 nouveau:
 - add panic support on nv50+
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmc+efwACgkQDHTzWXnE
 hr6Dyg/9HVVI3lxuWAz9MEt3w+BON5KTJAxg5Zhvc5DwiUbDXghu8sfkUfanDWS5
 /MqyPqLt5srXrtKTRDnzEI0Vf8YHeiDEcaydjpshEpCfteHZ7SADpvem8fp6/otV
 iYt8U6tMcGe9I+M2kwDkOTrKJIiyCKPi5hfBIAkxEAh6806ifPRtLkeMGbaSwBxH
 x6kZTE9ygGWAY7bAgbmVmm3JwrXG9mYDl9dW3cbi9gZ6PGAXHPZRUPvZoHhvfC2A
 UVgROH76Spm4rdWYGI3azj+gW3HsdGgUHcysb+lu37i261E+sT7kuV2UYtnOMzr5
 igO1RlQ+rcfPYLG4n+oNXDMu5d1OQXELrlQzXptym4Konpd7b/GSeVctWV0wHWuv
 nG8g7DWAFFnLAdeWqLZpf1Brze33h5+572D3BioWB4LYSEATjwoTwcBKsdRuc4Wk
 RHxjumCidybTdo/8EB1ElGlH39m/mDQA0scMlVhS/BuiIssfgcBRfltI8S3HzHcW
 YQYq6xH7F9E3shs3/TYbWR4clm66ZTnZV6ClDfGJolzyF/hbV0rsbeSpDelpooE8
 1Js7KuwVa+HvA4jtupY9vqxMTdXWwoGPfuUgKpOAreYibnd1T9Q1zVme/B1bUH05
 518IjiMGCxDnBvFWaPT9DcX4zg7pS3yzjw3hGkdz3reUqat0Gy8=
 =8cUI
 -----END PGP SIGNATURE-----

Merge tag 'drm-next-2024-11-21' of https://gitlab.freedesktop.org/drm/kernel

Pull drm updates from Dave Airlie:
 "There's a lot of rework, the panic helper support is being added to
  more drivers, v3d gets support for HW superpages, scheduler
  documentation, drm client and video aperture reworks, some new
  MAINTAINERS added, amdgpu has the usual lots of IP refactors, Intel
  has some Pantherlake enablement and xe is getting some SRIOV bits, but
  just lots of stuff everywhere.

  core:
   - split DSC helpers from DP helpers
   - clang build fixes for drm/mm test
   - drop simple pipeline support for gem vram
   - document submission error signaling
   - move drm_rect to drm core module from kms helper
   - add default client setup to most drivers
   - move to video aperture helpers instead of drm ones

  tests:
   - new framebuffer tests

  ttm:
   - remove swapped and pinned BOs from TTM lru

  panic:
   - fix uninit spinlock
   - add ABGR2101010 support

  bridge:
   - add TI TDP158 support
   - use standard PM OPS

  dma-fence:
   - use read_trylock instead of read_lock to help lockdep

  scheduler:
   - add errno to sched start to report different errors
   - add locking to drm_sched_entity_modify_sched
   - improve documentation

  xe:
   - add drm_line_printer
   - lots of refactoring
   - Enable Xe2 + PES disaggregation
   - add new ARL PCI ID
   - SRIOV development work
   - fix exec unnecessary implicit fence
   - define and parse OA sync props
   - forcewake refactoring

  i915:
   - Enable BMG/LNL ultra joiner
   - Enable 10bpx + CCS scanout on ICL+, fp16/CCS on TGL+
   - use DSB for plane/color mgmt
   - Arrow lake PCI IDs
   - lots of i915/xe display refactoring
   - enable PXP GuC autoteardown
   - Pantherlake (PTL) Xe3 LPD display enablement
   - Allow fastset HDR infoframe changes
   - write DP source OUI for non-eDP sinks
   - share PCI IDs between i915 and xe

  amdgpu:
   - SDMA queue reset support
   - SMU 13.0.6, JPEG 4.0.3 updates
   - Initial runtime repartitioning support
   - rework IP structs for multiple IP instances
   - Fetch EDID from _DDC if available
   - SMU13 zero rpm user control
   - lots of fixes/cleanups

  amdkfd:
   - Increase event FIFO size
   - add topology cap flag for per queue reset

  msm:
   - DPU:
      - SA8775P support
      - (disabled by default) MSM8917, MSM8937, MSM8953 and MSM8996 support
      - Enable large framebuffer support
      - Drop MSM8998 and SDM845
   - DP:
      - SA8775P support
   - GPU:
      - a7xx preemption support
      - Adreno A663 support

  ast:
   - warn about unsupported TX chips

  ivpu:
   - add coredump
   - add pantherlake support

  rockchip:
   - 4K@60Hz display enablement
   - generate pll programming tables

  panthor:
   - add timestamp query API
   - add realtime group priority
   - add fdinfo support

  etnaviv:
   - improve handling of DMA address limits
   - improve GPU hangcheck

  exynos:
   - Decon Exynos7870 support

  mediatek:
   - add OF graph support

  omap:
   - locking fixes

  bochs:
   - convert to gem/shmem from simpledrm

  v3d:
   - support big/super pages
   - add gemfs

  vc4:
   - BCM2712 support refactoring
   - add YUV444 format support

  udmabuf:
   - folio related fixes

  nouveau:
   - add panic support on nv50+"

* tag 'drm-next-2024-11-21' of https://gitlab.freedesktop.org/drm/kernel: (1583 commits)
  drm/xe/guc: Fix dereference before NULL check
  drm/amd: Fix initialization mistake for NBIO 7.7.0
  Revert "drm/amd/display: parse umc_info or vram_info based on ASIC"
  drm/amd/display: Fix failure to read vram info due to static BP_RESULT
  drm/amdgpu: enable GTT fallback handling for dGPUs only
  drm/amd/amdgpu: limit single process inside MES
  drm/fourcc: add AMD_FMT_MOD_TILE_GFX9_4K_D_X
  drm/amdgpu/mes12: correct kiq unmap latency
  drm/amdgpu: Support vcn and jpeg error info parsing
  drm/amd : Update MES API header file for v11 & v12
  drm/amd/amdkfd: add/remove kfd queues on start/stop KFD scheduling
  drm/amdkfd: change kfd process kref count at creation
  drm/amdgpu: Cleanup shift coding style
  drm/amd/amdgpu: Increase MES log buffer to dump mes scratch data
  drm/amdgpu: Implement virt req_ras_err_count
  drm/amdgpu: VF Query RAS Caps from Host if supported
  drm/amdgpu: Add msg handlers for SRIOV RAS Telemetry
  drm/amdgpu: Update SRIOV Exchange Headers for RAS Telemetry Support
  drm/amd/display: 3.2.309
  drm/amd/display: Adjust VSDB parser for replay feature
  ...
2024-11-21 14:56:17 -08:00

811 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/bug.h>
#include <linux/aperture.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_client_setup.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_fbdev_shmem.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_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_probe_helper.h>
#include <video/vga.h>
/* ---------------------------------------------------------------------- */
#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA 0x01CF
#define VBE_DISPI_INDEX_ID 0x0
#define VBE_DISPI_INDEX_XRES 0x1
#define VBE_DISPI_INDEX_YRES 0x2
#define VBE_DISPI_INDEX_BPP 0x3
#define VBE_DISPI_INDEX_ENABLE 0x4
#define VBE_DISPI_INDEX_BANK 0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
#define VBE_DISPI_INDEX_X_OFFSET 0x8
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
#define VBE_DISPI_ID0 0xB0C0
#define VBE_DISPI_ID1 0xB0C1
#define VBE_DISPI_ID2 0xB0C2
#define VBE_DISPI_ID3 0xB0C3
#define VBE_DISPI_ID4 0xB0C4
#define VBE_DISPI_ID5 0xB0C5
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_GETCAPS 0x02
#define VBE_DISPI_8BIT_DAC 0x20
#define VBE_DISPI_LFB_ENABLED 0x40
#define VBE_DISPI_NOCLEARMEM 0x80
static int bochs_modeset = -1;
static int defx = 1024;
static int defy = 768;
module_param_named(modeset, bochs_modeset, int, 0444);
MODULE_PARM_DESC(modeset, "enable/disable kernel modesetting");
module_param(defx, int, 0444);
module_param(defy, int, 0444);
MODULE_PARM_DESC(defx, "default x resolution");
MODULE_PARM_DESC(defy, "default y resolution");
/* ---------------------------------------------------------------------- */
enum bochs_types {
BOCHS_QEMU_STDVGA,
BOCHS_SIMICS,
BOCHS_UNKNOWN,
};
struct bochs_device {
struct drm_device dev;
/* hw */
void __iomem *mmio;
int ioports;
void __iomem *fb_map;
unsigned long fb_base;
unsigned long fb_size;
unsigned long qext_size;
/* mode */
u16 xres;
u16 yres;
u16 yres_virtual;
u32 stride;
u32 bpp;
/* drm */
struct drm_plane primary_plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
};
static struct bochs_device *to_bochs_device(const struct drm_device *dev)
{
return container_of(dev, struct bochs_device, dev);
}
/* ---------------------------------------------------------------------- */
static __always_inline bool bochs_uses_mmio(struct bochs_device *bochs)
{
return !IS_ENABLED(CONFIG_HAS_IOPORT) || bochs->mmio;
}
static void bochs_vga_writeb(struct bochs_device *bochs, u16 ioport, u8 val)
{
if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df))
return;
if (bochs_uses_mmio(bochs)) {
int offset = ioport - 0x3c0 + 0x400;
writeb(val, bochs->mmio + offset);
} else {
outb(val, ioport);
}
}
static u8 bochs_vga_readb(struct bochs_device *bochs, u16 ioport)
{
if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df))
return 0xff;
if (bochs_uses_mmio(bochs)) {
int offset = ioport - 0x3c0 + 0x400;
return readb(bochs->mmio + offset);
} else {
return inb(ioport);
}
}
static u16 bochs_dispi_read(struct bochs_device *bochs, u16 reg)
{
u16 ret = 0;
if (bochs_uses_mmio(bochs)) {
int offset = 0x500 + (reg << 1);
ret = readw(bochs->mmio + offset);
} else {
outw(reg, VBE_DISPI_IOPORT_INDEX);
ret = inw(VBE_DISPI_IOPORT_DATA);
}
return ret;
}
static void bochs_dispi_write(struct bochs_device *bochs, u16 reg, u16 val)
{
if (bochs_uses_mmio(bochs)) {
int offset = 0x500 + (reg << 1);
writew(val, bochs->mmio + offset);
} else {
outw(reg, VBE_DISPI_IOPORT_INDEX);
outw(val, VBE_DISPI_IOPORT_DATA);
}
}
static void bochs_hw_set_big_endian(struct bochs_device *bochs)
{
if (bochs->qext_size < 8)
return;
writel(0xbebebebe, bochs->mmio + 0x604);
}
static void bochs_hw_set_little_endian(struct bochs_device *bochs)
{
if (bochs->qext_size < 8)
return;
writel(0x1e1e1e1e, bochs->mmio + 0x604);
}
#ifdef __BIG_ENDIAN
#define bochs_hw_set_native_endian(_b) bochs_hw_set_big_endian(_b)
#else
#define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b)
#endif
static int bochs_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
{
struct bochs_device *bochs = data;
size_t i, start = block * EDID_LENGTH;
if (!bochs->mmio)
return -1;
if (start + len > 0x400 /* vga register offset */)
return -1;
for (i = 0; i < len; i++)
buf[i] = readb(bochs->mmio + start + i);
return 0;
}
static const struct drm_edid *bochs_hw_read_edid(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct bochs_device *bochs = to_bochs_device(dev);
u8 header[8];
/* check header to detect whenever edid support is enabled in qemu */
bochs_get_edid_block(bochs, header, 0, ARRAY_SIZE(header));
if (drm_edid_header_is_valid(header) != 8)
return NULL;
drm_dbg(dev, "Found EDID data blob.\n");
return drm_edid_read_custom(connector, bochs_get_edid_block, bochs);
}
static int bochs_hw_init(struct bochs_device *bochs)
{
struct drm_device *dev = &bochs->dev;
struct pci_dev *pdev = to_pci_dev(dev->dev);
unsigned long addr, size, mem, ioaddr, iosize;
u16 id;
if (pdev->resource[2].flags & IORESOURCE_MEM) {
ioaddr = pci_resource_start(pdev, 2);
iosize = pci_resource_len(pdev, 2);
/* mmio bar with vga and bochs registers present */
if (!devm_request_mem_region(&pdev->dev, ioaddr, iosize, "bochs-drm")) {
DRM_ERROR("Cannot request mmio region\n");
return -EBUSY;
}
bochs->mmio = devm_ioremap(&pdev->dev, ioaddr, iosize);
if (bochs->mmio == NULL) {
DRM_ERROR("Cannot map mmio region\n");
return -ENOMEM;
}
} else if (IS_ENABLED(CONFIG_HAS_IOPORT)) {
ioaddr = VBE_DISPI_IOPORT_INDEX;
iosize = 2;
if (!devm_request_region(&pdev->dev, ioaddr, iosize, "bochs-drm")) {
DRM_ERROR("Cannot request ioports\n");
return -EBUSY;
}
bochs->ioports = 1;
} else {
dev_err(dev->dev, "I/O ports are not supported\n");
return -EIO;
}
id = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID);
mem = bochs_dispi_read(bochs, VBE_DISPI_INDEX_VIDEO_MEMORY_64K)
* 64 * 1024;
if ((id & 0xfff0) != VBE_DISPI_ID0) {
DRM_ERROR("ID mismatch\n");
return -ENODEV;
}
if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0)
return -ENODEV;
addr = pci_resource_start(pdev, 0);
size = pci_resource_len(pdev, 0);
if (addr == 0)
return -ENODEV;
if (size != mem) {
DRM_ERROR("Size mismatch: pci=%ld, bochs=%ld\n",
size, mem);
size = min(size, mem);
}
if (!devm_request_mem_region(&pdev->dev, addr, size, "bochs-drm"))
DRM_WARN("Cannot request framebuffer, boot fb still active?\n");
bochs->fb_map = devm_ioremap_wc(&pdev->dev, addr, size);
if (bochs->fb_map == NULL) {
DRM_ERROR("Cannot map framebuffer\n");
return -ENOMEM;
}
bochs->fb_base = addr;
bochs->fb_size = size;
DRM_INFO("Found bochs VGA, ID 0x%x.\n", id);
DRM_INFO("Framebuffer size %ld kB @ 0x%lx, %s @ 0x%lx.\n",
size / 1024, addr,
bochs->ioports ? "ioports" : "mmio",
ioaddr);
if (bochs->mmio && pdev->revision >= 2) {
bochs->qext_size = readl(bochs->mmio + 0x600);
if (bochs->qext_size < 4 || bochs->qext_size > iosize) {
bochs->qext_size = 0;
goto noext;
}
DRM_DEBUG("Found qemu ext regs, size %ld\n",
bochs->qext_size);
bochs_hw_set_native_endian(bochs);
}
noext:
return 0;
}
static void bochs_hw_blank(struct bochs_device *bochs, bool blank)
{
DRM_DEBUG_DRIVER("hw_blank %d\n", blank);
/* enable color bit (so VGA_IS1_RC access works) */
bochs_vga_writeb(bochs, VGA_MIS_W, VGA_MIS_COLOR);
/* discard ar_flip_flop */
(void)bochs_vga_readb(bochs, VGA_IS1_RC);
/* blank or unblank; we need only update index and set 0x20 */
bochs_vga_writeb(bochs, VGA_ATT_W, blank ? 0 : 0x20);
}
static void bochs_hw_setmode(struct bochs_device *bochs, struct drm_display_mode *mode)
{
int idx;
if (!drm_dev_enter(&bochs->dev, &idx))
return;
bochs->xres = mode->hdisplay;
bochs->yres = mode->vdisplay;
bochs->bpp = 32;
bochs->stride = mode->hdisplay * (bochs->bpp / 8);
bochs->yres_virtual = bochs->fb_size / bochs->stride;
DRM_DEBUG_DRIVER("%dx%d @ %d bpp, vy %d\n",
bochs->xres, bochs->yres, bochs->bpp,
bochs->yres_virtual);
bochs_hw_blank(bochs, false);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, bochs->xres);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT,
bochs->yres_virtual);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE,
VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
drm_dev_exit(idx);
}
static void bochs_hw_setformat(struct bochs_device *bochs, const struct drm_format_info *format)
{
int idx;
if (!drm_dev_enter(&bochs->dev, &idx))
return;
DRM_DEBUG_DRIVER("format %c%c%c%c\n",
(format->format >> 0) & 0xff,
(format->format >> 8) & 0xff,
(format->format >> 16) & 0xff,
(format->format >> 24) & 0xff);
switch (format->format) {
case DRM_FORMAT_XRGB8888:
bochs_hw_set_little_endian(bochs);
break;
case DRM_FORMAT_BGRX8888:
bochs_hw_set_big_endian(bochs);
break;
default:
/* should not happen */
DRM_ERROR("%s: Huh? Got framebuffer format 0x%x",
__func__, format->format);
break;
}
drm_dev_exit(idx);
}
static void bochs_hw_setbase(struct bochs_device *bochs, int x, int y, int stride, u64 addr)
{
unsigned long offset;
unsigned int vx, vy, vwidth, idx;
if (!drm_dev_enter(&bochs->dev, &idx))
return;
bochs->stride = stride;
offset = (unsigned long)addr +
y * bochs->stride +
x * (bochs->bpp / 8);
vy = offset / bochs->stride;
vx = (offset % bochs->stride) * 8 / bochs->bpp;
vwidth = stride * 8 / bochs->bpp;
DRM_DEBUG_DRIVER("x %d, y %d, addr %llx -> offset %lx, vx %d, vy %d\n",
x, y, addr, offset, vx, vy);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, vwidth);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, vx);
bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, vy);
drm_dev_exit(idx);
}
/* ---------------------------------------------------------------------- */
static const uint32_t bochs_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_BGRX8888,
};
static int bochs_primary_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_crtc *new_crtc = new_plane_state->crtc;
struct drm_crtc_state *new_crtc_state = NULL;
int ret;
if (new_crtc)
new_crtc_state = drm_atomic_get_new_crtc_state(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, false);
if (ret)
return ret;
else if (!new_plane_state->visible)
return 0;
return 0;
}
static void bochs_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_device *dev = plane->dev;
struct bochs_device *bochs = to_bochs_device(dev);
struct drm_plane_state *plane_state = plane->state;
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(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;
if (!fb || !bochs->stride)
return;
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(bochs->fb_map);
iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, &damage));
drm_fb_memcpy(&dst, fb->pitches, shadow_plane_state->data, fb, &damage);
}
/* Always scanout image at VRAM offset 0 */
bochs_hw_setbase(bochs,
plane_state->crtc_x,
plane_state->crtc_y,
fb->pitches[0],
0);
bochs_hw_setformat(bochs, fb->format);
}
static const struct drm_plane_helper_funcs bochs_primary_plane_helper_funcs = {
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
.atomic_check = bochs_primary_plane_helper_atomic_check,
.atomic_update = bochs_primary_plane_helper_atomic_update,
};
static const struct drm_plane_funcs bochs_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
};
static void bochs_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
{
struct bochs_device *bochs = to_bochs_device(crtc->dev);
struct drm_crtc_state *crtc_state = crtc->state;
bochs_hw_setmode(bochs, &crtc_state->mode);
}
static int bochs_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (!crtc_state->enable)
return 0;
return drm_atomic_helper_check_crtc_primary_plane(crtc_state);
}
static void bochs_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
}
static void bochs_crtc_helper_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *crtc_state)
{
struct bochs_device *bochs = to_bochs_device(crtc->dev);
bochs_hw_blank(bochs, true);
}
static const struct drm_crtc_helper_funcs bochs_crtc_helper_funcs = {
.mode_set_nofb = bochs_crtc_helper_mode_set_nofb,
.atomic_check = bochs_crtc_helper_atomic_check,
.atomic_enable = bochs_crtc_helper_atomic_enable,
.atomic_disable = bochs_crtc_helper_atomic_disable,
};
static const struct drm_crtc_funcs bochs_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
static const struct drm_encoder_funcs bochs_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static int bochs_connector_helper_get_modes(struct drm_connector *connector)
{
const struct drm_edid *edid;
int count;
edid = bochs_hw_read_edid(connector);
if (edid) {
drm_edid_connector_update(connector, edid);
count = drm_edid_connector_add_modes(connector);
drm_edid_free(edid);
} else {
drm_edid_connector_update(connector, NULL);
count = drm_add_modes_noedid(connector, 8192, 8192);
drm_set_preferred_mode(connector, defx, defy);
}
return count;
}
static const struct drm_connector_helper_funcs bochs_connector_helper_funcs = {
.get_modes = bochs_connector_helper_get_modes,
};
static const struct drm_connector_funcs bochs_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static enum drm_mode_status bochs_mode_config_mode_valid(struct drm_device *dev,
const struct drm_display_mode *mode)
{
struct bochs_device *bochs = to_bochs_device(dev);
const struct drm_format_info *format = drm_format_info(DRM_FORMAT_XRGB8888);
u64 pitch;
if (drm_WARN_ON(dev, !format))
return MODE_ERROR;
pitch = drm_format_info_min_pitch(format, 0, mode->hdisplay);
if (!pitch)
return MODE_BAD_WIDTH;
if (mode->vdisplay > DIV_ROUND_DOWN_ULL(bochs->fb_size, pitch))
return MODE_MEM;
return MODE_OK;
}
static const struct drm_mode_config_funcs bochs_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.mode_valid = bochs_mode_config_mode_valid,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static int bochs_kms_init(struct bochs_device *bochs)
{
struct drm_device *dev = &bochs->dev;
struct drm_plane *primary_plane;
struct drm_crtc *crtc;
struct drm_connector *connector;
struct drm_encoder *encoder;
int ret;
ret = drmm_mode_config_init(dev);
if (ret)
return ret;
dev->mode_config.max_width = 8192;
dev->mode_config.max_height = 8192;
dev->mode_config.preferred_depth = 24;
dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
dev->mode_config.funcs = &bochs_mode_config_funcs;
primary_plane = &bochs->primary_plane;
ret = drm_universal_plane_init(dev, primary_plane, 0,
&bochs_primary_plane_funcs,
bochs_primary_plane_formats,
ARRAY_SIZE(bochs_primary_plane_formats),
NULL,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ret;
drm_plane_helper_add(primary_plane, &bochs_primary_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(primary_plane);
crtc = &bochs->crtc;
ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
&bochs_crtc_funcs, NULL);
if (ret)
return ret;
drm_crtc_helper_add(crtc, &bochs_crtc_helper_funcs);
encoder = &bochs->encoder;
ret = drm_encoder_init(dev, encoder, &bochs_encoder_funcs,
DRM_MODE_ENCODER_VIRTUAL, NULL);
if (ret)
return ret;
encoder->possible_crtcs = drm_crtc_mask(crtc);
connector = &bochs->connector;
ret = drm_connector_init(dev, connector, &bochs_connector_funcs,
DRM_MODE_CONNECTOR_VIRTUAL);
if (ret)
return ret;
drm_connector_helper_add(connector, &bochs_connector_helper_funcs);
drm_connector_attach_edid_property(connector);
drm_connector_attach_encoder(connector, encoder);
drm_mode_config_reset(dev);
return 0;
}
/* ---------------------------------------------------------------------- */
/* drm interface */
static int bochs_load(struct bochs_device *bochs)
{
int ret;
ret = bochs_hw_init(bochs);
if (ret)
return ret;
ret = bochs_kms_init(bochs);
if (ret)
return ret;
return 0;
}
DEFINE_DRM_GEM_FOPS(bochs_fops);
static const struct drm_driver bochs_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &bochs_fops,
.name = "bochs-drm",
.desc = "bochs dispi vga interface (qemu stdvga)",
.date = "20130925",
.major = 1,
.minor = 0,
DRM_GEM_SHMEM_DRIVER_OPS,
DRM_FBDEV_SHMEM_DRIVER_OPS,
};
/* ---------------------------------------------------------------------- */
/* pm interface */
#ifdef CONFIG_PM_SLEEP
static int bochs_pm_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
return drm_mode_config_helper_suspend(drm_dev);
}
static int bochs_pm_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
return drm_mode_config_helper_resume(drm_dev);
}
#endif
static const struct dev_pm_ops bochs_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,
bochs_pm_resume)
};
/* ---------------------------------------------------------------------- */
/* pci interface */
static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct bochs_device *bochs;
struct drm_device *dev;
int ret;
ret = aperture_remove_conflicting_pci_devices(pdev, bochs_driver.name);
if (ret)
return ret;
bochs = devm_drm_dev_alloc(&pdev->dev, &bochs_driver, struct bochs_device, dev);
if (IS_ERR(bochs))
return PTR_ERR(bochs);
dev = &bochs->dev;
ret = pcim_enable_device(pdev);
if (ret)
goto err_free_dev;
pci_set_drvdata(pdev, dev);
ret = bochs_load(bochs);
if (ret)
goto err_free_dev;
ret = drm_dev_register(dev, 0);
if (ret)
goto err_free_dev;
drm_client_setup(dev, NULL);
return ret;
err_free_dev:
drm_dev_put(dev);
return ret;
}
static void bochs_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_dev_unplug(dev);
drm_atomic_helper_shutdown(dev);
drm_dev_put(dev);
}
static void bochs_pci_shutdown(struct pci_dev *pdev)
{
drm_atomic_helper_shutdown(pci_get_drvdata(pdev));
}
static const struct pci_device_id bochs_pci_tbl[] = {
{
.vendor = 0x1234,
.device = 0x1111,
.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
.subdevice = PCI_SUBDEVICE_ID_QEMU,
.driver_data = BOCHS_QEMU_STDVGA,
},
{
.vendor = 0x1234,
.device = 0x1111,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = BOCHS_UNKNOWN,
},
{
.vendor = 0x4321,
.device = 0x1111,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = BOCHS_SIMICS,
},
{ /* end of list */ }
};
static struct pci_driver bochs_pci_driver = {
.name = "bochs-drm",
.id_table = bochs_pci_tbl,
.probe = bochs_pci_probe,
.remove = bochs_pci_remove,
.shutdown = bochs_pci_shutdown,
.driver.pm = &bochs_pm_ops,
};
/* ---------------------------------------------------------------------- */
/* module init/exit */
drm_module_pci_driver_if_modeset(bochs_pci_driver, bochs_modeset);
MODULE_DEVICE_TABLE(pci, bochs_pci_tbl);
MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
MODULE_DESCRIPTION("DRM Support for bochs dispi vga interface (qemu stdvga)");
MODULE_LICENSE("GPL");