amdgpu: multiple CRTC support, DPMS/EDID properties, set_property dispatch

- Support 4 CRTCs instead of hardcoded 1 (AMD GPUs have 4-6 CRTCs)

- Add CONN_PROP_DPMS (ID 31) and CONN_PROP_EDID (ID 32) connector properties.
  DPMS is an enum property (On/Standby/Suspend/Off). EDID is an immutable blob.

- Add DrmModeObjSetPropertyWire struct and wire OBJ_SETPROPERTY ioctl to
  call driver.set_property() with proper error dispatch. Unknown properties
  are silently ignored (not errors).

- Add set_property() to GpuDriver trait with default Unsupported impl.
  AmdDriver implements DPMS property set by mapping connector_id -> CRTC and
  calling DisplayCore::set_dpms().
This commit is contained in:
2026-06-01 06:55:04 +03:00
parent c5bd162aea
commit 333c333fc1
3 changed files with 81 additions and 2 deletions
@@ -123,6 +123,10 @@ pub trait GpuDriver: Send + Sync {
Ok(None)
}
fn set_property(&self, _obj_id: u32, _prop_id: u32, _value: u64) -> Result<()> {
Err(DriverError::Unsupported("property setting not implemented for this backend"))
}
fn redox_private_cs_submit(
&self,
_submit: &RedoxPrivateCsSubmit,
@@ -164,7 +164,10 @@ impl AmdDriver {
display,
gem: Mutex::new(GemManager::new()),
connectors: Mutex::new(connectors),
crtcs: Mutex::new(vec![Crtc::new(1)]),
crtcs: Mutex::new({
let count = 4usize;
(1..=count).map(|id| Crtc::new(id as u32)).collect()
}),
encoders: Mutex::new(encoders),
gtt: Mutex::new(gtt),
ring: Mutex::new(ring),
@@ -715,6 +718,37 @@ impl GpuDriver for AmdDriver {
Ok(None)
}
fn set_property(&self, obj_id: u32, prop_id: u32, value: u64) -> Result<()> {
const CONN_PROP_DPMS: u32 = 31;
const DRM_MODE_DPMS_ON: i32 = 0;
const DRM_MODE_DPMS_OFF: i32 = 3;
if prop_id == CONN_PROP_DPMS {
let crtc_id = {
let connectors = self.connectors.lock().map_err(|_| {
DriverError::Initialization("connector state poisoned".into())
})?;
connectors
.iter()
.find(|c| c.info.id == obj_id)
.map(|c| c.info.encoder_id)
.ok_or_else(|| DriverError::NotFound(format!("unknown connector {obj_id}")))?
};
let dpms_mode = match value {
0 => DRM_MODE_DPMS_ON,
3 => DRM_MODE_DPMS_OFF,
_ => return Ok(()),
};
return self.display.set_dpms(crtc_id, dpms_mode);
}
Err(DriverError::Unsupported(
"property not supported for this object",
))
}
};
if !irq_event {
@@ -117,6 +117,8 @@ const CRTC_PROP_MODE_ID: u32 = 21;
// Connector property IDs (30-39 range)
const CONN_PROP_CRTC_ID: u32 = 30;
const CONN_PROP_DPMS: u32 = 31;
const CONN_PROP_EDID: u32 = 32;
const PLANE_PROPERTY_COUNT: usize = 10;
@@ -362,6 +364,16 @@ struct DrmModePropertyEnumWire {
name: [u8; DRM_PROP_NAME_LEN],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct DrmModeObjSetPropertyWire {
value: u64,
obj_id: u32,
obj_type: u32,
prop_id: u32,
_pad: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct DrmSetClientCapWire {
@@ -1247,6 +1259,30 @@ impl DrmScheme {
out = bytes_of(&response);
}
CONN_PROP_DPMS => {
response.flags = DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_ENUM;
response.count_enum_blobs = 4;
copy_c_string(&mut response.name, b"DPMS");
out = bytes_of(&response);
for (value, name) in [
(0, b"On\0"),
(1, b"Standby\0"),
(2, b"Suspend\0"),
(3, b"Off\0"),
] {
let mut e = DrmModePropertyEnumWire::default();
e.value = value;
copy_c_string(&mut e.name, name);
out.extend_from_slice(&bytes_of(&e));
}
}
CONN_PROP_EDID => {
response.flags = DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB;
copy_c_string(&mut response.name, b"EDID");
out = bytes_of(&response);
}
_ => out = bytes_of(&response),
}
@@ -2438,7 +2474,12 @@ impl DrmScheme {
}
DRM_IOCTL_MODE_OBJ_SETPROPERTY => {
vec![]
let req = decode_wire::<DrmModeObjSetPropertyWire>(payload)?;
match self.driver.set_property(req.obj_id, req.prop_id, req.value) {
Ok(()) => vec![],
Err(DriverError::Unsupported(_)) => vec![],
Err(e) => return Err(driver_to_syscall(e)),
}
}
0 => {