Merge branch 'gpu_drm11' into 'main'
Various graphics infrastructure refactorings and fix ihdgd when BIOS data can't be mapped See merge request redox-os/base!78
This commit is contained in:
Generated
+1
@@ -2569,6 +2569,7 @@ dependencies = [
|
||||
"daemon",
|
||||
"driver-graphics",
|
||||
"drm-sys",
|
||||
"edid",
|
||||
"futures",
|
||||
"graphics-ipc",
|
||||
"inputd",
|
||||
|
||||
@@ -24,9 +24,11 @@ use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL, ENOENT, EOPNOTSUPP};
|
||||
|
||||
use crate::objects::{DrmObjectId, DrmObjects, DrmPropertyKind};
|
||||
use crate::objects::{DrmObjectId, DrmObjects};
|
||||
use crate::properties::DrmPropertyKind;
|
||||
|
||||
pub mod objects;
|
||||
pub mod properties;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct StandardProperties {
|
||||
@@ -35,7 +37,7 @@ pub struct StandardProperties {
|
||||
}
|
||||
|
||||
pub trait GraphicsAdapter: Sized + Debug {
|
||||
type Connector: Debug;
|
||||
type Connector: Debug + 'static;
|
||||
|
||||
type Framebuffer: Framebuffer;
|
||||
type Cursor: CursorFramebuffer;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_char;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use drm_sys::{
|
||||
drm_mode_modeinfo, DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_ENCODER,
|
||||
DRM_MODE_OBJECT_PROPERTY, DRM_PROP_NAME_LEN,
|
||||
};
|
||||
use drm_sys::{drm_mode_modeinfo, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_ENCODER};
|
||||
use syscall::{Error, Result, EINVAL};
|
||||
|
||||
use crate::GraphicsAdapter;
|
||||
@@ -14,7 +13,8 @@ pub struct DrmObjects<T: GraphicsAdapter> {
|
||||
next_id: DrmObjectId,
|
||||
connectors: Vec<DrmObjectId>,
|
||||
encoders: Vec<DrmObjectId>,
|
||||
objects: HashMap<DrmObjectId, DrmObject<T>>,
|
||||
pub(crate) objects: HashMap<DrmObjectId, DrmObjectData>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> DrmObjects<T> {
|
||||
@@ -24,162 +24,69 @@ impl<T: GraphicsAdapter> DrmObjects<T> {
|
||||
connectors: vec![],
|
||||
encoders: vec![],
|
||||
objects: HashMap::new(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add<U: DrmObject>(&mut self, data: U) -> DrmObjectId {
|
||||
let id = self.next_id;
|
||||
self.objects.insert(
|
||||
id,
|
||||
DrmObjectData {
|
||||
kind: Box::new(data),
|
||||
properties: vec![],
|
||||
},
|
||||
);
|
||||
self.next_id.0 += 1;
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn get<U: DrmObject>(&self, id: DrmObjectId) -> Result<&U> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
if let Some(object) = (&*object.kind as &dyn Any).downcast_ref::<U>() {
|
||||
Ok(object)
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut<U: DrmObject>(&mut self, id: DrmObjectId) -> Result<&mut U> {
|
||||
let object = self.objects.get_mut(&id).ok_or(Error::new(EINVAL))?;
|
||||
if let Some(object) = (&mut *object.kind as &mut dyn Any).downcast_mut::<U>() {
|
||||
Ok(object)
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object_type(&self, id: DrmObjectId) -> Result<u32> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
Ok(match object.kind {
|
||||
DrmObjectKind::Property(_) => DRM_MODE_OBJECT_PROPERTY,
|
||||
DrmObjectKind::Blob(_) => DRM_MODE_OBJECT_BLOB,
|
||||
DrmObjectKind::Connector(_) => DRM_MODE_OBJECT_CONNECTOR,
|
||||
DrmObjectKind::Encoder(_) => DRM_MODE_OBJECT_ENCODER,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_property(
|
||||
&mut self,
|
||||
name: &str,
|
||||
immutable: bool,
|
||||
atomic: bool,
|
||||
kind: DrmPropertyKind,
|
||||
) -> DrmObjectId {
|
||||
if name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("Property name {name} is too long");
|
||||
}
|
||||
|
||||
match &kind {
|
||||
DrmPropertyKind::Range(start, end) => assert!(start < end),
|
||||
DrmPropertyKind::Enum(variants) => {
|
||||
// FIXME check duplicate variant numbers
|
||||
for (variant_name, _) in variants {
|
||||
if variant_name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("Property variant name {variant_name} is too long");
|
||||
}
|
||||
}
|
||||
}
|
||||
DrmPropertyKind::Blob => {}
|
||||
DrmPropertyKind::Bitmask(bitmask_flags) => {
|
||||
// FIXME check overlapping flag numbers
|
||||
for (flag_name, _) in bitmask_flags {
|
||||
if flag_name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("Property bitflag name {flag_name} is too long");
|
||||
}
|
||||
}
|
||||
}
|
||||
DrmPropertyKind::Object => {}
|
||||
DrmPropertyKind::SignedRange(start, end) => assert!(start < end),
|
||||
}
|
||||
|
||||
let mut name_bytes = [0; DRM_PROP_NAME_LEN as usize];
|
||||
for (to, &from) in name_bytes.iter_mut().zip(name.as_bytes()) {
|
||||
*to = from as c_char;
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.objects.insert(
|
||||
id,
|
||||
DrmObject {
|
||||
kind: DrmObjectKind::Property(DrmProperty {
|
||||
name: name_bytes,
|
||||
immutable,
|
||||
atomic,
|
||||
kind,
|
||||
}),
|
||||
properties: vec![],
|
||||
},
|
||||
);
|
||||
self.next_id.0 += 1;
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_property(&self, id: DrmObjectId) -> Result<&DrmProperty> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
match &object.kind {
|
||||
DrmObjectKind::Property(drm_property) => Ok(drm_property),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_object_property(&mut self, object: DrmObjectId, property: DrmObjectId, value: u64) {
|
||||
let object = self.objects.get_mut(&object).unwrap();
|
||||
// FIXME validate property uniqueness and value
|
||||
object.properties.push((property, value));
|
||||
}
|
||||
|
||||
pub fn set_object_property(&mut self, object: DrmObjectId, property: DrmObjectId, value: u64) {
|
||||
let object = self.objects.get_mut(&object).unwrap();
|
||||
// FIXME validate property existence and value
|
||||
for (prop, val) in object.properties.iter_mut() {
|
||||
if *prop == property {
|
||||
*val = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_object_properties(&self, id: DrmObjectId) -> Result<&[(DrmObjectId, u64)]> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
Ok(&object.properties)
|
||||
}
|
||||
|
||||
pub fn add_blob(&mut self, data: Vec<u8>) -> DrmObjectId {
|
||||
let id = self.next_id;
|
||||
self.objects.insert(
|
||||
id,
|
||||
DrmObject {
|
||||
kind: DrmObjectKind::Blob(data),
|
||||
properties: vec![],
|
||||
},
|
||||
);
|
||||
self.next_id.0 += 1;
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_blob(&self, id: DrmObjectId) -> Result<&[u8]> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
match &object.kind {
|
||||
DrmObjectKind::Blob(data) => Ok(data),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
Ok(object.kind.object_type())
|
||||
}
|
||||
|
||||
pub fn add_connector(&mut self, driver_data: T::Connector) -> DrmObjectId {
|
||||
let connector_id = self.next_id;
|
||||
let encoder_id = DrmObjectId(self.next_id.0 + 1);
|
||||
self.objects.insert(
|
||||
connector_id,
|
||||
DrmObject {
|
||||
kind: DrmObjectKind::Connector(DrmConnector {
|
||||
modes: vec![],
|
||||
encoder_id,
|
||||
connector_type: 0,
|
||||
connector_type_id: 0,
|
||||
connection: DrmConnectorStatus::Unknown,
|
||||
mm_width: 0,
|
||||
mm_height: 0,
|
||||
subpixel: DrmSubpixelOrder::Unknown,
|
||||
driver_data,
|
||||
}),
|
||||
properties: vec![],
|
||||
},
|
||||
);
|
||||
let connector_id = self.add(DrmConnector {
|
||||
modes: vec![],
|
||||
encoder_id: DrmObjectId::INVALID,
|
||||
connector_type: 0,
|
||||
connector_type_id: 0,
|
||||
connection: DrmConnectorStatus::Unknown,
|
||||
mm_width: 0,
|
||||
mm_height: 0,
|
||||
subpixel: DrmSubpixelOrder::Unknown,
|
||||
driver_data,
|
||||
});
|
||||
self.connectors.push(connector_id);
|
||||
|
||||
self.objects.insert(
|
||||
encoder_id,
|
||||
DrmObject {
|
||||
kind: DrmObjectKind::Encoder(DrmEncoder {
|
||||
crtc_id: DrmObjectId::INVALID,
|
||||
possible_crtcs: 0,
|
||||
possible_clones: 0,
|
||||
}),
|
||||
properties: vec![],
|
||||
},
|
||||
);
|
||||
let encoder_id = self.add(DrmEncoder {
|
||||
crtc_id: DrmObjectId::INVALID,
|
||||
possible_crtcs: 0,
|
||||
possible_clones: 0,
|
||||
});
|
||||
self.encoders.push(encoder_id);
|
||||
self.next_id.0 += 2;
|
||||
|
||||
self.get_connector_mut(connector_id).unwrap().encoder_id = encoder_id;
|
||||
|
||||
connector_id
|
||||
}
|
||||
@@ -188,29 +95,23 @@ impl<T: GraphicsAdapter> DrmObjects<T> {
|
||||
&self.connectors
|
||||
}
|
||||
|
||||
pub fn connectors(&self) -> impl Iterator<Item = &DrmConnector<T>> + use<'_, T> {
|
||||
self.connectors
|
||||
.iter()
|
||||
.map(|&id| match &self.objects[&id].kind {
|
||||
DrmObjectKind::Connector(connector) => connector,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
pub fn connectors(&self) -> impl Iterator<Item = &DrmConnector<T::Connector>> + use<'_, T> {
|
||||
self.connectors.iter().map(|&id| {
|
||||
(&self.objects[&id].kind as &dyn Any)
|
||||
.downcast_ref::<DrmConnector<T::Connector>>()
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_connector(&self, id: DrmObjectId) -> Result<&DrmConnector<T>> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
match &object.kind {
|
||||
DrmObjectKind::Connector(drm_connector) => Ok(drm_connector),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
pub fn get_connector(&self, id: DrmObjectId) -> Result<&DrmConnector<T::Connector>> {
|
||||
self.get(id)
|
||||
}
|
||||
|
||||
pub fn get_connector_mut(&mut self, id: DrmObjectId) -> Result<&mut DrmConnector<T>> {
|
||||
let object = self.objects.get_mut(&id).ok_or(Error::new(EINVAL))?;
|
||||
match &mut object.kind {
|
||||
DrmObjectKind::Connector(drm_connector) => Ok(drm_connector),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
pub fn get_connector_mut(
|
||||
&mut self,
|
||||
id: DrmObjectId,
|
||||
) -> Result<&mut DrmConnector<T::Connector>> {
|
||||
self.get_mut(id)
|
||||
}
|
||||
|
||||
pub fn encoder_ids(&self) -> &[DrmObjectId] {
|
||||
@@ -218,19 +119,11 @@ impl<T: GraphicsAdapter> DrmObjects<T> {
|
||||
}
|
||||
|
||||
pub fn get_encoder(&self, id: DrmObjectId) -> Result<&DrmEncoder> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
match &object.kind {
|
||||
DrmObjectKind::Encoder(drm_encoder) => Ok(drm_encoder),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
self.get(id)
|
||||
}
|
||||
|
||||
pub fn get_encoder_mut(&mut self, id: DrmObjectId) -> Result<&mut DrmEncoder> {
|
||||
let object = self.objects.get_mut(&id).ok_or(Error::new(EINVAL))?;
|
||||
match &mut object.kind {
|
||||
DrmObjectKind::Encoder(drm_encoder) => Ok(drm_encoder),
|
||||
_ => Err(Error::new(EINVAL)),
|
||||
}
|
||||
self.get_mut(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,39 +141,17 @@ impl From<DrmObjectId> for u64 {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DrmObject<T: GraphicsAdapter> {
|
||||
kind: DrmObjectKind<T>,
|
||||
properties: Vec<(DrmObjectId, u64)>,
|
||||
pub(crate) struct DrmObjectData {
|
||||
kind: Box<dyn DrmObject + 'static>,
|
||||
pub(crate) properties: Vec<(DrmObjectId, u64)>,
|
||||
}
|
||||
|
||||
pub trait DrmObject: Any + Debug {
|
||||
fn object_type(&self) -> u32;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DrmObjectKind<T: GraphicsAdapter> {
|
||||
Property(DrmProperty),
|
||||
Blob(Vec<u8>),
|
||||
Connector(DrmConnector<T>),
|
||||
Encoder(DrmEncoder),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmProperty {
|
||||
pub name: [c_char; DRM_PROP_NAME_LEN as usize],
|
||||
pub immutable: bool,
|
||||
pub atomic: bool,
|
||||
pub kind: DrmPropertyKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DrmPropertyKind {
|
||||
Range(u64, u64),
|
||||
Enum(Vec<(&'static str, u64)>),
|
||||
Blob,
|
||||
Bitmask(Vec<(&'static str, u64)>),
|
||||
Object,
|
||||
SignedRange(i64, i64),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmConnector<T: GraphicsAdapter> {
|
||||
pub struct DrmConnector<T: Debug + 'static> {
|
||||
pub modes: Vec<drm_mode_modeinfo>,
|
||||
pub encoder_id: DrmObjectId,
|
||||
pub connector_type: u32,
|
||||
@@ -289,7 +160,7 @@ pub struct DrmConnector<T: GraphicsAdapter> {
|
||||
pub mm_width: u32,
|
||||
pub mm_height: u32,
|
||||
pub subpixel: DrmSubpixelOrder,
|
||||
pub driver_data: T::Connector,
|
||||
pub driver_data: T,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@@ -311,6 +182,12 @@ pub enum DrmSubpixelOrder {
|
||||
None,
|
||||
}
|
||||
|
||||
impl<T: Debug + 'static> DrmObject for DrmConnector<T> {
|
||||
fn object_type(&self) -> u32 {
|
||||
DRM_MODE_OBJECT_CONNECTOR
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME can we represent connector and encoder using a single struct?
|
||||
#[derive(Debug)]
|
||||
pub struct DrmEncoder {
|
||||
@@ -318,3 +195,9 @@ pub struct DrmEncoder {
|
||||
pub possible_crtcs: u32,
|
||||
pub possible_clones: u32,
|
||||
}
|
||||
|
||||
impl DrmObject for DrmEncoder {
|
||||
fn object_type(&self) -> u32 {
|
||||
DRM_MODE_OBJECT_ENCODER
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
use std::ffi::c_char;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use drm_sys::{DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_PROPERTY, DRM_PROP_NAME_LEN};
|
||||
use syscall::{Error, Result, EINVAL};
|
||||
|
||||
use crate::objects::{DrmObject, DrmObjectId, DrmObjects};
|
||||
use crate::GraphicsAdapter;
|
||||
|
||||
impl<T: GraphicsAdapter> DrmObjects<T> {
|
||||
pub fn add_property(
|
||||
&mut self,
|
||||
name: &str,
|
||||
immutable: bool,
|
||||
atomic: bool,
|
||||
kind: DrmPropertyKind,
|
||||
) -> DrmObjectId {
|
||||
if name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("Property name {name} is too long");
|
||||
}
|
||||
|
||||
match &kind {
|
||||
DrmPropertyKind::Range(start, end) => assert!(start < end),
|
||||
DrmPropertyKind::Enum(variants) => {
|
||||
// FIXME check duplicate variant numbers
|
||||
for (variant_name, _) in variants {
|
||||
if variant_name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("Property variant name {variant_name} is too long");
|
||||
}
|
||||
}
|
||||
}
|
||||
DrmPropertyKind::Blob => {}
|
||||
DrmPropertyKind::Bitmask(bitmask_flags) => {
|
||||
// FIXME check overlapping flag numbers
|
||||
for (flag_name, _) in bitmask_flags {
|
||||
if flag_name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("Property bitflag name {flag_name} is too long");
|
||||
}
|
||||
}
|
||||
}
|
||||
DrmPropertyKind::Object => {}
|
||||
DrmPropertyKind::SignedRange(start, end) => assert!(start < end),
|
||||
}
|
||||
|
||||
let mut name_bytes = [0; DRM_PROP_NAME_LEN as usize];
|
||||
for (to, &from) in name_bytes.iter_mut().zip(name.as_bytes()) {
|
||||
*to = from as c_char;
|
||||
}
|
||||
|
||||
self.add(DrmProperty {
|
||||
name: name_bytes,
|
||||
immutable,
|
||||
atomic,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_property(&self, id: DrmObjectId) -> Result<&DrmProperty> {
|
||||
self.get(id)
|
||||
}
|
||||
|
||||
pub fn add_object_property(&mut self, object: DrmObjectId, property: DrmObjectId, value: u64) {
|
||||
let object = self.objects.get_mut(&object).unwrap();
|
||||
// FIXME validate property uniqueness and value
|
||||
object.properties.push((property, value));
|
||||
}
|
||||
|
||||
pub fn set_object_property(&mut self, object: DrmObjectId, property: DrmObjectId, value: u64) {
|
||||
let object = self.objects.get_mut(&object).unwrap();
|
||||
// FIXME validate property existence and value
|
||||
for (prop, val) in object.properties.iter_mut() {
|
||||
if *prop == property {
|
||||
*val = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_object_properties(&self, id: DrmObjectId) -> Result<&[(DrmObjectId, u64)]> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
Ok(&object.properties)
|
||||
}
|
||||
|
||||
pub fn add_blob(&mut self, data: Vec<u8>) -> DrmObjectId {
|
||||
self.add(DrmBlob { data })
|
||||
}
|
||||
|
||||
pub fn get_blob(&self, id: DrmObjectId) -> Result<&[u8]> {
|
||||
Ok(&self.get::<DrmBlob>(id)?.data)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmProperty {
|
||||
pub name: [c_char; DRM_PROP_NAME_LEN as usize],
|
||||
pub immutable: bool,
|
||||
pub atomic: bool,
|
||||
pub kind: DrmPropertyKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DrmPropertyKind {
|
||||
Range(u64, u64),
|
||||
Enum(Vec<(&'static str, u64)>),
|
||||
Blob,
|
||||
Bitmask(Vec<(&'static str, u64)>),
|
||||
Object,
|
||||
SignedRange(i64, i64),
|
||||
}
|
||||
|
||||
impl DrmObject for DrmProperty {
|
||||
fn object_type(&self) -> u32 {
|
||||
DRM_MODE_OBJECT_PROPERTY
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DrmBlob {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl DrmObject for DrmBlob {
|
||||
fn object_type(&self) -> u32 {
|
||||
DRM_MODE_OBJECT_BLOB
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
use common::io::{Io, MmioPtr, WriteOnly};
|
||||
use common::timeout::Timeout;
|
||||
use embedded_hal::prelude::*;
|
||||
use std::sync::Arc;
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use crate::device::aux::Aux;
|
||||
use crate::device::power::PowerWells;
|
||||
use crate::device::{CallbackGuard, Gmbus};
|
||||
|
||||
use super::{GpioPort, MmioRegion};
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_CTL
|
||||
@@ -200,6 +206,101 @@ impl Ddi {
|
||||
self.port_reg((reg as usize) + (lane as usize))
|
||||
}
|
||||
|
||||
pub fn probe_edid(
|
||||
&mut self,
|
||||
power_wells: &mut PowerWells,
|
||||
gttmm: &MmioRegion,
|
||||
gmbus: &mut Gmbus,
|
||||
) -> Result<Option<(&'static str, [u8; 128])>, Error> {
|
||||
if let Some(port_comp_dw0) = self.port_comp(PortCompReg::Dw0) {
|
||||
log::debug!("PORT_COMP_DW0_{}: {:08X}", self.name, port_comp_dw0.read());
|
||||
}
|
||||
let mut aux_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
|
||||
//TODO: BLOCK TCCOLD?
|
||||
|
||||
//TODO: the request can be shared by multiple DDIs
|
||||
let pwr_well_ctl_aux_request = ddi.pwr_well_ctl_aux_request;
|
||||
let pwr_well_ctl_aux_state = ddi.pwr_well_ctl_aux_state;
|
||||
let mut pwr_well_ctl_aux = unsafe { MmioPtr::new(power_wells.ctl_aux.as_mut_ptr()) };
|
||||
let _pwr_guard = CallbackGuard::new(
|
||||
&mut pwr_well_ctl_aux,
|
||||
|pwr_well_ctl_aux| {
|
||||
// Enable aux power
|
||||
pwr_well_ctl_aux.writef(pwr_well_ctl_aux_request, true);
|
||||
let timeout = Timeout::from_micros(1500);
|
||||
while !pwr_well_ctl_aux.readf(pwr_well_ctl_aux_state) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::debug!("timeout while requesting DDI {} aux power", ddi.name);
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
|pwr_well_ctl_aux| {
|
||||
// Disable aux power
|
||||
pwr_well_ctl_aux.writef(pwr_well_ctl_aux_request, false);
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut edid_data = [0; 128];
|
||||
Aux::new(ddi)
|
||||
.write_read(0x50, &[0x00], &mut edid_data)
|
||||
.map_err(|_err| Error::new(EIO))?;
|
||||
|
||||
Ok(edid_data)
|
||||
};
|
||||
let mut gmbus_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
|
||||
let Some(pin_pair) = ddi.gmbus_pin_pair else {
|
||||
return Err(Error::new(EIO));
|
||||
};
|
||||
|
||||
let mut edid_data = [0; 128];
|
||||
gmbus
|
||||
.pin_pair(pin_pair)
|
||||
.write_read(0x50, &[0x00], &mut edid_data)
|
||||
.map_err(|_err| Error::new(EIO))?;
|
||||
|
||||
Ok(edid_data)
|
||||
};
|
||||
let gpio_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
|
||||
let Some(port) = &ddi.gpio_port else {
|
||||
return Err(Error::new(EIO));
|
||||
};
|
||||
|
||||
let mut edid_data = [0; 128];
|
||||
unsafe { port.i2c(gttmm)? }
|
||||
.write_read(0x50, &[0x00], &mut edid_data)
|
||||
.map_err(|_err| Error::new(EIO))?;
|
||||
|
||||
Ok(edid_data)
|
||||
};
|
||||
let (source, edid_data) = match aux_read_edid(self) {
|
||||
Ok(edid_data) => ("AUX", edid_data),
|
||||
Err(err) => {
|
||||
log::debug!("DDI {} failed to read EDID from AUX: {}", self.name, err);
|
||||
match gmbus_read_edid(self) {
|
||||
Ok(edid_data) => ("GMBUS", edid_data),
|
||||
Err(err) => {
|
||||
log::debug!("DDI {} failed to read EDID from GMBUS: {}", self.name, err);
|
||||
match gpio_read_edid(self) {
|
||||
Ok(edid_data) => ("GPIO", edid_data),
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
"DDI {} failed to read EDID from GPIO: {}",
|
||||
self.name,
|
||||
err
|
||||
);
|
||||
// Will try again but not fail the driver
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(Some((source, edid_data)))
|
||||
}
|
||||
|
||||
pub fn voltage_swing_hdmi(
|
||||
&mut self,
|
||||
gttmm: &MmioRegion,
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
use std::convert::Infallible;
|
||||
use std::time::Duration;
|
||||
|
||||
use common::io::{Io, MmioPtr};
|
||||
use embedded_hal::blocking::i2c;
|
||||
use embedded_hal::digital::v2 as digital;
|
||||
|
||||
use crate::device::HalTimer;
|
||||
|
||||
use super::MmioRegion;
|
||||
|
||||
const GPIO_DIR_MASK: u32 = 1 << 0;
|
||||
@@ -35,18 +39,28 @@ pub enum GpioPort {
|
||||
}
|
||||
|
||||
impl GpioPort {
|
||||
pub unsafe fn clock(&self, gttmm: &MmioRegion) -> syscall::Result<GpioPin> {
|
||||
Ok(GpioPin {
|
||||
ctl: gttmm.mmio(*self as usize)?,
|
||||
shift: GPIO_CLOCK_SHIFT,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn data(&self, gttmm: &MmioRegion) -> syscall::Result<GpioPin> {
|
||||
Ok(GpioPin {
|
||||
ctl: gttmm.mmio(*self as usize)?,
|
||||
shift: GPIO_DATA_SHIFT,
|
||||
})
|
||||
pub unsafe fn i2c(
|
||||
&self,
|
||||
gttmm: &MmioRegion,
|
||||
) -> syscall::Result<bitbang_hal::i2c::I2cBB<GpioPin, GpioPin, HalTimer>> {
|
||||
let i2c_freq = 100_000.0;
|
||||
let (scl, sda) = unsafe {
|
||||
(
|
||||
GpioPin {
|
||||
ctl: gttmm.mmio(*self as usize)?,
|
||||
shift: GPIO_CLOCK_SHIFT,
|
||||
},
|
||||
GpioPin {
|
||||
ctl: gttmm.mmio(*self as usize)?,
|
||||
shift: GPIO_DATA_SHIFT,
|
||||
},
|
||||
)
|
||||
};
|
||||
Ok(bitbang_hal::i2c::I2cBB::new(
|
||||
scl,
|
||||
sda,
|
||||
HalTimer::new(Duration::from_secs_f64(1.0 / i2c_freq)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ use common::{
|
||||
io::{Io, MmioPtr},
|
||||
timeout::Timeout,
|
||||
};
|
||||
use embedded_hal::prelude::*;
|
||||
use pcid_interface::{PciFunction, PciFunctionHandle};
|
||||
use range_alloc::RangeAllocator;
|
||||
use std::{collections::VecDeque, fmt, mem, sync::Arc, time::Duration};
|
||||
use std::{collections::VecDeque, fmt, mem, sync::Arc};
|
||||
use syscall::error::{Error, Result, EIO, ENODEV, ERANGE};
|
||||
|
||||
mod aux;
|
||||
use self::aux::*;
|
||||
mod bios;
|
||||
use self::bios::*;
|
||||
mod ddi;
|
||||
@@ -271,14 +269,20 @@ impl Device {
|
||||
log::info!("BIOS {:X?}", bios_base);
|
||||
// This is the default BIOS size
|
||||
let bios_size = 8 * 1024;
|
||||
match Bios::new(MmioRegion::new(
|
||||
match MmioRegion::new(
|
||||
bios_base as usize,
|
||||
bios_size,
|
||||
common::MemoryType::Uncacheable,
|
||||
)?) {
|
||||
Ok(bios) => Some(bios),
|
||||
) {
|
||||
Ok(region) => match Bios::new(region) {
|
||||
Ok(bios) => Some(bios),
|
||||
Err(err) => {
|
||||
log::warn!("failed to parse BIOS at {:08X}: {}", bios_base, err);
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::warn!("failed to parse BIOS at {:08X}: {}", bios_base, err);
|
||||
log::warn!("failed to map BIOS at {:08X}: {}", bios_base, err);
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -552,102 +556,10 @@ impl Device {
|
||||
// Enable DDI power well
|
||||
self.power_wells.enable_well_by_ddi(ddi.name)?;
|
||||
|
||||
//TODO: init port if needed
|
||||
if let Some(port_comp_dw0) = ddi.port_comp(PortCompReg::Dw0) {
|
||||
log::debug!("PORT_COMP_DW0_{}: {:08X}", ddi.name, port_comp_dw0.read());
|
||||
}
|
||||
|
||||
let mut aux_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
|
||||
//TODO: BLOCK TCCOLD?
|
||||
|
||||
//TODO: the request can be shared by multiple DDIs
|
||||
let pwr_well_ctl_aux_request = ddi.pwr_well_ctl_aux_request;
|
||||
let pwr_well_ctl_aux_state = ddi.pwr_well_ctl_aux_state;
|
||||
let mut pwr_well_ctl_aux =
|
||||
unsafe { MmioPtr::new(self.power_wells.ctl_aux.as_mut_ptr()) };
|
||||
let _pwr_guard = CallbackGuard::new(
|
||||
&mut pwr_well_ctl_aux,
|
||||
|pwr_well_ctl_aux| {
|
||||
// Enable aux power
|
||||
pwr_well_ctl_aux.writef(pwr_well_ctl_aux_request, true);
|
||||
let timeout = Timeout::from_micros(1500);
|
||||
while !pwr_well_ctl_aux.readf(pwr_well_ctl_aux_state) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::debug!("timeout while requesting DDI {} aux power", ddi.name);
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
|pwr_well_ctl_aux| {
|
||||
// Disable aux power
|
||||
pwr_well_ctl_aux.writef(pwr_well_ctl_aux_request, false);
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut edid_data = [0; 128];
|
||||
Aux::new(ddi)
|
||||
.write_read(0x50, &[0x00], &mut edid_data)
|
||||
.map_err(|_err| Error::new(EIO))?;
|
||||
|
||||
Ok(edid_data)
|
||||
};
|
||||
|
||||
let mut gmbus_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
|
||||
let Some(pin_pair) = ddi.gmbus_pin_pair else {
|
||||
return Err(Error::new(EIO));
|
||||
};
|
||||
|
||||
let mut edid_data = [0; 128];
|
||||
self.gmbus
|
||||
.pin_pair(pin_pair)
|
||||
.write_read(0x50, &[0x00], &mut edid_data)
|
||||
.map_err(|_err| Error::new(EIO))?;
|
||||
|
||||
Ok(edid_data)
|
||||
};
|
||||
|
||||
let gpio_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
|
||||
let Some(port) = &ddi.gpio_port else {
|
||||
return Err(Error::new(EIO));
|
||||
};
|
||||
|
||||
let mut edid_data = [0; 128];
|
||||
let i2c_freq = 100_000.0;
|
||||
bitbang_hal::i2c::I2cBB::new(
|
||||
unsafe { port.clock(&self.gttmm)? },
|
||||
unsafe { port.data(&self.gttmm)? },
|
||||
HalTimer::new(Duration::from_secs_f64(1.0 / i2c_freq)),
|
||||
)
|
||||
.write_read(0x50, &[0x00], &mut edid_data)
|
||||
.map_err(|_err| Error::new(EIO))?;
|
||||
|
||||
Ok(edid_data)
|
||||
};
|
||||
|
||||
let (source, edid_data) = match aux_read_edid(ddi) {
|
||||
Ok(edid_data) => ("AUX", edid_data),
|
||||
Err(err) => {
|
||||
log::debug!("DDI {} failed to read EDID from AUX: {}", ddi.name, err);
|
||||
match gmbus_read_edid(ddi) {
|
||||
Ok(edid_data) => ("GMBUS", edid_data),
|
||||
Err(err) => {
|
||||
log::debug!("DDI {} failed to read EDID from GMBUS: {}", ddi.name, err);
|
||||
match gpio_read_edid(ddi) {
|
||||
Ok(edid_data) => ("GPIO", edid_data),
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
"DDI {} failed to read EDID from GPIO: {}",
|
||||
ddi.name,
|
||||
err
|
||||
);
|
||||
// Will try again but not fail the driver
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some((source, edid_data)) =
|
||||
ddi.probe_edid(&mut self.power_wells, &self.gttmm, &mut self.gmbus)?
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let edid = match edid::parse(&edid_data).to_full_result() {
|
||||
@@ -667,16 +579,10 @@ impl Device {
|
||||
}
|
||||
};
|
||||
|
||||
let mut timing_opt = None;
|
||||
for desc in edid.descriptors.iter() {
|
||||
match desc {
|
||||
edid::Descriptor::DetailedTiming(timing) => {
|
||||
timing_opt = Some(timing);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let timing_opt = edid.descriptors.iter().find_map(|desc| match desc {
|
||||
edid::Descriptor::DetailedTiming(timing) => Some(timing),
|
||||
_ => None,
|
||||
});
|
||||
let Some(timing) = timing_opt else {
|
||||
log::warn!(
|
||||
"DDI {} EDID from {} missing detailed timing",
|
||||
|
||||
@@ -6,6 +6,8 @@ authors = ["Anhad Singh <andypython@protonmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
drm-sys = "0.8.0"
|
||||
#TODO: edid is abandoned, fork it an maintain?
|
||||
edid = "0.3.0"
|
||||
log = "0.4"
|
||||
static_assertions = "1.1.0"
|
||||
futures = { version = "0.3.28", features = ["executor"] }
|
||||
|
||||
@@ -7,7 +7,7 @@ use driver_graphics::{
|
||||
modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter,
|
||||
GraphicsScheme, StandardProperties,
|
||||
};
|
||||
use drm_sys::DRM_MODE_DPMS_ON;
|
||||
use drm_sys::{DRM_MODE_DPMS_ON, DRM_MODE_TYPE_PREFERRED};
|
||||
use graphics_ipc::v1::Damage;
|
||||
use graphics_ipc::v2::ipc::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
|
||||
use inputd::DisplayHandle;
|
||||
@@ -307,6 +307,41 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> {
|
||||
};
|
||||
|
||||
if self.has_edid {
|
||||
let edid = edid::parse(&display.edid).unwrap().1;
|
||||
|
||||
let first_detailed_timing = edid
|
||||
.descriptors
|
||||
.iter()
|
||||
.find_map(|descriptor| match descriptor {
|
||||
edid::Descriptor::DetailedTiming(detailed_timing) => Some(detailed_timing),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
connector.mm_width = first_detailed_timing.horizontal_size.into();
|
||||
connector.mm_height = first_detailed_timing.vertical_size.into();
|
||||
|
||||
connector.modes = edid
|
||||
.descriptors
|
||||
.iter()
|
||||
.filter_map(|descriptor| {
|
||||
match descriptor {
|
||||
edid::Descriptor::DetailedTiming(detailed_timing) => {
|
||||
// FIXME extract full information
|
||||
Some(modeinfo_for_size(
|
||||
u32::from(detailed_timing.horizontal_active_pixels),
|
||||
u32::from(detailed_timing.vertical_active_lines),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// First detailed timing descriptor indicates preferred mode.
|
||||
for mode in connector.modes.iter_mut().skip(1) {
|
||||
mode.flags &= !DRM_MODE_TYPE_PREFERRED;
|
||||
}
|
||||
|
||||
let blob = objects.add_blob(display.edid.clone());
|
||||
objects.set_object_property(id, standard_properties.edid, blob.into());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user