amdgpu: flip_surface per-family offsets, DPMS, cursor, hotplug polling
- Move flip_surface from Rust hardcoded registers (HUBP 0x5800, Navi23-only) to C side amdgpu_dc_flip_surface() using asic_props per-family HUBP offsets. - Add amdgpu_dc_set_dpms() for DPMS ON/OFF via OTG_CONTROL register. Uses per-family OTG base offsets. DPMS standby/suspend return noop. - Override cursor_set/cursor_move on AmdDriver with honest error messages documenting Display Core dependency. - Add poll_hotplug() to GpuDriver trait. AmdDriver overrides with connector count change detection when IRQ handle is unavailable. - Remove hardcoded HUBP_FLIP_ADDR_* constants from Rust display.rs.
This commit is contained in:
@@ -517,8 +517,88 @@ int amdgpu_dc_get_connector_info(int idx, void *info)
|
||||
ffi_info->mm_width = desc->mm_width;
|
||||
ffi_info->mm_height = desc->mm_height;
|
||||
ffi_info->encoder_id = desc->encoder_id;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_dc_flip_surface(int crtc_id, uint64_t fb_addr)
|
||||
{
|
||||
u32 hubp_base;
|
||||
|
||||
if (!g_mmio_base) {
|
||||
pr_err("amdgpu_redox: flip_surface called before init\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
{
|
||||
const struct asic_props *props = asic_props();
|
||||
if (!props || crtc_id >= props->max_crtcs) {
|
||||
pr_err("amdgpu_redox: no register layout for ASIC family %d crtc %d\n",
|
||||
g_asic_family, crtc_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
hubp_base = props->hubp_base[crtc_id];
|
||||
}
|
||||
|
||||
if (amdgpu_dc_validate_mmio_access(hubp_base, AMDGPU_DC_HUBP_FLIP_ADDR_HIGH) != 0 ||
|
||||
amdgpu_dc_validate_mmio_access(hubp_base, AMDGPU_DC_HUBP_FLIP_CONTROL) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
amdgpu_dc_write_reg(hubp_base, AMDGPU_DC_HUBP_FLIP_ADDR_LOW, (u32)(fb_addr & 0xFFFFFFFFULL));
|
||||
amdgpu_dc_write_reg(hubp_base, AMDGPU_DC_HUBP_FLIP_ADDR_HIGH, (u32)((fb_addr >> 32) & 0xFFFFFFFFULL));
|
||||
amdgpu_dc_write_reg(hubp_base, AMDGPU_DC_HUBP_FLIP_CONTROL, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_dc_set_dpms(int crtc_id, int mode)
|
||||
{
|
||||
u32 otg_base;
|
||||
u32 otg_control;
|
||||
|
||||
if (!g_mmio_base) {
|
||||
pr_err("amdgpu_redox: dpms called before init\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
{
|
||||
const struct asic_props *props = asic_props();
|
||||
if (!props || crtc_id >= props->max_crtcs) {
|
||||
pr_err("amdgpu_redox: no register layout for ASIC family %d crtc %d\n",
|
||||
g_asic_family, crtc_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
otg_base = props->otg_base[crtc_id];
|
||||
}
|
||||
|
||||
if (amdgpu_dc_validate_mmio_access(otg_base, AMDGPU_DC_OTG_CONTROL) != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
otg_control = amdgpu_dc_read_reg(otg_base, AMDGPU_DC_OTG_CONTROL);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
otg_control |= 0x01U;
|
||||
printk("amdgpu_redox: DPMS ON crtc %d\n", crtc_id);
|
||||
break;
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
otg_control &= ~0x01U;
|
||||
printk("amdgpu_redox: DPMS OFF crtc %d\n", crtc_id);
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
printk("amdgpu_redox: DPMS mode %d on crtc %d (no hardware support)\n",
|
||||
mode, crtc_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mb();
|
||||
amdgpu_dc_write_reg(otg_base, AMDGPU_DC_OTG_CONTROL, otg_control);
|
||||
mb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
active_index++;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ pub trait GpuDriver: Send + Sync {
|
||||
fn get_edid(&self, connector_id: u32) -> Vec<u8>;
|
||||
fn handle_irq(&self) -> Result<Option<DriverEvent>>;
|
||||
|
||||
fn poll_hotplug(&self) -> Result<Option<DriverEvent>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn redox_private_cs_submit(
|
||||
&self,
|
||||
_submit: &RedoxPrivateCsSubmit,
|
||||
|
||||
@@ -37,6 +37,10 @@ unsafe extern "C" {
|
||||
fn ffi_amdgpu_dc_get_connector_info(idx: i32, info: *mut ConnectorInfoFFI) -> i32;
|
||||
#[link_name = "amdgpu_dc_set_crtc"]
|
||||
fn ffi_amdgpu_dc_set_crtc(crtc_id: i32, fb_addr: u64, width: u32, height: u32) -> i32;
|
||||
#[link_name = "amdgpu_dc_flip_surface"]
|
||||
fn ffi_amdgpu_dc_flip_surface(crtc_id: i32, fb_addr: u64) -> i32;
|
||||
#[link_name = "amdgpu_dc_set_dpms"]
|
||||
fn ffi_amdgpu_dc_set_dpms(crtc_id: i32, mode: i32) -> i32;
|
||||
|
||||
#[link_name = "amdgpu_redox_cleanup"]
|
||||
fn ffi_amdgpu_redox_cleanup();
|
||||
@@ -105,6 +109,14 @@ fn amdgpu_dc_get_connector_info(_idx: i32, _info: *mut ConnectorInfoFFI) -> i32
|
||||
fn amdgpu_dc_set_crtc(_crtc_id: i32, _fb_addr: u64, _width: u32, _height: u32) -> i32 {
|
||||
0
|
||||
}
|
||||
#[cfg(no_amdgpu_c)]
|
||||
fn amdgpu_dc_flip_surface(_crtc_id: i32, _fb_addr: u64) -> i32 {
|
||||
0
|
||||
}
|
||||
#[cfg(no_amdgpu_c)]
|
||||
fn amdgpu_dc_set_dpms(_crtc_id: i32, _mode: i32) -> i32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(no_amdgpu_c)]
|
||||
fn amdgpu_dc_cleanup() {
|
||||
@@ -200,6 +212,14 @@ fn amdgpu_dc_get_connector_info(idx: i32, info: *mut ConnectorInfoFFI) -> i32 {
|
||||
fn amdgpu_dc_set_crtc(crtc_id: i32, fb_addr: u64, width: u32, height: u32) -> i32 {
|
||||
unsafe { ffi_amdgpu_dc_set_crtc(crtc_id, fb_addr, width, height) }
|
||||
}
|
||||
#[cfg(not(no_amdgpu_c))]
|
||||
fn amdgpu_dc_flip_surface(crtc_id: i32, fb_addr: u64) -> i32 {
|
||||
unsafe { ffi_amdgpu_dc_flip_surface(crtc_id, fb_addr) }
|
||||
}
|
||||
#[cfg(not(no_amdgpu_c))]
|
||||
fn amdgpu_dc_set_dpms(crtc_id: i32, mode: i32) -> i32 {
|
||||
unsafe { ffi_amdgpu_dc_set_dpms(crtc_id, mode) }
|
||||
}
|
||||
|
||||
#[cfg(not(no_amdgpu_c))]
|
||||
fn amdgpu_dc_cleanup() {
|
||||
@@ -338,17 +358,31 @@ impl DisplayCore {
|
||||
));
|
||||
}
|
||||
|
||||
const HUBP_FLIP_ADDR_LOW: usize = 0x5800;
|
||||
const HUBP_FLIP_ADDR_HIGH: usize = 0x5804;
|
||||
let rc = amdgpu_dc_flip_surface(crtc_id as i32, fb_addr);
|
||||
if rc < 0 {
|
||||
return Err(DriverError::Mmio(format!(
|
||||
"amdgpu_dc_flip_surface failed for CRTC {} with status {}",
|
||||
crtc_id, rc
|
||||
)));
|
||||
}
|
||||
|
||||
let hubp_base = HUBP_FLIP_ADDR_LOW + (crtc_id as usize) * 0x400;
|
||||
let hubp_high = HUBP_FLIP_ADDR_HIGH + (crtc_id as usize) * 0x400;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
self.write_reg(hubp_high, (fb_addr >> 32) as u32)?;
|
||||
self.write_reg(hubp_base, fb_addr as u32)?;
|
||||
pub fn set_dpms(&self, crtc_id: u32, mode: i32) -> Result<()> {
|
||||
if !self.initialized {
|
||||
return Err(DriverError::Initialization(
|
||||
"display core must be initialized before DPMS".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let flip_control = 0x5834 + (crtc_id as usize) * 0x400;
|
||||
self.write_reg(flip_control, 1)?;
|
||||
let rc = amdgpu_dc_set_dpms(crtc_id as i32, mode);
|
||||
if rc < 0 {
|
||||
return Err(DriverError::Mmio(format!(
|
||||
"amdgpu_dc_set_dpms failed for CRTC {} mode {} with status {}",
|
||||
crtc_id, mode, rc
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -307,6 +307,31 @@ impl AmdDriver {
|
||||
self.write_mmio_reg(AMD_IH_RB_CNTL, ih_rb_cntl);
|
||||
}
|
||||
|
||||
fn detect_hotplug_poll(&self) -> Option<u32> {
|
||||
let previous_connectors = self
|
||||
.connectors
|
||||
.lock()
|
||||
.ok()
|
||||
.map(|c| c.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
let current_connectors = self
|
||||
.display
|
||||
.detect_connectors()
|
||||
.map(|c| c.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
if current_connectors != previous_connectors {
|
||||
info!(
|
||||
"redox-drm: hotplug poll detected connector count change: {} -> {}",
|
||||
previous_connectors, current_connectors
|
||||
);
|
||||
Some(1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_connectors(&self) -> Result<()> {
|
||||
let (connectors, encoders) = detect_display_topology(&self.display)?;
|
||||
|
||||
@@ -414,7 +439,19 @@ impl GpuDriver for AmdDriver {
|
||||
}
|
||||
|
||||
fn driver_date(&self) -> &str {
|
||||
"2026-04-11"
|
||||
"2026-05-01"
|
||||
}
|
||||
|
||||
fn cursor_set(&self, _crtc_id: u32, _fb_handle: u32, _hot_x: u32, _hot_y: u32) -> Result<()> {
|
||||
Err(DriverError::Unsupported(
|
||||
"AMD hardware cursor requires Display Core initialization (imported Linux DC tree not yet compiled; cursor plane registers need DC hw programming)",
|
||||
))
|
||||
}
|
||||
|
||||
fn cursor_move(&self, _crtc_id: u32, _x: i32, _y: i32) -> Result<()> {
|
||||
Err(DriverError::Unsupported(
|
||||
"AMD hardware cursor movement requires Display Core cursor plane programming",
|
||||
))
|
||||
}
|
||||
|
||||
fn detect_connectors(&self) -> Vec<ConnectorInfo> {
|
||||
@@ -631,6 +668,59 @@ impl GpuDriver for AmdDriver {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let irq = self
|
||||
.irq_handle
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|guard| guard.as_ref().map(|h| h.irq()));
|
||||
|
||||
match self.process_irq()? {
|
||||
Some(DriverEvent::Vblank { crtc_id, count }) => {
|
||||
debug!(
|
||||
"redox-drm: handled AMD vblank IRQ for {} CRTC {} count={} irq={:?}",
|
||||
self.info.location, crtc_id, count, irq
|
||||
);
|
||||
Ok(Some(DriverEvent::Vblank { crtc_id, count }))
|
||||
}
|
||||
Some(DriverEvent::Hotplug { connector_id }) => {
|
||||
info!(
|
||||
"redox-drm: handled AMD hotplug IRQ for {} connector {} irq={:?}",
|
||||
self.info.location, connector_id, irq
|
||||
);
|
||||
Ok(Some(DriverEvent::Hotplug { connector_id }))
|
||||
}
|
||||
None => {
|
||||
debug!(
|
||||
"redox-drm: handled AMD IRQ for {} with no decoded source irq={:?}",
|
||||
self.info.location, irq
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_hotplug(&self) -> Result<Option<DriverEvent>> {
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
if self.irq_handle.lock().map_or(true, |guard| guard.is_none()) {
|
||||
if let Some(connector_id) = self.detect_hotplug_poll() {
|
||||
self.hotplug_pending.store(true, Ordering::SeqCst);
|
||||
if let Err(e) = self.refresh_connectors() {
|
||||
warn!("redox-drm: hotplug poll refresh failed: {}", e);
|
||||
}
|
||||
self.hotplug_pending.store(false, Ordering::SeqCst);
|
||||
return Ok(Some(DriverEvent::Hotplug { connector_id }));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
if !irq_event {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let irq = self
|
||||
.irq_handle
|
||||
.lock()
|
||||
|
||||
Reference in New Issue
Block a user