Add GPU driver interrupt handling, Intel GPU PCI config, and display improvements
AMD display driver: expanded DCN pipeline setup with plane/controller/stream mapping. Intel driver: cleaned up module structure. New interrupt module for MSI-X vector management across GPU drivers. PCID config endpoint patch and Intel GPU TOML for automatic driver spawning. Expanded redox_stubs with additional kernel API shims. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
# PCID configuration for Intel GPU auto-detection
|
||||||
|
# When pcid detects an Intel GPU (vendor 0x8086, class 0x03),
|
||||||
|
# it launches redox-drm with the PCI device location.
|
||||||
|
|
||||||
|
[[device]]
|
||||||
|
vendor = 0x8086
|
||||||
|
class = 0x03
|
||||||
|
subclass = 0x00
|
||||||
|
command = ["redox-drm"]
|
||||||
|
args = ["$BUS", "$DEV", "$FUNC"]
|
||||||
|
|
||||||
|
[[device]]
|
||||||
|
vendor = 0x8086
|
||||||
|
class = 0x03
|
||||||
|
subclass = 0x02
|
||||||
|
command = ["redox-drm"]
|
||||||
|
args = ["$BUS", "$DEV", "$FUNC"]
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||||
|
index ce55b33f..c06bdec4 100644
|
||||||
|
--- a/drivers/pcid/src/scheme.rs
|
||||||
|
+++ b/drivers/pcid/src/scheme.rs
|
||||||
|
@@ -21,6 +21,7 @@ enum Handle {
|
||||||
|
TopLevel { entries: Vec<String> },
|
||||||
|
Access,
|
||||||
|
Device,
|
||||||
|
+ Config { addr: PciAddress },
|
||||||
|
Channel { addr: PciAddress, st: ChannelState },
|
||||||
|
SchemeRoot,
|
||||||
|
}
|
||||||
|
@@ -30,14 +31,20 @@ struct HandleWrapper {
|
||||||
|
}
|
||||||
|
impl Handle {
|
||||||
|
fn is_file(&self) -> bool {
|
||||||
|
- matches!(self, Self::Access | Self::Channel { .. })
|
||||||
|
+ matches!(
|
||||||
|
+ self,
|
||||||
|
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
fn is_dir(&self) -> bool {
|
||||||
|
!self.is_file()
|
||||||
|
}
|
||||||
|
// TODO: capability rather than root
|
||||||
|
fn requires_root(&self) -> bool {
|
||||||
|
- matches!(self, Self::Access | Self::Channel { .. })
|
||||||
|
+ matches!(
|
||||||
|
+ self,
|
||||||
|
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
fn is_scheme_root(&self) -> bool {
|
||||||
|
matches!(self, Self::SchemeRoot)
|
||||||
|
@@ -153,6 +160,7 @@ impl SchemeSync for PciScheme {
|
||||||
|
let (len, mode) = match handle.inner {
|
||||||
|
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||||
|
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||||
|
+ Handle::Config { .. } => (256, MODE_CHR | 0o600),
|
||||||
|
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||||
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||||
|
};
|
||||||
|
@@ -177,6 +185,18 @@ impl SchemeSync for PciScheme {
|
||||||
|
match handle.inner {
|
||||||
|
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||||
|
Handle::Device => Err(Error::new(EISDIR)),
|
||||||
|
+ Handle::Config { addr } => {
|
||||||
|
+ let offset = _offset as u16;
|
||||||
|
+ let dword_offset = offset & !0x3;
|
||||||
|
+ let byte_offset = (offset & 0x3) as usize;
|
||||||
|
+ let bytes_to_read = buf.len().min(4 - byte_offset);
|
||||||
|
+
|
||||||
|
+ let dword = unsafe { self.pcie.read(addr, dword_offset) };
|
||||||
|
+ let bytes = dword.to_le_bytes();
|
||||||
|
+ buf[..bytes_to_read]
|
||||||
|
+ .copy_from_slice(&bytes[byte_offset..byte_offset + bytes_to_read]);
|
||||||
|
+ Ok(bytes_to_read)
|
||||||
|
+ }
|
||||||
|
Handle::Channel {
|
||||||
|
addr: _,
|
||||||
|
ref mut st,
|
||||||
|
@@ -214,7 +234,9 @@ impl SchemeSync for PciScheme {
|
||||||
|
return Ok(buf);
|
||||||
|
}
|
||||||
|
Handle::Device => DEVICE_CONTENTS,
|
||||||
|
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||||
|
+ Handle::Access | Handle::Config { .. } | Handle::Channel { .. } => {
|
||||||
|
+ return Err(Error::new(ENOTDIR));
|
||||||
|
+ }
|
||||||
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -244,6 +266,20 @@ impl SchemeSync for PciScheme {
|
||||||
|
}
|
||||||
|
|
||||||
|
match handle.inner {
|
||||||
|
+ Handle::Config { addr } => {
|
||||||
|
+ let offset = _offset as u16;
|
||||||
|
+ let dword_offset = offset & !0x3;
|
||||||
|
+ let byte_offset = (offset & 0x3) as usize;
|
||||||
|
+ let bytes_to_write = buf.len().min(4 - byte_offset);
|
||||||
|
+
|
||||||
|
+ let mut dword = unsafe { self.pcie.read(addr, dword_offset) };
|
||||||
|
+ let mut bytes = dword.to_le_bytes();
|
||||||
|
+ bytes[byte_offset..byte_offset + bytes_to_write]
|
||||||
|
+ .copy_from_slice(&buf[..bytes_to_write]);
|
||||||
|
+ dword = u32::from_le_bytes(bytes);
|
||||||
|
+ unsafe { self.pcie.write(addr, dword_offset, dword) };
|
||||||
|
+ Ok(buf.len())
|
||||||
|
+ }
|
||||||
|
Handle::Channel { addr, ref mut st } => {
|
||||||
|
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||||
|
}
|
||||||
|
@@ -339,6 +375,10 @@ impl PciScheme {
|
||||||
|
func.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ Some(HandleWrapper {
|
||||||
|
+ inner: Handle::Config { .. },
|
||||||
|
+ ..
|
||||||
|
+ }) => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -365,6 +405,7 @@ impl PciScheme {
|
||||||
|
let path = &after[1..];
|
||||||
|
|
||||||
|
match path {
|
||||||
|
+ "config" => Handle::Config { addr },
|
||||||
|
"channel" => {
|
||||||
|
if func.enabled {
|
||||||
|
return Err(Error::new(ENOLCK));
|
||||||
@@ -556,3 +556,116 @@ index 0933f638..d4b0f3d0 100644
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||||
|
index ce55b33f..c06bdec4 100644
|
||||||
|
--- a/drivers/pcid/src/scheme.rs
|
||||||
|
+++ b/drivers/pcid/src/scheme.rs
|
||||||
|
@@ -21,6 +21,7 @@ enum Handle {
|
||||||
|
TopLevel { entries: Vec<String> },
|
||||||
|
Access,
|
||||||
|
Device,
|
||||||
|
+ Config { addr: PciAddress },
|
||||||
|
Channel { addr: PciAddress, st: ChannelState },
|
||||||
|
SchemeRoot,
|
||||||
|
}
|
||||||
|
@@ -30,14 +31,20 @@ struct HandleWrapper {
|
||||||
|
}
|
||||||
|
impl Handle {
|
||||||
|
fn is_file(&self) -> bool {
|
||||||
|
- matches!(self, Self::Access | Self::Channel { .. })
|
||||||
|
+ matches!(
|
||||||
|
+ self,
|
||||||
|
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
fn is_dir(&self) -> bool {
|
||||||
|
!self.is_file()
|
||||||
|
}
|
||||||
|
// TODO: capability rather than root
|
||||||
|
fn requires_root(&self) -> bool {
|
||||||
|
- matches!(self, Self::Access | Self::Channel { .. })
|
||||||
|
+ matches!(
|
||||||
|
+ self,
|
||||||
|
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
fn is_scheme_root(&self) -> bool {
|
||||||
|
matches!(self, Self::SchemeRoot)
|
||||||
|
@@ -153,6 +160,7 @@ impl SchemeSync for PciScheme {
|
||||||
|
let (len, mode) = match handle.inner {
|
||||||
|
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||||
|
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||||
|
+ Handle::Config { .. } => (256, MODE_CHR | 0o600),
|
||||||
|
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||||
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||||
|
};
|
||||||
|
@@ -177,6 +185,18 @@ impl SchemeSync for PciScheme {
|
||||||
|
match handle.inner {
|
||||||
|
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||||
|
Handle::Device => Err(Error::new(EISDIR)),
|
||||||
|
+ Handle::Config { addr } => {
|
||||||
|
+ let offset = _offset as u16;
|
||||||
|
+ let dword_offset = offset & !0x3;
|
||||||
|
+ let byte_offset = (offset & 0x3) as usize;
|
||||||
|
+ let bytes_to_read = buf.len().min(4 - byte_offset);
|
||||||
|
+
|
||||||
|
+ let dword = unsafe { self.pcie.read(addr, dword_offset) };
|
||||||
|
+ let bytes = dword.to_le_bytes();
|
||||||
|
+ buf[..bytes_to_read]
|
||||||
|
+ .copy_from_slice(&bytes[byte_offset..byte_offset + bytes_to_read]);
|
||||||
|
+ Ok(bytes_to_read)
|
||||||
|
+ }
|
||||||
|
Handle::Channel {
|
||||||
|
addr: _,
|
||||||
|
ref mut st,
|
||||||
|
@@ -214,7 +234,9 @@ impl SchemeSync for PciScheme {
|
||||||
|
return Ok(buf);
|
||||||
|
}
|
||||||
|
Handle::Device => DEVICE_CONTENTS,
|
||||||
|
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||||
|
+ Handle::Access | Handle::Config { .. } | Handle::Channel { .. } => {
|
||||||
|
+ return Err(Error::new(ENOTDIR));
|
||||||
|
+ }
|
||||||
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -244,6 +266,20 @@ impl SchemeSync for PciScheme {
|
||||||
|
}
|
||||||
|
|
||||||
|
match handle.inner {
|
||||||
|
+ Handle::Config { addr } => {
|
||||||
|
+ let offset = _offset as u16;
|
||||||
|
+ let dword_offset = offset & !0x3;
|
||||||
|
+ let byte_offset = (offset & 0x3) as usize;
|
||||||
|
+ let bytes_to_write = buf.len().min(4 - byte_offset);
|
||||||
|
+
|
||||||
|
+ let mut dword = unsafe { self.pcie.read(addr, dword_offset) };
|
||||||
|
+ let mut bytes = dword.to_le_bytes();
|
||||||
|
+ bytes[byte_offset..byte_offset + bytes_to_write]
|
||||||
|
+ .copy_from_slice(&buf[..bytes_to_write]);
|
||||||
|
+ dword = u32::from_le_bytes(bytes);
|
||||||
|
+ unsafe { self.pcie.write(addr, dword_offset, dword) };
|
||||||
|
+ Ok(buf.len())
|
||||||
|
+ }
|
||||||
|
Handle::Channel { addr, ref mut st } => {
|
||||||
|
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||||
|
}
|
||||||
|
@@ -339,6 +375,10 @@ impl PciScheme {
|
||||||
|
func.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ Some(HandleWrapper {
|
||||||
|
+ inner: Handle::Config { .. },
|
||||||
|
+ ..
|
||||||
|
+ }) => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -365,6 +405,7 @@ impl PciScheme {
|
||||||
|
let path = &after[1..];
|
||||||
|
|
||||||
|
match path {
|
||||||
|
+ "config" => Handle::Config { addr },
|
||||||
|
"channel" => {
|
||||||
|
if func.enabled {
|
||||||
|
return Err(Error::new(ENOLCK));
|
||||||
|
|||||||
@@ -218,6 +218,9 @@ struct pci_dev {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern struct pci_dev *redox_pci_find_amd_gpu(void);
|
extern struct pci_dev *redox_pci_find_amd_gpu(void);
|
||||||
|
extern void redox_pci_set_device_info(u16 vendor, u16 device, u8 revision,
|
||||||
|
u8 irq, u64 bar0_addr, u64 bar0_size,
|
||||||
|
u64 bar2_addr, u64 bar2_size);
|
||||||
extern void redox_pci_dev_put(struct pci_dev *pdev);
|
extern void redox_pci_dev_put(struct pci_dev *pdev);
|
||||||
extern int redox_pci_enable_device(struct pci_dev *pdev);
|
extern int redox_pci_enable_device(struct pci_dev *pdev);
|
||||||
extern void redox_pci_set_master(struct pci_dev *pdev);
|
extern void redox_pci_set_master(struct pci_dev *pdev);
|
||||||
|
|||||||
@@ -214,22 +214,50 @@ void redox_dma_free_coherent(size_t size, void *vaddr, dma_addr_t dma_handle)
|
|||||||
free(vaddr);
|
free(vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCI device state — populated by the Rust side via redox_pci_set_device_info()
|
||||||
|
* before amdgpu_redox_init() is called. redox_pci_find_amd_gpu() returns a
|
||||||
|
* pointer to this struct, or NULL if the device info has not been set yet.
|
||||||
|
*/
|
||||||
|
static struct pci_dev g_pci_dev;
|
||||||
|
static int g_pci_dev_populated;
|
||||||
|
|
||||||
|
void redox_pci_set_device_info(u16 vendor, u16 device, u8 revision,
|
||||||
|
u8 irq, u64 bar0_addr, u64 bar0_size,
|
||||||
|
u64 bar2_addr, u64 bar2_size)
|
||||||
|
{
|
||||||
|
memset(&g_pci_dev, 0, sizeof(g_pci_dev));
|
||||||
|
g_pci_dev.vendor = vendor;
|
||||||
|
g_pci_dev.device = device;
|
||||||
|
g_pci_dev.revision = revision;
|
||||||
|
g_pci_dev.irq = irq;
|
||||||
|
g_pci_dev.resource_start[0] = (phys_addr_t)bar0_addr;
|
||||||
|
g_pci_dev.resource_len[0] = bar0_size;
|
||||||
|
g_pci_dev.resource_flags[0] = IORESOURCE_MEM;
|
||||||
|
g_pci_dev.resource_start[2] = (phys_addr_t)bar2_addr;
|
||||||
|
g_pci_dev.resource_len[2] = bar2_size;
|
||||||
|
g_pci_dev.resource_flags[2] = IORESOURCE_MEM;
|
||||||
|
g_pci_dev.driver_data = NULL;
|
||||||
|
g_pci_dev.mmio_base = NULL;
|
||||||
|
g_pci_dev.is_amdgpu = 1;
|
||||||
|
g_pci_dev_populated = 1;
|
||||||
|
|
||||||
|
printk("PCI device info set: vendor=%#06x device=%#06x rev=%#04x irq=%u "
|
||||||
|
"bar0=%#llx+%#llx bar2=%#llx+%#llx\n",
|
||||||
|
vendor, device, revision, irq,
|
||||||
|
(unsigned long long)bar0_addr, (unsigned long long)bar0_size,
|
||||||
|
(unsigned long long)bar2_addr, (unsigned long long)bar2_size);
|
||||||
|
}
|
||||||
|
|
||||||
struct pci_dev *redox_pci_find_amd_gpu(void)
|
struct pci_dev *redox_pci_find_amd_gpu(void)
|
||||||
{
|
{
|
||||||
static struct pci_dev dev = {
|
if (!g_pci_dev_populated) {
|
||||||
.vendor = 0x1002,
|
pr_err("redox_pci_find_amd_gpu: device info not set — "
|
||||||
.device = 0,
|
"call redox_pci_set_device_info() first\n");
|
||||||
.revision = 0,
|
return NULL;
|
||||||
.irq = 0,
|
}
|
||||||
.resource_start = {0},
|
|
||||||
.resource_len = {0},
|
|
||||||
.resource_flags = {IORESOURCE_MEM, 0, 0, 0, 0, 0},
|
|
||||||
.driver_data = NULL,
|
|
||||||
.mmio_base = NULL,
|
|
||||||
.is_amdgpu = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
return &dev;
|
return &g_pci_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
void redox_pci_dev_put(struct pci_dev *pdev)
|
void redox_pci_dev_put(struct pci_dev *pdev)
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ pub struct ConnectorInfoFFI {
|
|||||||
|
|
||||||
#[cfg(not(no_amdgpu_c))]
|
#[cfg(not(no_amdgpu_c))]
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
/// Full hardware initialization: sets MMIO base, FB aperture, PCI device.
|
|
||||||
/// Must be called before any other DC function — the C side depends on
|
|
||||||
/// globals populated here (g_mmio_base, g_fb_phys, etc.).
|
|
||||||
#[link_name = "amdgpu_redox_init"]
|
#[link_name = "amdgpu_redox_init"]
|
||||||
fn ffi_amdgpu_redox_init(
|
fn ffi_amdgpu_redox_init(
|
||||||
mmio_base: *const u8,
|
mmio_base: *const u8,
|
||||||
@@ -40,9 +37,20 @@ unsafe extern "C" {
|
|||||||
#[link_name = "amdgpu_dc_set_crtc"]
|
#[link_name = "amdgpu_dc_set_crtc"]
|
||||||
fn ffi_amdgpu_dc_set_crtc(crtc_id: i32, fb_addr: u64, width: u32, height: u32) -> i32;
|
fn ffi_amdgpu_dc_set_crtc(crtc_id: i32, fb_addr: u64, width: u32, height: u32) -> i32;
|
||||||
|
|
||||||
/// Releases global state in the C layer.
|
|
||||||
#[link_name = "amdgpu_redox_cleanup"]
|
#[link_name = "amdgpu_redox_cleanup"]
|
||||||
fn ffi_amdgpu_redox_cleanup();
|
fn ffi_amdgpu_redox_cleanup();
|
||||||
|
|
||||||
|
#[link_name = "redox_pci_set_device_info"]
|
||||||
|
fn ffi_redox_pci_set_device_info(
|
||||||
|
vendor: u16,
|
||||||
|
device: u16,
|
||||||
|
revision: u8,
|
||||||
|
irq: u8,
|
||||||
|
bar0_addr: u64,
|
||||||
|
bar0_size: u64,
|
||||||
|
bar2_addr: u64,
|
||||||
|
bar2_size: u64,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(no_amdgpu_c)]
|
#[cfg(no_amdgpu_c)]
|
||||||
@@ -94,6 +102,27 @@ fn amdgpu_dc_cleanup() {
|
|||||||
FALLBACK_MMIO_SIZE.store(0, Ordering::Relaxed);
|
FALLBACK_MMIO_SIZE.store(0, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_pci_device_info(
|
||||||
|
vendor: u16,
|
||||||
|
device: u16,
|
||||||
|
revision: u8,
|
||||||
|
irq: u32,
|
||||||
|
bar0_addr: u64,
|
||||||
|
bar0_size: u64,
|
||||||
|
bar2_addr: u64,
|
||||||
|
bar2_size: u64,
|
||||||
|
) {
|
||||||
|
#[cfg(not(no_amdgpu_c))]
|
||||||
|
unsafe {
|
||||||
|
ffi_redox_pci_set_device_info(
|
||||||
|
vendor, device, revision, irq as u8, bar0_addr, bar0_size, bar2_addr, bar2_size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let _ = (
|
||||||
|
vendor, device, revision, irq, bar0_addr, bar0_size, bar2_addr, bar2_size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(no_amdgpu_c))]
|
#[cfg(not(no_amdgpu_c))]
|
||||||
fn amdgpu_dc_init(mmio_base: *const u8, mmio_size: usize) -> i32 {
|
fn amdgpu_dc_init(mmio_base: *const u8, mmio_size: usize) -> i32 {
|
||||||
unsafe { ffi_amdgpu_redox_init(mmio_base, mmio_size, 0, 0) }
|
unsafe { ffi_amdgpu_redox_init(mmio_base, mmio_size, 0, 0) }
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use redox_driver_sys::irq::IrqHandle;
|
|
||||||
use redox_driver_sys::memory::MmioRegion;
|
use redox_driver_sys::memory::MmioRegion;
|
||||||
use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo};
|
use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo};
|
||||||
|
|
||||||
use crate::driver::{DriverError, GpuDriver, Result};
|
use crate::driver::{DriverError, GpuDriver, Result};
|
||||||
|
use crate::drivers::interrupt::InterruptHandle;
|
||||||
use crate::gem::{GemHandle, GemManager};
|
use crate::gem::{GemHandle, GemManager};
|
||||||
use crate::kms::connector::{synthetic_edid, Connector};
|
use crate::kms::connector::{synthetic_edid, Connector};
|
||||||
use crate::kms::crtc::Crtc;
|
use crate::kms::crtc::Crtc;
|
||||||
@@ -51,7 +51,7 @@ pub enum IrqEvent {
|
|||||||
pub struct AmdDriver {
|
pub struct AmdDriver {
|
||||||
info: PciDeviceInfo,
|
info: PciDeviceInfo,
|
||||||
mmio: MmioRegion,
|
mmio: MmioRegion,
|
||||||
irq_handle: Option<IrqHandle>,
|
irq_handle: Option<InterruptHandle>,
|
||||||
display: DisplayCore,
|
display: DisplayCore,
|
||||||
gem: Mutex<GemManager>,
|
gem: Mutex<GemManager>,
|
||||||
connectors: Mutex<Vec<Connector>>,
|
connectors: Mutex<Vec<Connector>>,
|
||||||
@@ -99,19 +99,23 @@ impl AmdDriver {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let irq_handle = match info.irq {
|
display::set_pci_device_info(
|
||||||
Some(irq) => Some(
|
info.vendor_id,
|
||||||
IrqHandle::request(irq)
|
info.device_id,
|
||||||
.map_err(|e| DriverError::Io(format!("failed to request IRQ {irq}: {e}")))?,
|
info.revision,
|
||||||
),
|
info.irq.unwrap_or(0),
|
||||||
None => {
|
bar0.addr,
|
||||||
warn!(
|
bar0.size,
|
||||||
"redox-drm: AMD device {} has no IRQ assigned",
|
bar2.as_ref().map(|b| b.addr).unwrap_or(0),
|
||||||
info.location
|
bar2.as_ref().map(|b| b.size).unwrap_or(0),
|
||||||
);
|
);
|
||||||
None
|
|
||||||
}
|
let irq_handle = Some(InterruptHandle::setup(&info, &mut device).map_err(|e| {
|
||||||
};
|
DriverError::Io(format!(
|
||||||
|
"failed to setup interrupt for {}: {e}",
|
||||||
|
info.location
|
||||||
|
))
|
||||||
|
})?);
|
||||||
|
|
||||||
let display = DisplayCore::with_framebuffer(mmio.as_ptr(), mmio.size(), fb_phys, fb_size)?;
|
let display = DisplayCore::with_framebuffer(mmio.as_ptr(), mmio.size(), fb_phys, fb_size)?;
|
||||||
let (connectors, encoders) = detect_display_topology(&display)?;
|
let (connectors, encoders) = detect_display_topology(&display)?;
|
||||||
@@ -558,7 +562,7 @@ impl GpuDriver for AmdDriver {
|
|||||||
self.info.location,
|
self.info.location,
|
||||||
crtc_id,
|
crtc_id,
|
||||||
count,
|
count,
|
||||||
self.irq_handle.as_ref().map(IrqHandle::irq)
|
self.irq_handle.as_ref().map(|h| h.irq())
|
||||||
);
|
);
|
||||||
Ok(Some((crtc_id, count)))
|
Ok(Some((crtc_id, count)))
|
||||||
}
|
}
|
||||||
@@ -567,7 +571,7 @@ impl GpuDriver for AmdDriver {
|
|||||||
"redox-drm: handled AMD hotplug IRQ for {} connector {} irq={:?}",
|
"redox-drm: handled AMD hotplug IRQ for {} connector {} irq={:?}",
|
||||||
self.info.location,
|
self.info.location,
|
||||||
connector_id,
|
connector_id,
|
||||||
self.irq_handle.as_ref().map(IrqHandle::irq)
|
self.irq_handle.as_ref().map(|h| h.irq())
|
||||||
);
|
);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@@ -575,7 +579,7 @@ impl GpuDriver for AmdDriver {
|
|||||||
debug!(
|
debug!(
|
||||||
"redox-drm: handled AMD IRQ for {} with no decoded source irq={:?}",
|
"redox-drm: handled AMD IRQ for {} with no decoded source irq={:?}",
|
||||||
self.info.location,
|
self.info.location,
|
||||||
self.irq_handle.as_ref().map(IrqHandle::irq)
|
self.irq_handle.as_ref().map(|h| h.irq())
|
||||||
);
|
);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use redox_driver_sys::irq::IrqHandle;
|
|
||||||
use redox_driver_sys::memory::MmioRegion;
|
use redox_driver_sys::memory::MmioRegion;
|
||||||
use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo};
|
use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo};
|
||||||
|
|
||||||
use crate::driver::{DriverError, GpuDriver, Result};
|
use crate::driver::{DriverError, GpuDriver, Result};
|
||||||
|
use crate::drivers::interrupt::InterruptHandle;
|
||||||
use crate::gem::{GemHandle, GemManager};
|
use crate::gem::{GemHandle, GemManager};
|
||||||
use crate::kms::connector::{synthetic_edid, Connector};
|
use crate::kms::connector::{synthetic_edid, Connector};
|
||||||
use crate::kms::crtc::Crtc;
|
use crate::kms::crtc::Crtc;
|
||||||
@@ -37,7 +37,7 @@ const RING_HEAD_OFFSET: usize = 0x34;
|
|||||||
pub struct IntelDriver {
|
pub struct IntelDriver {
|
||||||
info: PciDeviceInfo,
|
info: PciDeviceInfo,
|
||||||
mmio: MmioRegion,
|
mmio: MmioRegion,
|
||||||
irq_handle: Mutex<Option<IrqHandle>>,
|
irq_handle: Mutex<Option<InterruptHandle>>,
|
||||||
display: IntelDisplay,
|
display: IntelDisplay,
|
||||||
gem: Mutex<GemManager>,
|
gem: Mutex<GemManager>,
|
||||||
connectors: Mutex<Vec<Connector>>,
|
connectors: Mutex<Vec<Connector>>,
|
||||||
@@ -83,14 +83,11 @@ impl IntelDriver {
|
|||||||
let (connectors, encoders) = detect_display_topology(&display)?;
|
let (connectors, encoders) = detect_display_topology(&display)?;
|
||||||
let crtcs = build_crtcs(&display)?;
|
let crtcs = build_crtcs(&display)?;
|
||||||
|
|
||||||
let irq_handle = match info.irq {
|
let irq_handle = match InterruptHandle::setup(&info, &mut device) {
|
||||||
Some(irq) => Some(
|
Ok(handle) => Some(handle),
|
||||||
IrqHandle::request(irq)
|
Err(e) => {
|
||||||
.map_err(|e| DriverError::Io(format!("failed to request IRQ {irq}: {e}")))?,
|
|
||||||
),
|
|
||||||
None => {
|
|
||||||
warn!(
|
warn!(
|
||||||
"redox-drm: Intel device {} has no IRQ assigned",
|
"redox-drm: Intel device {} interrupt setup failed: {e}",
|
||||||
info.location
|
info.location
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
@@ -497,7 +494,7 @@ impl GpuDriver for IntelDriver {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if irq_event.is_none() {
|
if !irq_event {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use log::{info, warn};
|
||||||
|
use redox_driver_sys::irq::{IrqHandle, MsixTable, MsixVector};
|
||||||
|
use redox_driver_sys::pci::{PciDevice, PciDeviceInfo, PCI_CAP_ID_MSIX};
|
||||||
|
|
||||||
|
use crate::driver::{DriverError, Result};
|
||||||
|
|
||||||
|
pub enum InterruptHandle {
|
||||||
|
Msix {
|
||||||
|
vector: MsixVector,
|
||||||
|
table: MsixTable,
|
||||||
|
cap_offset: u8,
|
||||||
|
},
|
||||||
|
Legacy {
|
||||||
|
handle: IrqHandle,
|
||||||
|
irq: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptHandle {
|
||||||
|
pub fn setup(device_info: &PciDeviceInfo, pci_device: &mut PciDevice) -> Result<Self> {
|
||||||
|
if let Ok(Some(handle)) = Self::try_msix(device_info, pci_device) {
|
||||||
|
return Ok(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::try_legacy(device_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_msix(device_info: &PciDeviceInfo, pci_device: &mut PciDevice) -> Result<Option<Self>> {
|
||||||
|
let msix_cap = match device_info.find_capability(PCI_CAP_ID_MSIX) {
|
||||||
|
Some(cap) => cap,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let msix_info = match pci_device.parse_msix(msix_cap.offset) {
|
||||||
|
Ok(info) => info,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"redox-drm: MSI-X capability parse failed for {}: {e}",
|
||||||
|
device_info.location
|
||||||
|
);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let table = match MsixTable::map(device_info, &msix_info) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"redox-drm: MSI-X table map failed for {}: {e}",
|
||||||
|
device_info.location
|
||||||
|
);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
table.mask_all();
|
||||||
|
|
||||||
|
if let Err(e) = pci_device.enable_msix(msix_cap.offset) {
|
||||||
|
warn!(
|
||||||
|
"redox-drm: MSI-X enable failed for {}: {e}",
|
||||||
|
device_info.location
|
||||||
|
);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vector = match table.request_vector(0) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"redox-drm: MSI-X vector allocation failed for {}: {e}",
|
||||||
|
device_info.location
|
||||||
|
);
|
||||||
|
let _ = pci_device.disable_msix(msix_cap.offset);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"redox-drm: MSI-X enabled for {} vector {} irq {}",
|
||||||
|
device_info.location, vector.index, vector.irq
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Some(InterruptHandle::Msix {
|
||||||
|
vector,
|
||||||
|
table,
|
||||||
|
cap_offset: msix_cap.offset,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_legacy(device_info: &PciDeviceInfo) -> Result<Self> {
|
||||||
|
let irq = device_info
|
||||||
|
.irq
|
||||||
|
.ok_or_else(|| DriverError::Io(format!("no IRQ for {}", device_info.location)))?;
|
||||||
|
|
||||||
|
let handle = IrqHandle::request(irq).map_err(|e| DriverError::Io(e.to_string()))?;
|
||||||
|
info!(
|
||||||
|
"redox-drm: using legacy IRQ {irq} for {}",
|
||||||
|
device_info.location
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(InterruptHandle::Legacy { handle, irq })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_wait(&mut self) -> Result<bool> {
|
||||||
|
match self {
|
||||||
|
InterruptHandle::Msix { vector, .. } => {
|
||||||
|
let mut buf = [0u8; 8];
|
||||||
|
match vector.fd.read(&mut buf) {
|
||||||
|
Ok(n) if n > 0 => Ok(true),
|
||||||
|
Ok(_) => Ok(false),
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(false),
|
||||||
|
Err(e) => Err(DriverError::Io(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InterruptHandle::Legacy { handle, .. } => handle
|
||||||
|
.try_wait()
|
||||||
|
.map(|ev| ev.is_some())
|
||||||
|
.map_err(|e| DriverError::Io(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eoi(&mut self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
InterruptHandle::Msix { vector, .. } => {
|
||||||
|
let mut buf = [0u8; 8];
|
||||||
|
vector
|
||||||
|
.fd
|
||||||
|
.read_exact(&mut buf)
|
||||||
|
.map_err(|e| DriverError::Io(e.to_string()))?;
|
||||||
|
vector
|
||||||
|
.fd
|
||||||
|
.write_all(&buf)
|
||||||
|
.map_err(|e| DriverError::Io(e.to_string()))
|
||||||
|
}
|
||||||
|
InterruptHandle::Legacy { handle, .. } => {
|
||||||
|
let mut buf = [0u8; 8];
|
||||||
|
let _ = handle.wait().map_err(|e| DriverError::Io(e.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn irq(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
InterruptHandle::Msix { vector, .. } => vector.irq,
|
||||||
|
InterruptHandle::Legacy { irq, .. } => *irq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_msix(&self) -> bool {
|
||||||
|
matches!(self, InterruptHandle::Msix { .. })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod amd;
|
pub mod amd;
|
||||||
pub mod intel;
|
pub mod intel;
|
||||||
|
pub mod interrupt;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
Reference in New Issue
Block a user