ihdgd driver
This commit is contained in:
Generated
+107
-84
@@ -77,7 +77,7 @@ version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"memchr 2.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -156,7 +156,7 @@ version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -167,7 +167,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -278,6 +278,16 @@ version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitbang-hal"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a6e78d48a099db1643b06824ea903b72c39b38ebc0e98ba5a6693d35f360a28"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -669,6 +679,25 @@ dependencies = [
|
||||
"redox_syscall 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "edid"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ce75530893d834dcfe3bb67ce0e7dec489484e7cb4423ca31618af4bab24fe"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||
dependencies = [
|
||||
"nb 0.1.3",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.4"
|
||||
@@ -695,7 +724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -855,7 +884,7 @@ dependencies = [
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"memchr 2.7.6",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@@ -1048,6 +1077,29 @@ dependencies = [
|
||||
"spin 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ihdgd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitbang-hal",
|
||||
"common",
|
||||
"daemon",
|
||||
"driver-graphics",
|
||||
"edid",
|
||||
"embedded-hal",
|
||||
"graphics-ipc",
|
||||
"inputd",
|
||||
"libredox",
|
||||
"log",
|
||||
"nb 1.1.0",
|
||||
"pcid",
|
||||
"range-alloc",
|
||||
"redox-scheme",
|
||||
"redox_event",
|
||||
"redox_syscall 0.6.0",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
@@ -1224,12 +1276,45 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||
dependencies = [
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
|
||||
dependencies = [
|
||||
"memchr 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
@@ -1631,6 +1716,11 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "range-alloc"
|
||||
version = "0.1.4"
|
||||
source = "git+https://github.com/jackpot51/range-alloc.git#23493c401729c9617355804d4ef6bd0ffafb0465"
|
||||
|
||||
[[package]]
|
||||
name = "ransid"
|
||||
version = "0.4.9"
|
||||
@@ -1803,7 +1893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"memchr 2.7.6",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
@@ -1815,7 +1905,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"memchr 2.7.6",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
@@ -1899,7 +1989,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.11.0",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2035,7 +2125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"memchr 2.7.6",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
@@ -2506,6 +2596,12 @@ dependencies = [
|
||||
"virtio-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.3.3"
|
||||
@@ -2597,7 +2693,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2665,15 +2761,6 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -2683,77 +2770,13 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"memchr 2.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -37,6 +37,7 @@ members = [
|
||||
"drivers/graphics/driver-graphics",
|
||||
"drivers/graphics/fbcond",
|
||||
"drivers/graphics/graphics-ipc",
|
||||
"drivers/graphics/ihdgd",
|
||||
"drivers/graphics/vesad",
|
||||
"drivers/graphics/virtio-gpud",
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ use core::{
|
||||
};
|
||||
|
||||
mod mmio;
|
||||
mod mmio_ptr;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
mod pio;
|
||||
|
||||
pub use mmio::*;
|
||||
pub use mmio_ptr::*;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
pub use pio::*;
|
||||
|
||||
@@ -89,9 +91,5 @@ impl<I: Io> WriteOnly<I> {
|
||||
self.inner.write(value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
/// Calls [Io::writef]
|
||||
pub fn writef(&mut self, flags: I::Value, value: bool) {
|
||||
self.inner.writef(flags, value)
|
||||
}
|
||||
// writef requires read which is not valid when write-only
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
use core::ptr;
|
||||
|
||||
use super::Io;
|
||||
|
||||
/// MMIO using pointer instead of wrapped type
|
||||
pub struct MmioPtr<T> {
|
||||
ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<T> MmioPtr<T> {
|
||||
//TODO: reads and writes are unsafe, not new.
|
||||
pub unsafe fn new(ptr: *mut T) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
pub const fn as_ptr(&self) -> *const T {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
pub const fn as_mut_ptr(&mut self) -> *mut T {
|
||||
self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
// Generic implementation (WARNING: requires aligned pointers!)
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
impl<T> Io for MmioPtr<T>
|
||||
where
|
||||
T: Copy
|
||||
+ PartialEq
|
||||
+ core::ops::BitAnd<Output = T>
|
||||
+ core::ops::BitOr<Output = T>
|
||||
+ core::ops::Not<Output = T>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn read(&self) -> T {
|
||||
unsafe { ptr::read_volatile(self.ptr) }
|
||||
}
|
||||
|
||||
fn write(&mut self, value: T) {
|
||||
unsafe { ptr::write_volatile(self.ptr, value) };
|
||||
}
|
||||
}
|
||||
|
||||
// x86 u8 implementation
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
impl Io for MmioPtr<u8> {
|
||||
type Value = u8;
|
||||
|
||||
fn read(&self) -> Self::Value {
|
||||
unsafe {
|
||||
let value: Self::Value;
|
||||
core::arch::asm!(
|
||||
"mov {}, [{}]",
|
||||
out(reg_byte) value,
|
||||
in(reg) self.ptr
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, value: Self::Value) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov [{}], {}",
|
||||
in(reg) self.ptr,
|
||||
in(reg_byte) value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// x86 u16 implementation
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
impl Io for MmioPtr<u16> {
|
||||
type Value = u16;
|
||||
|
||||
fn read(&self) -> Self::Value {
|
||||
unsafe {
|
||||
let value: Self::Value;
|
||||
core::arch::asm!(
|
||||
"mov {:x}, [{}]",
|
||||
out(reg) value,
|
||||
in(reg) self.ptr
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, value: Self::Value) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov [{}], {:x}",
|
||||
in(reg) self.ptr,
|
||||
in(reg) value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// x86 u32 implementation
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
impl Io for MmioPtr<u32> {
|
||||
type Value = u32;
|
||||
|
||||
fn read(&self) -> Self::Value {
|
||||
unsafe {
|
||||
let value: Self::Value;
|
||||
core::arch::asm!(
|
||||
"mov {:e}, [{}]",
|
||||
out(reg) value,
|
||||
in(reg) self.ptr
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, value: Self::Value) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov [{}], {:e}",
|
||||
in(reg) self.ptr,
|
||||
in(reg) value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// x86 u64 implementation (x86_64 only)
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl Io for MmioPtr<u64> {
|
||||
type Value = u64;
|
||||
|
||||
fn read(&self) -> Self::Value {
|
||||
unsafe {
|
||||
let value: Self::Value;
|
||||
core::arch::asm!(
|
||||
"mov {:r}, [{}]",
|
||||
out(reg) value,
|
||||
in(reg) self.ptr
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, value: Self::Value) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov [{}], {:r}",
|
||||
in(reg) self.ptr,
|
||||
in(reg) value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "ihdgd"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bitbang-hal = "0.3"
|
||||
#TODO: edid is abandoned, fork it an maintain?
|
||||
edid = "0.3.0"
|
||||
#TODO: waiting for bitbang-hal to update to embedded-hal 1.0
|
||||
embedded-hal = "0.2.7"
|
||||
log = "0.4"
|
||||
nb = "1.0"
|
||||
# Patched to allow for exact range allocation
|
||||
range-alloc = { git = "https://github.com/jackpot51/range-alloc.git" }
|
||||
void = "1.0"
|
||||
|
||||
common = { path = "../../common" }
|
||||
daemon = { path = "../../../daemon" }
|
||||
driver-graphics = { path = "../driver-graphics" }
|
||||
graphics-ipc = { path = "../graphics-ipc" }
|
||||
inputd = { path = "../../inputd" }
|
||||
pcid = { path = "../../pcid" }
|
||||
|
||||
libredox = "0.1.3"
|
||||
redox-scheme = "0.8"
|
||||
redox_event = "0.4.1"
|
||||
redox_syscall = "0.6"
|
||||
@@ -0,0 +1,55 @@
|
||||
[[drivers]]
|
||||
name = "Intel HD Graphics"
|
||||
class = 0x03
|
||||
ids = { 0x8086 = [
|
||||
# Kaby Lake from Volume 4: Configurations in
|
||||
# https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/kaby-lake.html
|
||||
0x5912,
|
||||
0x5916,
|
||||
0x591B,
|
||||
0x591E,
|
||||
0x5926,
|
||||
# Comet Lake from Volume 1: Configurations in
|
||||
# https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/comet-lake.html
|
||||
0x9B21,
|
||||
0x9B41,
|
||||
0x9BA4,
|
||||
0x9BAA,
|
||||
0x9BAC,
|
||||
0x9BC4,
|
||||
0x9BC5,
|
||||
0x9BC6,
|
||||
0x9BC8,
|
||||
0x9BCA,
|
||||
0x9BCC,
|
||||
0x9BE6,
|
||||
0x9BF6,
|
||||
# Tiger Lake Mobile from Volume 4: Configurations in
|
||||
# https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/tiger-lake.html
|
||||
0x9A40,
|
||||
0x9A49,
|
||||
0x9A60,
|
||||
0x9A68,
|
||||
0x9A70,
|
||||
0x9A78,
|
||||
# Alchemist from Volume 4: Configurations in
|
||||
# https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/alchemist-arctic-sound-m.html
|
||||
0x5690, # A770M
|
||||
0x5691, # A730M
|
||||
0x5692, # A550M
|
||||
0x5693, # A370M
|
||||
0x5694, # A350M
|
||||
0x5696, # A570M
|
||||
0x5697, # A530M
|
||||
0x56A0, # A770
|
||||
0x56A1, # A750
|
||||
0x56A5, # A380
|
||||
0x56A6, # A310
|
||||
0x56B0, # Pro A30M
|
||||
0x56B1, # Pro A40/A50
|
||||
0x56B2, # Pro A60M
|
||||
0x56B3, # Pro A60
|
||||
0x56C0, # GPU Flex 170
|
||||
0x56C1, # GPU Flex 140
|
||||
] }
|
||||
command = ["ihdgd"]
|
||||
@@ -0,0 +1,166 @@
|
||||
use common::{io::Io, timeout::Timeout};
|
||||
use embedded_hal::blocking::i2c::{self, SevenBitAddress, Operation, Transactional};
|
||||
|
||||
use super::ddi::*;
|
||||
|
||||
pub struct Aux<'a> {
|
||||
ddi: &'a mut Ddi
|
||||
}
|
||||
|
||||
impl<'a> Aux<'a> {
|
||||
pub fn new(ddi: &'a mut Ddi) -> Self {
|
||||
Self { ddi }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Transactional for Aux<'a> {
|
||||
type Error = ();
|
||||
fn exec(&mut self, addr7: SevenBitAddress, full_ops: &mut [Operation<'_>]) -> Result<(), ()> {
|
||||
// Break ops into 16-byte chunks that will fit into aux data
|
||||
let mut ops = Vec::new();
|
||||
for op in full_ops.iter_mut() {
|
||||
match op {
|
||||
Operation::Read(buf) => {
|
||||
for chunk in buf.chunks_mut(16) {
|
||||
ops.push(Operation::Read(chunk));
|
||||
}
|
||||
}
|
||||
Operation::Write(buf) => {
|
||||
for chunk in buf.chunks(16) {
|
||||
ops.push(Operation::Write(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ops_len = ops.len();
|
||||
for (i, op) in ops.iter_mut().enumerate() {
|
||||
// Write header and data
|
||||
let mut header = 0;
|
||||
match op {
|
||||
Operation::Read(_) => {
|
||||
header |= 1 << 4;
|
||||
},
|
||||
Operation::Write(_) => ()
|
||||
}
|
||||
if (i + 1) < ops_len {
|
||||
// Middle of transaction
|
||||
header |= 1 << 6;
|
||||
}
|
||||
let mut aux_datas = [0u8; 20];
|
||||
let mut aux_data_i = 0;
|
||||
aux_datas[aux_data_i] = header;
|
||||
aux_data_i += 1;
|
||||
//TODO: what is this byte?
|
||||
aux_datas[aux_data_i] = 0;
|
||||
aux_data_i += 1;
|
||||
aux_datas[aux_data_i] = addr7;
|
||||
aux_data_i += 1;
|
||||
match op {
|
||||
Operation::Read(buf) => {
|
||||
if !buf.is_empty() {
|
||||
aux_datas[aux_data_i] = (buf.len() - 1) as u8;
|
||||
aux_data_i += 1;
|
||||
}
|
||||
}
|
||||
Operation::Write(buf) => {
|
||||
if !buf.is_empty() {
|
||||
aux_datas[aux_data_i] = (buf.len() - 1) as u8;
|
||||
aux_data_i += 1;
|
||||
for b in buf.iter() {
|
||||
aux_datas[aux_data_i] = *b;
|
||||
aux_data_i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write data to registers (big endian, dword access only)
|
||||
for (i, chunk) in aux_datas.chunks(4).enumerate() {
|
||||
let mut bytes = [0; 4];
|
||||
bytes[..chunk.len()].copy_from_slice(&chunk);
|
||||
self.ddi.aux_datas[i].write(u32::from_be_bytes(bytes));
|
||||
}
|
||||
|
||||
let mut v = self.ddi.aux_ctl.read();
|
||||
// Set length
|
||||
v &= !DDI_AUX_CTL_SIZE_MASK;
|
||||
v |= (aux_data_i as u32) << DDI_AUX_CTL_SIZE_SHIFT;
|
||||
// Set timeout
|
||||
v &= !DDI_AUX_CTL_TIMEOUT_MASK;
|
||||
v |= DDI_AUX_CTL_TIMEOUT_4000US;
|
||||
// Set I/O select to legacy (cleared)
|
||||
//TODO: TBT support?
|
||||
v &= !DDI_AUX_CTL_IO_SELECT;
|
||||
// Start transaction
|
||||
v |= DDI_AUX_CTL_BUSY;
|
||||
self.ddi.aux_ctl.write(v);
|
||||
|
||||
// Wait while busy
|
||||
let timeout = Timeout::from_secs(1);
|
||||
while self.ddi.aux_ctl.readf(DDI_AUX_CTL_BUSY) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::debug!("AUX I2C transaction wait timeout 0x{:08X}", self.ddi.aux_ctl.read());
|
||||
()
|
||||
})?;
|
||||
}
|
||||
|
||||
// Read result
|
||||
v = self.ddi.aux_ctl.read();
|
||||
if (v & DDI_AUX_CTL_TIMEOUT_ERROR) != 0 {
|
||||
log::debug!("AUX I2C transaction timeout error");
|
||||
return Err(());
|
||||
}
|
||||
if (v & DDI_AUX_CTL_RECEIVE_ERROR) != 0 {
|
||||
log::debug!("AUX I2C transaction receive error");
|
||||
return Err(());
|
||||
}
|
||||
if (v & DDI_AUX_CTL_DONE) == 0 {
|
||||
log::debug!("AUX I2C transaction done not set");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Read data from registers (big endian, dword access only)
|
||||
for (i, chunk) in aux_datas.chunks_mut(4).enumerate() {
|
||||
let bytes = self.ddi.aux_datas[i].read().to_be_bytes();
|
||||
chunk.copy_from_slice(&bytes[..chunk.len()]);
|
||||
}
|
||||
|
||||
aux_data_i = 0;
|
||||
let response = aux_datas[aux_data_i];
|
||||
if response != 0 {
|
||||
log::debug!("AUX I2C unexpected response {:02X}", response);
|
||||
return Err(());
|
||||
}
|
||||
aux_data_i += 1;
|
||||
match op {
|
||||
Operation::Read(buf) => {
|
||||
if !buf.is_empty() {
|
||||
for b in buf.iter_mut() {
|
||||
*b = aux_datas[aux_data_i];
|
||||
aux_data_i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::Write(_) => ()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> i2c::WriteRead for Aux<'a> {
|
||||
type Error = ();
|
||||
fn write_read(
|
||||
&mut self,
|
||||
addr7: SevenBitAddress,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8]
|
||||
) -> Result<(), ()> {
|
||||
self.exec(addr7, &mut [
|
||||
Operation::Write(bytes),
|
||||
Operation::Read(buffer),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
use common::io::{Io, MmioPtr, WriteOnly};
|
||||
use std::sync::Arc;
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use super::{DeviceKind, GpioPort, MmioRegion};
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_CTL
|
||||
pub const DDI_AUX_CTL_BUSY: u32 = 1 << 31;
|
||||
pub const DDI_AUX_CTL_DONE: u32 = 1 << 30;
|
||||
pub const DDI_AUX_CTL_TIMEOUT_ERROR: u32 = 1 << 28;
|
||||
pub const DDI_AUX_CTL_TIMEOUT_SHIFT: u32 = 26;
|
||||
pub const DDI_AUX_CTL_TIMEOUT_MASK: u32 = 0b11 << DDI_AUX_CTL_TIMEOUT_SHIFT;
|
||||
pub const DDI_AUX_CTL_TIMEOUT_4000US: u32 = 0b11 << DDI_AUX_CTL_TIMEOUT_SHIFT;
|
||||
pub const DDI_AUX_CTL_RECEIVE_ERROR: u32 = 1 << 25;
|
||||
pub const DDI_AUX_CTL_SIZE_SHIFT: u32 = 20;
|
||||
pub const DDI_AUX_CTL_SIZE_MASK: u32 = 0b11111 << 20;
|
||||
pub const DDI_AUX_CTL_IO_SELECT: u32 = 1 << 11;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DDI_BUF_CTL
|
||||
pub const DDI_BUF_CTL_ENABLE: u32 = 1 << 31;
|
||||
pub const DDI_BUF_CTL_IDLE: u32 = 1 << 7;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_CL_DW5
|
||||
pub const PORT_CL_DW5_SUS_CLOCK_MASK: u32 = 0b11 << 0;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_CL_DW10
|
||||
pub const PORT_CL_DW10_EDP4K2K_MODE_OVRD_EN: u32 = 1 << 3;
|
||||
pub const PORT_CL_DW10_EDP4K2K_MODE_OVRD_VAL: u32 = 1 << 2;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_PCS_DW9
|
||||
pub const PORT_PCS_DW1_CMNKEEPER_ENABLE: u32 = 1 << 26;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW2
|
||||
pub const PORT_TX_DW2_SWING_SEL_UPPER_SHIFT: u32 = 15;
|
||||
pub const PORT_TX_DW2_SWING_SEL_UPPER_MASK: u32 = 1 << PORT_TX_DW2_SWING_SEL_UPPER_SHIFT;
|
||||
pub const PORT_TX_DW2_SWING_SEL_LOWER_SHIFT: u32 = 11;
|
||||
pub const PORT_TX_DW2_SWING_SEL_LOWER_MASK: u32 = 0b111 << PORT_TX_DW2_SWING_SEL_LOWER_SHIFT;
|
||||
pub const PORT_TX_DW2_RCOMP_SCALAR_SHIFT: u32 = 0;
|
||||
pub const PORT_TX_DW2_RCOMP_SCALAR_MASK: u32 = 0xFF << PORT_TX_DW2_RCOMP_SCALAR_SHIFT;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW4
|
||||
pub const PORT_TX_DW4_SELECT: u32 = 1 << 31;
|
||||
pub const PORT_TX_DW4_POST_CURSOR_1_SHIFT: u32 = 12;
|
||||
pub const PORT_TX_DW4_POST_CURSOR_1_MASK: u32 = 0b111111 << PORT_TX_DW4_POST_CURSOR_1_SHIFT;
|
||||
pub const PORT_TX_DW4_POST_CURSOR_2_SHIFT: u32 = 6;
|
||||
pub const PORT_TX_DW4_POST_CURSOR_2_MASK: u32 = 0b111111 << PORT_TX_DW4_POST_CURSOR_2_SHIFT;
|
||||
pub const PORT_TX_DW4_CURSOR_COEFF_SHIFT: u32 = 0;
|
||||
pub const PORT_TX_DW4_CURSOR_COEFF_MASK: u32 = 0b111111 << PORT_TX_DW4_CURSOR_COEFF_SHIFT;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW5
|
||||
pub const PORT_TX_DW5_TRAINING_ENABLE: u32 = 1 << 31;
|
||||
pub const PORT_TX_DW5_DISABLE_2_TAP_SHIFT: u32 = 29;
|
||||
pub const PORT_TX_DW5_DISABLE_2_TAP: u32 = 1 << PORT_TX_DW5_DISABLE_2_TAP_SHIFT;
|
||||
pub const PORT_TX_DW5_DISABLE_3_TAP: u32 = 1 << 29;
|
||||
pub const PORT_TX_DW5_CURSOR_PROGRAM: u32 = 1 << 26;
|
||||
pub const PORT_TX_DW5_COEFF_POLARITY: u32 = 1 << 25;
|
||||
pub const PORT_TX_DW5_SCALING_MODE_SEL_SHIFT: u32 = 18;
|
||||
pub const PORT_TX_DW5_SCALING_MODE_SEL_MASK: u32 = 0b111 << PORT_TX_DW5_SCALING_MODE_SEL_SHIFT;
|
||||
pub const PORT_TX_DW5_RTERM_SELECT_SHIFT: u32 = 3;
|
||||
pub const PORT_TX_DW5_RTERM_SELECT_MASK: u32 = 0b111 << PORT_TX_DW5_RTERM_SELECT_SHIFT;
|
||||
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW7
|
||||
pub const PORT_TX_DW7_N_SCALAR_SHIFT: u32 = 24;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum PortClReg {
|
||||
Dw5 = 0x14,
|
||||
Dw10 = 0x28,
|
||||
Dw12 = 0x30,
|
||||
Dw15 = 0x3C,
|
||||
Dw16 = 0x40,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum PortCompReg {
|
||||
Dw0 = 0x100,
|
||||
Dw1 = 0x104,
|
||||
Dw3 = 0x10C,
|
||||
Dw8 = 0x120,
|
||||
Dw9 = 0x124,
|
||||
Dw10 = 0x128,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum PortPcsReg {
|
||||
Dw1 = 0x04,
|
||||
Dw9 = 0x24,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum PortTxReg {
|
||||
Dw0 = 0x80,
|
||||
Dw1 = 0x84,
|
||||
Dw2 = 0x88,
|
||||
Dw4 = 0x90,
|
||||
Dw5 = 0x94,
|
||||
Dw6 = 0x98,
|
||||
Dw7 = 0x9C,
|
||||
Dw8 = 0xA0,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum PortLane {
|
||||
Aux = 0x300,
|
||||
Grp = 0x600,
|
||||
Ln0 = 0x800,
|
||||
Ln1 = 0x900,
|
||||
Ln2 = 0xA00,
|
||||
Ln3 = 0xB00,
|
||||
}
|
||||
|
||||
pub struct Ddi {
|
||||
pub name: &'static str,
|
||||
pub index: usize,
|
||||
pub gttmm: Arc<MmioRegion>,
|
||||
pub port_base: Option<usize>,
|
||||
pub aux_ctl: MmioPtr<u32>,
|
||||
pub aux_datas: [MmioPtr<u32>; 5],
|
||||
pub buf_ctl: MmioPtr<u32>,
|
||||
pub dpclka_cfgcr0_clock_shift: Option<u32>,
|
||||
pub dpclka_cfgcr0_clock_off: Option<u32>,
|
||||
pub gmbus_pin_pair: Option<u8>,
|
||||
pub gpio_port: Option<GpioPort>,
|
||||
pub pwr_well_ctl_aux_request: u32,
|
||||
pub pwr_well_ctl_aux_state: u32,
|
||||
pub pwr_well_ctl_ddi_request: u32,
|
||||
pub pwr_well_ctl_ddi_state: u32,
|
||||
pub sde_interrupt_hotplug: Option<u32>,
|
||||
pub transcoder_index: Option<u32>,
|
||||
}
|
||||
|
||||
//TODO: verify offsets and count using DeviceKind?
|
||||
impl Ddi {
|
||||
pub fn dump(&self) {
|
||||
eprint!("Ddi {} {}", self.name, self.index);
|
||||
eprint!(" buf_ctl {:08X}", self.buf_ctl.read());
|
||||
let lanes = [PortLane::Ln0, PortLane::Ln1, PortLane::Ln2, PortLane::Ln3];
|
||||
for reg in [
|
||||
PortClReg::Dw5,
|
||||
PortClReg::Dw10,
|
||||
PortClReg::Dw12,
|
||||
PortClReg::Dw15,
|
||||
PortClReg::Dw16,
|
||||
] {
|
||||
if let Some(mmio) = self.port_cl(reg) {
|
||||
eprint!(" CL_{:?} {:08X}", reg, mmio.read());
|
||||
}
|
||||
}
|
||||
for reg in [
|
||||
PortPcsReg::Dw1,
|
||||
PortPcsReg::Dw9,
|
||||
] {
|
||||
for lane in lanes {
|
||||
if let Some(mmio) = self.port_pcs(reg, lane) {
|
||||
eprint!(" PCS_{:?}_{:?} {:08X}", reg, lane, mmio.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
for reg in [
|
||||
PortTxReg::Dw0,
|
||||
PortTxReg::Dw1,
|
||||
PortTxReg::Dw2,
|
||||
PortTxReg::Dw4,
|
||||
PortTxReg::Dw5,
|
||||
PortTxReg::Dw6,
|
||||
PortTxReg::Dw7,
|
||||
PortTxReg::Dw8,
|
||||
] {
|
||||
for lane in lanes {
|
||||
if let Some(mmio) = self.port_tx(reg, lane) {
|
||||
eprint!(" TX_{:?}_{:?} {:08X}", reg, lane, mmio.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
fn port_reg(&self, offset: usize) -> Option<MmioPtr<u32>> {
|
||||
//TODO: handle gttmm.mmio error?
|
||||
unsafe { self.gttmm.mmio(self.port_base? + offset).ok() }
|
||||
}
|
||||
|
||||
pub fn port_cl(&self, reg: PortClReg) -> Option<MmioPtr<u32>> {
|
||||
self.port_reg(reg as usize)
|
||||
}
|
||||
|
||||
pub fn port_comp(&self, reg: PortCompReg) -> Option<MmioPtr<u32>> {
|
||||
self.port_reg(reg as usize)
|
||||
}
|
||||
|
||||
//TODO: return WriteOnly if PortLane::Grp?
|
||||
pub fn port_pcs(&self, reg: PortPcsReg, lane: PortLane) -> Option<MmioPtr<u32>> {
|
||||
self.port_reg((reg as usize) + (lane as usize))
|
||||
}
|
||||
|
||||
//TODO: return WriteOnly if PortLane::Grp?
|
||||
pub fn port_tx(&self, reg: PortTxReg, lane: PortLane) -> Option<MmioPtr<u32>> {
|
||||
self.port_reg((reg as usize) + (lane as usize))
|
||||
}
|
||||
|
||||
pub fn voltage_swing_hdmi(&mut self, gttmm: &MmioRegion, timing: &edid::DetailedTiming) -> Result<()> {
|
||||
struct Setting {
|
||||
dw2_swing_sel: u32,
|
||||
dw7_n_scalar: u32,
|
||||
dw4_cursor_coeff: u32,
|
||||
dw4_post_cursor_1: u32,
|
||||
dw5_2_tap_disable: u32,
|
||||
}
|
||||
|
||||
impl Setting {
|
||||
pub fn new(
|
||||
dw2_swing_sel: u32,
|
||||
dw7_n_scalar: u32,
|
||||
dw4_cursor_coeff: u32,
|
||||
dw4_post_cursor_1: u32,
|
||||
dw5_2_tap_disable: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
dw2_swing_sel,
|
||||
dw7_n_scalar,
|
||||
dw4_cursor_coeff,
|
||||
dw4_post_cursor_1,
|
||||
dw5_2_tap_disable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IHD-OS-TGL-Vol 12-1.22-Rev2.0 "Voltage Swing Programming"
|
||||
let settings = vec![
|
||||
// HDMI 450mV, 450mV, 0.0dB
|
||||
Setting::new(0b1010, 0x60, 0x3F, 0x00, 0b0),
|
||||
// HDMI 450mV, 650mV, 3.2dB
|
||||
Setting::new(0b1011, 0x73, 0x36, 0x09, 0b0),
|
||||
// HDMI 450mV, 850mV, 5.5dB
|
||||
Setting::new(0b0110, 0x7F, 0x31, 0x0E, 0b0),
|
||||
// HDMI 650mV, 650mV, 0.0dB
|
||||
Setting::new(0b1011, 0x73, 0x3F, 0x00, 0b0),
|
||||
// HDMI 650mV, 850mV, 2.3dB
|
||||
Setting::new(0b0110, 0x7F, 0x37, 0x08, 0b0),
|
||||
// HDMI 850mV, 850mV, 0.0dB
|
||||
Setting::new(0b0110, 0x7F, 0x3F, 0x00, 0b0),
|
||||
// HDMI 600mV, 850mV, 3.0dB
|
||||
Setting::new(0b0110, 0x7F, 0x35, 0x0A, 0b0),
|
||||
];
|
||||
|
||||
// Last setting is the default
|
||||
//TODO: get correct setting index from BIOS
|
||||
let setting = settings.last().unwrap();
|
||||
|
||||
// This allows unwraps on port functions below without panic
|
||||
if self.port_base.is_none() {
|
||||
log::error!("HDMI voltage swing procedure only implemented on combo DDI");
|
||||
return Err(Error::new(EIO));
|
||||
};
|
||||
|
||||
// Clear cmnkeeper_enable for HDMI
|
||||
{
|
||||
// It is not possible to read from GRP register, so use LN0 as template
|
||||
let mut pcs_dw1_ln0 = self.port_pcs(PortPcsReg::Dw1, PortLane::Ln0).unwrap();
|
||||
let mut pcs_dw1_grp = WriteOnly::new(self.port_pcs(PortPcsReg::Dw1, PortLane::Grp).unwrap());
|
||||
let mut v = pcs_dw1_ln0.read();
|
||||
v &= !PORT_PCS_DW1_CMNKEEPER_ENABLE;
|
||||
pcs_dw1_grp.write(v);
|
||||
}
|
||||
|
||||
// Program loadgen select
|
||||
//TODO: this assumes bit rate <= 6 GHz and 4 lanes enabled
|
||||
{
|
||||
let mut tx_dw4_ln0 = self.port_tx(PortTxReg::Dw4, PortLane::Ln0).unwrap();
|
||||
tx_dw4_ln0.writef(PORT_TX_DW4_SELECT, false);
|
||||
|
||||
let mut tx_dw4_ln1 = self.port_tx(PortTxReg::Dw4, PortLane::Ln1).unwrap();
|
||||
tx_dw4_ln1.writef(PORT_TX_DW4_SELECT, true);
|
||||
|
||||
let mut tx_dw4_ln2 = self.port_tx(PortTxReg::Dw4, PortLane::Ln2).unwrap();
|
||||
tx_dw4_ln2.writef(PORT_TX_DW4_SELECT, true);
|
||||
|
||||
let mut tx_dw4_ln3 = self.port_tx(PortTxReg::Dw4, PortLane::Ln3).unwrap();
|
||||
tx_dw4_ln3.writef(PORT_TX_DW4_SELECT, true);
|
||||
}
|
||||
|
||||
// Set PORT_CL_DW5 sus clock config to 11b
|
||||
{
|
||||
let mut cl_dw5 = self.port_cl(PortClReg::Dw5).unwrap();
|
||||
cl_dw5.writef(PORT_CL_DW5_SUS_CLOCK_MASK, true);
|
||||
}
|
||||
|
||||
// Clear training enable to change swing values
|
||||
let mut tx_dw5_ln0 = self.port_tx(PortTxReg::Dw5, PortLane::Ln0).unwrap();
|
||||
let mut tx_dw5_grp = WriteOnly::new(self.port_tx(PortTxReg::Dw5, PortLane::Grp).unwrap());
|
||||
{
|
||||
let mut v = tx_dw5_ln0.read();
|
||||
v &= !PORT_TX_DW5_TRAINING_ENABLE;
|
||||
tx_dw5_grp.write(v);
|
||||
}
|
||||
|
||||
// Program swing and de-emphasis
|
||||
|
||||
// Disable eDP bits in PORT_CL_DW10
|
||||
let mut cl_dw10 = self.port_cl(PortClReg::Dw10).unwrap();
|
||||
cl_dw10.writef(PORT_CL_DW10_EDP4K2K_MODE_OVRD_EN | PORT_CL_DW10_EDP4K2K_MODE_OVRD_VAL, false);
|
||||
|
||||
// For PORT_TX_DW5:
|
||||
// - Set 2 tap disable from settings
|
||||
// - Set scaling mode sel to 010b
|
||||
// - Set rterm select to 110b
|
||||
// - Set 3 tap disable to 1
|
||||
// - Set cursor program to 0
|
||||
// - Set coeff polarity to 0
|
||||
{
|
||||
let mut v = tx_dw5_ln0.read();
|
||||
v &= !(
|
||||
PORT_TX_DW5_DISABLE_2_TAP |
|
||||
PORT_TX_DW5_CURSOR_PROGRAM |
|
||||
PORT_TX_DW5_COEFF_POLARITY |
|
||||
PORT_TX_DW5_SCALING_MODE_SEL_MASK |
|
||||
PORT_TX_DW5_RTERM_SELECT_MASK
|
||||
);
|
||||
v |= (
|
||||
(setting.dw5_2_tap_disable << PORT_TX_DW5_DISABLE_2_TAP_SHIFT) |
|
||||
PORT_TX_DW5_DISABLE_3_TAP |
|
||||
(0b010 << PORT_TX_DW5_SCALING_MODE_SEL_SHIFT) |
|
||||
(0b110 << PORT_TX_DW5_RTERM_SELECT_SHIFT)
|
||||
);
|
||||
tx_dw5_grp.write(v);
|
||||
}
|
||||
|
||||
// Individual lane settings are used to avoid overwriting lane-specific settings, and because
|
||||
// group registers cannot be read
|
||||
let lanes = [PortLane::Ln0, PortLane::Ln1, PortLane::Ln2, PortLane::Ln3];
|
||||
|
||||
// For PORT_TX_DW2:
|
||||
// - Set swing sel from settings
|
||||
// - Set rcomp scalar to 0x98
|
||||
for lane in lanes {
|
||||
let mut tx_dw2 = self.port_tx(PortTxReg::Dw2, lane).unwrap();
|
||||
let mut v = tx_dw2.read();
|
||||
v &= !(
|
||||
PORT_TX_DW2_SWING_SEL_UPPER_MASK |
|
||||
PORT_TX_DW2_SWING_SEL_LOWER_MASK |
|
||||
PORT_TX_DW2_RCOMP_SCALAR_MASK
|
||||
);
|
||||
v |= (
|
||||
(((setting.dw2_swing_sel >> 3) & 1) << PORT_TX_DW2_SWING_SEL_UPPER_SHIFT) |
|
||||
((setting.dw2_swing_sel & 0b111) << PORT_TX_DW2_SWING_SEL_LOWER_SHIFT) |
|
||||
(0x98 << PORT_TX_DW2_RCOMP_SCALAR_SHIFT)
|
||||
|
||||
);
|
||||
tx_dw2.write(v);
|
||||
}
|
||||
|
||||
// For PORT_TX_DW4:
|
||||
// - Set post cursor 1 from settings
|
||||
// - Set post cursor 2 to 0x0
|
||||
// - Set cursor coeff from settings
|
||||
for lane in lanes {
|
||||
let mut tx_dw4 = self.port_tx(PortTxReg::Dw4, lane).unwrap();
|
||||
let mut v = tx_dw4.read();
|
||||
v &= !(
|
||||
PORT_TX_DW4_POST_CURSOR_1_MASK |
|
||||
PORT_TX_DW4_POST_CURSOR_2_MASK |
|
||||
PORT_TX_DW4_CURSOR_COEFF_MASK
|
||||
);
|
||||
v |=
|
||||
(setting.dw4_post_cursor_1 << PORT_TX_DW4_POST_CURSOR_1_SHIFT) |
|
||||
(setting.dw4_cursor_coeff << PORT_TX_DW4_CURSOR_COEFF_SHIFT);
|
||||
tx_dw4.write(v);
|
||||
}
|
||||
|
||||
// For PORT_TX_DW7:
|
||||
// - Set n scalar from settings
|
||||
for lane in lanes {
|
||||
let mut tx_dw7 = self.port_tx(PortTxReg::Dw7, lane).unwrap();
|
||||
// All other bits are spare
|
||||
tx_dw7.write(setting.dw7_n_scalar << PORT_TX_DW7_N_SCALAR_SHIFT);
|
||||
}
|
||||
|
||||
// Set training enable to trigger update
|
||||
{
|
||||
let mut v = tx_dw5_ln0.read();
|
||||
v |= PORT_TX_DW5_TRAINING_ENABLE;
|
||||
tx_dw5_grp.write(v);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn kabylake(gttmm: &Arc<MmioRegion>) -> Result<Vec<Self>> {
|
||||
let mut ddis = Vec::new();
|
||||
for (i, name) in [
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
//TODO: missing AUX regs? "E",
|
||||
].iter().enumerate() {
|
||||
ddis.push(Self {
|
||||
name,
|
||||
index: i,
|
||||
port_base: None, //TODO: port regs
|
||||
gttmm: gttmm.clone(),
|
||||
// IHD-OS-KBL-Vol 2c-1.17 DDI_AUX_CTL
|
||||
aux_ctl: unsafe { gttmm.mmio(0x64010 + i * 0x100)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 DDI_AUX_DATA
|
||||
aux_datas: [
|
||||
unsafe { gttmm.mmio(0x64014 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64018 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x6401C + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64020 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64024 + i * 0x100)? },
|
||||
],
|
||||
// IHD-OS-KBL-Vol 2c-1.17 DDI_BUF_CTL
|
||||
buf_ctl: unsafe { gttmm.mmio(0x64000 + i * 0x100)? },
|
||||
// N/A
|
||||
dpclka_cfgcr0_clock_shift: None,
|
||||
dpclka_cfgcr0_clock_off: None,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 GMBUS0
|
||||
gmbus_pin_pair: match *name {
|
||||
"B" => Some(0b101),
|
||||
"C" => Some(0b100),
|
||||
"D" => Some(0b110),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-KBL-Vol 12-1.17 GMBUS and GPIO
|
||||
gpio_port: match *name {
|
||||
"B" => Some(GpioPort::Port4),
|
||||
"C" => Some(GpioPort::Port3),
|
||||
"D" => Some(GpioPort::Port5),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PWR_WELL_CTL
|
||||
// All auxes go through the same Misc IO request
|
||||
pwr_well_ctl_aux_request: 1 << 1,
|
||||
pwr_well_ctl_aux_state: 1 << 0,
|
||||
pwr_well_ctl_ddi_request: match *name {
|
||||
"A" | "E" => 1 << 3,
|
||||
"B" => 1 << 5,
|
||||
"C" => 1 << 7,
|
||||
"D" => 1 << 9,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
pwr_well_ctl_ddi_state: match *name {
|
||||
"A" | "E" => 1 << 2,
|
||||
"B" => 1 << 4,
|
||||
"C" => 1 << 6,
|
||||
"D" => 1 << 8,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
// IHD-OS-KBL-Vol 2c-1.17 SDE_INTERRUPT
|
||||
sde_interrupt_hotplug: match *name {
|
||||
"A" => Some(1 << 24),
|
||||
"B" => Some(1 << 21),
|
||||
"C" => Some(1 << 22),
|
||||
"D" => Some(1 << 23),
|
||||
"E" => Some(1 << 25),
|
||||
_ => None,
|
||||
},
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_CLK_SEL
|
||||
transcoder_index: match *name {
|
||||
"B" => Some(0b010),
|
||||
"C" => Some(0b011),
|
||||
"D" => Some(0b100),
|
||||
"E" => Some(0b101),
|
||||
_ => None,
|
||||
},
|
||||
});
|
||||
}
|
||||
Ok(ddis)
|
||||
}
|
||||
|
||||
pub fn tigerlake(gttmm: &Arc<MmioRegion>) -> Result<Vec<Self>> {
|
||||
let mut ddis = Vec::new();
|
||||
for (i, name) in [
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"USBC1",
|
||||
"USBC2",
|
||||
"USBC3",
|
||||
"USBC4",
|
||||
"USBC5",
|
||||
"USBC6",
|
||||
].iter().enumerate() {
|
||||
let port_base = match i {
|
||||
0 => Some(0x162000),
|
||||
1 => Some(0x6C000),
|
||||
2 => Some(0x160000),
|
||||
_ => None,
|
||||
};
|
||||
ddis.push(Self {
|
||||
name,
|
||||
index: i,
|
||||
port_base,
|
||||
gttmm: gttmm.clone(),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_CTL
|
||||
aux_ctl: unsafe { gttmm.mmio(0x64010 + i * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_DATA
|
||||
aux_datas: [
|
||||
unsafe { gttmm.mmio(0x64014 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64018 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x6401C + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64020 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64024 + i * 0x100)? },
|
||||
],
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DDI_BUF_CTL
|
||||
buf_ctl: unsafe { gttmm.mmio(0x64000 + i * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPCLKA_CFGCR0
|
||||
dpclka_cfgcr0_clock_shift: match i {
|
||||
0 => Some(0),
|
||||
1 => Some(2),
|
||||
2 => Some(4),
|
||||
_ => None
|
||||
},
|
||||
dpclka_cfgcr0_clock_off: match i {
|
||||
// DDI
|
||||
0 => Some(1 << 10),
|
||||
1 => Some(1 << 11),
|
||||
2 => Some(1 << 24),
|
||||
// Type C
|
||||
3 => Some(1 << 12),
|
||||
4 => Some(1 << 13),
|
||||
5 => Some(1 << 14),
|
||||
6 => Some(1 << 21),
|
||||
7 => Some(1 << 22),
|
||||
8 => Some(1 << 23),
|
||||
_ => None
|
||||
},
|
||||
//TODO: link to docs
|
||||
gmbus_pin_pair: match i {
|
||||
// DDI pins
|
||||
0 => Some(1),
|
||||
1 => Some(2),
|
||||
2 => Some(3),
|
||||
// Type C pins
|
||||
3 => Some(9),
|
||||
4 => Some(10),
|
||||
5 => Some(11),
|
||||
6 => Some(12),
|
||||
7 => Some(13),
|
||||
8 => Some(14),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-TGL-Vol 12-1.22-Rev2.0 GMBUS and GPIO
|
||||
gpio_port: match *name {
|
||||
"A" => Some(GpioPort::Port1),
|
||||
"B" => Some(GpioPort::Port2),
|
||||
"C" => Some(GpioPort::Port3),
|
||||
"USBC1" => Some(GpioPort::Port9),
|
||||
"USBC2" => Some(GpioPort::Port10),
|
||||
"USBC3" => Some(GpioPort::Port11),
|
||||
"USBC4" => Some(GpioPort::Port12),
|
||||
"USBC5" => Some(GpioPort::Port13),
|
||||
"USBC6" => Some(GpioPort::Port14),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_AUX
|
||||
pwr_well_ctl_aux_request: 2 << (i * 2),
|
||||
pwr_well_ctl_aux_state: 1 << (i * 2),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_DDI
|
||||
pwr_well_ctl_ddi_request: 2 << (i * 2),
|
||||
pwr_well_ctl_ddi_state: 1 << (i * 2),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 SDE_INTERRUPT
|
||||
sde_interrupt_hotplug: match i {
|
||||
0 => Some(1 << 16),
|
||||
1 => Some(1 << 17),
|
||||
2 => Some(1 << 18),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_CLK_SEL
|
||||
transcoder_index: Some((i + 1) as u32),
|
||||
})
|
||||
}
|
||||
Ok(ddis)
|
||||
}
|
||||
|
||||
pub fn alchemist(gttmm: &Arc<MmioRegion>) -> Result<Vec<Self>> {
|
||||
let mut ddis = Vec::new();
|
||||
for (i, name) in [
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"USBC1",
|
||||
"USBC2",
|
||||
"USBC3",
|
||||
"USBC4",
|
||||
"D",
|
||||
"E",
|
||||
].iter().enumerate() {
|
||||
let port_base = match i {
|
||||
0 => Some(0x162000),
|
||||
1 => Some(0x6C000),
|
||||
2 => Some(0x160000),
|
||||
_ => None,
|
||||
};
|
||||
ddis.push(Self {
|
||||
name,
|
||||
index: i,
|
||||
port_base,
|
||||
gttmm: gttmm.clone(),
|
||||
// IHD-OS-ACM-Vol 2c-3.23 DDI_AUX_CTL
|
||||
aux_ctl: unsafe { gttmm.mmio(0x64010 + i * 0x100)? },
|
||||
// IHD-OS-ACM-Vol 2c-3.23 DDI_AUX_DATA
|
||||
aux_datas: [
|
||||
unsafe { gttmm.mmio(0x64014 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64018 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x6401C + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64020 + i * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x64024 + i * 0x100)? },
|
||||
],
|
||||
// IHD-OS-ACM-Vol 2c-3.23 DDI_BUF_CTL
|
||||
buf_ctl: unsafe { gttmm.mmio(0x64000 + i * 0x100)? },
|
||||
// IHD-OS-ACM-Vol 2c-3.23 DPCLKA_CFGCR0
|
||||
dpclka_cfgcr0_clock_shift: match i {
|
||||
0 => Some(0),
|
||||
1 => Some(2),
|
||||
2 => Some(4),
|
||||
_ => None
|
||||
},
|
||||
dpclka_cfgcr0_clock_off: match i {
|
||||
// DDI
|
||||
0 => Some(1 << 10),
|
||||
1 => Some(1 << 11),
|
||||
2 => Some(1 << 24),
|
||||
// Type C
|
||||
3 => Some(1 << 12),
|
||||
4 => Some(1 << 13),
|
||||
5 => Some(1 << 14),
|
||||
6 => Some(1 << 21),
|
||||
7 => Some(1 << 22),
|
||||
8 => Some(1 << 23),
|
||||
_ => None
|
||||
},
|
||||
//TODO: link to docs
|
||||
gmbus_pin_pair: match i {
|
||||
// DDI pins
|
||||
0 => Some(1),
|
||||
1 => Some(2),
|
||||
2 => Some(3),
|
||||
// Type C pins
|
||||
3 => Some(9),
|
||||
4 => Some(10),
|
||||
5 => Some(11),
|
||||
6 => Some(12),
|
||||
7 => Some(13),
|
||||
8 => Some(14),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-ACM-Vol 12-3.23 GMBUS and GPIO
|
||||
gpio_port: match *name {
|
||||
"A" => Some(GpioPort::Port1),
|
||||
"B" => Some(GpioPort::Port2),
|
||||
"C" => Some(GpioPort::Port3),
|
||||
"D" => Some(GpioPort::Port4),
|
||||
"USBC1" => Some(GpioPort::Port9),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_AUX
|
||||
pwr_well_ctl_aux_request: 2 << (i * 2),
|
||||
pwr_well_ctl_aux_state: 1 << (i * 2),
|
||||
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_DDI
|
||||
pwr_well_ctl_ddi_request: 2 << (i * 2),
|
||||
pwr_well_ctl_ddi_state: 1 << (i * 2),
|
||||
// IHD-OS-ACM-Vol 2c-3.23 SDE_INTERRUPT
|
||||
sde_interrupt_hotplug: match i {
|
||||
0 => Some(1 << 16),
|
||||
1 => Some(1 << 17),
|
||||
2 => Some(1 << 18),
|
||||
_ => None
|
||||
},
|
||||
// IHD-OS-ACM-Vol 2c-3.23 TRANS_CLK_SEL
|
||||
transcoder_index: Some((i + 1) as u32),
|
||||
})
|
||||
}
|
||||
Ok(ddis)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
|
||||
use common::io::{Io, MmioPtr};
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use super::{DeviceKind, MmioRegion};
|
||||
|
||||
pub const DPLL_CFGCR1_QDIV_RATIO_SHIFT: u32 = 10;
|
||||
pub const DPLL_CFGCR1_QDIV_RATIO_MASK: u32 = 0xFF << DPLL_CFGCR1_QDIV_RATIO_SHIFT;
|
||||
pub const DPLL_CFGCR1_QDIV_MODE: u32 = 1 << 9;
|
||||
pub const DPLL_CFGCR1_KDIV_1: u32 = 0b001 << 6;
|
||||
pub const DPLL_CFGCR1_KDIV_2: u32 = 0b010 << 6;
|
||||
pub const DPLL_CFGCR1_KDIV_3: u32 = 0b100 << 6;
|
||||
pub const DPLL_CFGCR1_KDIV_MASK: u32 = 0b111 << 6;
|
||||
pub const DPLL_CFGCR1_PDIV_2: u32 = 0b0001 << 2;
|
||||
pub const DPLL_CFGCR1_PDIV_3: u32 = 0b0010 << 2;
|
||||
pub const DPLL_CFGCR1_PDIV_5: u32 = 0b0100 << 2;
|
||||
pub const DPLL_CFGCR1_PDIV_7: u32 = 0b1000 << 2;
|
||||
pub const DPLL_CFGCR1_PDIV_MASK: u32 = 0b1111 << 2;
|
||||
|
||||
pub const DPLL_ENABLE_ENABLE: u32 = 1 << 31;
|
||||
pub const DPLL_ENABLE_LOCK: u32 = 1 << 30;
|
||||
pub const DPLL_ENABLE_POWER_ENABLE: u32 = 1 << 27;
|
||||
pub const DPLL_ENABLE_POWER_STATE: u32 = 1 << 26;
|
||||
|
||||
pub const DPLL_SSC_ENABLE: u32 = 1 << 9;
|
||||
|
||||
pub struct Dpll {
|
||||
pub name: &'static str,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPLL_CFGCR0
|
||||
pub cfgcr0: MmioPtr<u32>,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPLL_CFGCR1
|
||||
pub cfgcr1: MmioPtr<u32>,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPLL_DIV0
|
||||
pub div0: MmioPtr<u32>,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPCLKA_CFGCR0
|
||||
pub dpclka_cfgcr0_clock_value: u32,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPLL_ENABLE
|
||||
pub enable: MmioPtr<u32>,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 DPLL_SSC
|
||||
pub ssc: MmioPtr<u32>,
|
||||
}
|
||||
|
||||
//TODO: verify offsets and count using DeviceKind?
|
||||
impl Dpll {
|
||||
pub fn dump(&self) {
|
||||
eprint!("Dpll {}", self.name);
|
||||
eprint!(" cfgcr0 {:08X}", self.cfgcr0.read());
|
||||
eprint!(" cfgcr1 {:08X}", self.cfgcr1.read());
|
||||
eprint!(" div0 {:08X}", self.div0.read());
|
||||
eprint!(" enable {:08X}", self.enable.read());
|
||||
eprint!(" ssc {:08X}", self.ssc.read());
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
pub fn set_freq_hdmi(&mut self, mut ref_freq: u64, timing: &edid::DetailedTiming) -> Result<()> {
|
||||
// IHD-OS-TGL-Vol 12-1.22-Rev2.0 "Formula for HDMI Mode DPLL Programming"
|
||||
const KHz: u64 = 1_000;
|
||||
const MHz: u64 = KHz * 1_000;
|
||||
let dco_min: u64 = 7_998 * MHz;
|
||||
let dco_mid: u64 = 8_999 * MHz;
|
||||
let dco_max: u64 = 10_000 * MHz;
|
||||
|
||||
// If reference frequency is 38.4, use 19.2 because the DPLL automatically divides that by 2.
|
||||
if ref_freq == 38_400_000 {
|
||||
ref_freq /= 2;
|
||||
}
|
||||
|
||||
//TODO: this symbol frequency is only valid for RGB 8 bits per color
|
||||
let symbol_freq = (timing.pixel_clock as u64) * KHz;
|
||||
let pll_freq = symbol_freq * 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Setting {
|
||||
pdiv: u64,
|
||||
kdiv: u64,
|
||||
qdiv: u64,
|
||||
cfgcr1: u32,
|
||||
dco: u64,
|
||||
dco_dist: u64,
|
||||
}
|
||||
|
||||
let mut best_setting: Option<Setting> = None;
|
||||
for (pdiv, pdiv_reg) in [
|
||||
(2, DPLL_CFGCR1_PDIV_2),
|
||||
(3, DPLL_CFGCR1_PDIV_3),
|
||||
(5, DPLL_CFGCR1_PDIV_5),
|
||||
(7, DPLL_CFGCR1_PDIV_7),
|
||||
] {
|
||||
for (kdiv, kdiv_reg) in [
|
||||
(1, DPLL_CFGCR1_KDIV_1),
|
||||
(2, DPLL_CFGCR1_KDIV_2),
|
||||
(3, DPLL_CFGCR1_KDIV_3),
|
||||
] {
|
||||
let qdiv_range = if kdiv == 2 {
|
||||
1..=0xFF
|
||||
} else {
|
||||
1..=1
|
||||
};
|
||||
for qdiv in qdiv_range {
|
||||
let qdiv_reg = if qdiv == 1 {
|
||||
0
|
||||
} else {
|
||||
((qdiv as u32) << DPLL_CFGCR1_QDIV_RATIO_SHIFT) |
|
||||
DPLL_CFGCR1_QDIV_MODE
|
||||
};
|
||||
|
||||
let dco = pll_freq * pdiv * kdiv * qdiv;
|
||||
if dco <= dco_min || dco >= dco_max {
|
||||
// DCO outside of valid range
|
||||
continue;
|
||||
}
|
||||
|
||||
let dco_dist = dco.abs_diff(dco_mid);
|
||||
|
||||
let setting = Setting {
|
||||
pdiv,
|
||||
kdiv,
|
||||
qdiv,
|
||||
cfgcr1: pdiv_reg | kdiv_reg | qdiv_reg,
|
||||
dco,
|
||||
dco_dist,
|
||||
};
|
||||
|
||||
best_setting = match best_setting.take() {
|
||||
Some(other) if other.dco_dist < setting.dco_dist => Some(other),
|
||||
_ => Some(setting),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(setting) = best_setting else {
|
||||
log::error!("failed to find valid DPLL setting");
|
||||
return Err(Error::new(EIO));
|
||||
};
|
||||
|
||||
eprintln!("{:?}", setting);
|
||||
|
||||
// Configure DPLL_CFGCR0 to set DCO frequency
|
||||
{
|
||||
let dco_int = setting.dco / ref_freq;
|
||||
let dco_fract = ((setting.dco - (dco_int * ref_freq)) << 15) / ref_freq;
|
||||
self.cfgcr0.write(((dco_fract as u32) << 10) | (dco_int as u32));
|
||||
}
|
||||
|
||||
// Configure DPLL_CFGCR1 to set the dividers
|
||||
{
|
||||
let mut v = self.cfgcr1.read();
|
||||
let mask =
|
||||
DPLL_CFGCR1_QDIV_RATIO_MASK |
|
||||
DPLL_CFGCR1_QDIV_MODE |
|
||||
DPLL_CFGCR1_KDIV_MASK |
|
||||
DPLL_CFGCR1_PDIV_MASK;
|
||||
v &= !mask;
|
||||
v |= (setting.cfgcr1 & mask);
|
||||
self.cfgcr1.write(v);
|
||||
}
|
||||
|
||||
// Read back DPLL_CFGCR0 and DPLL_CFGCR1 to ensure writes are complete
|
||||
let _ = self.cfgcr0.read();
|
||||
let _ = self.cfgcr1.read();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn tigerlake(gttmm: &MmioRegion) -> Result<Vec<Self>> {
|
||||
let mut dplls = Vec::new();
|
||||
dplls.push(Self {
|
||||
name: "0",
|
||||
cfgcr0: unsafe { gttmm.mmio(0x164284)? },
|
||||
cfgcr1: unsafe { gttmm.mmio(0x164288)? },
|
||||
div0: unsafe { gttmm.mmio(0x164B00)? },
|
||||
dpclka_cfgcr0_clock_value: 0b00,
|
||||
enable: unsafe { gttmm.mmio(0x46010)? },
|
||||
ssc: unsafe { gttmm.mmio(0x164B10)? },
|
||||
});
|
||||
dplls.push(Self {
|
||||
name: "1",
|
||||
cfgcr0: unsafe { gttmm.mmio(0x16428C)? },
|
||||
cfgcr1: unsafe { gttmm.mmio(0x164290)? },
|
||||
div0: unsafe { gttmm.mmio(0x164C00)? },
|
||||
dpclka_cfgcr0_clock_value: 0b01,
|
||||
enable: unsafe { gttmm.mmio(0x46014)? },
|
||||
ssc: unsafe { gttmm.mmio(0x164C10)? },
|
||||
});
|
||||
/*TODO: not present on U-class CPUs
|
||||
dplls.push(Self {
|
||||
name: "4",
|
||||
cfgcr0: unsafe { gttmm.mmio(0x164294)? },
|
||||
cfgcr1: unsafe { gttmm.mmio(0x164298)? },
|
||||
div0: unsafe { gttmm.mmio(0x164E00)? },
|
||||
dpclka_cfgcr0_clock_value: 0b10,
|
||||
enable: unsafe { gttmm.mmio(0x46018)? },
|
||||
ssc: unsafe { gttmm.mmio(0x164E10)? },
|
||||
});
|
||||
*/
|
||||
Ok(dplls)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use common::{io::{Io, MmioPtr}, timeout::Timeout};
|
||||
use embedded_hal::blocking::i2c::{self, SevenBitAddress, Operation, Transactional};
|
||||
|
||||
use super::MmioRegion;
|
||||
|
||||
const GMBUS1_SW_RDY: u32 = 1 << 30;
|
||||
const GMBUS1_CYCLE_STOP: u32 = 1 << 27;
|
||||
const GMBUS1_CYCLE_INDEX: u32 = 1 << 26;
|
||||
const GMBUS1_CYCLE_WAIT: u32 = 1 << 25;
|
||||
const GMBUS1_SIZE_SHIFT: u32 = 16;
|
||||
const GMBUS1_INDEX_SHIFT: u32 = 8;
|
||||
|
||||
const GMBUS2_HW_RDY: u32 = 1 << 11;
|
||||
const GMBUS2_ACTIVE: u32 = 1 << 9;
|
||||
|
||||
pub struct Gmbus {
|
||||
regs: [MmioPtr<u32>; 6],
|
||||
}
|
||||
|
||||
impl Gmbus {
|
||||
pub unsafe fn new(gttmm: &MmioRegion) -> syscall::Result<Self> {
|
||||
Ok(Self {
|
||||
regs: [
|
||||
gttmm.mmio(0xC5100)?,
|
||||
gttmm.mmio(0xC5104)?,
|
||||
gttmm.mmio(0xC5108)?,
|
||||
gttmm.mmio(0xC510C)?,
|
||||
gttmm.mmio(0xC5110)?,
|
||||
gttmm.mmio(0xC5120)?,
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pin_pair<'a>(&'a mut self, pin_pair: u8) -> GmbusPinPair<'a> {
|
||||
GmbusPinPair {
|
||||
regs: &mut self.regs,
|
||||
pin_pair
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GmbusPinPair<'a> {
|
||||
regs: &'a mut [MmioPtr<u32>; 6],
|
||||
pin_pair: u8,
|
||||
}
|
||||
|
||||
impl<'a> Transactional for GmbusPinPair<'a> {
|
||||
type Error = ();
|
||||
fn exec(&mut self, addr7: SevenBitAddress, ops: &mut [Operation<'_>]) -> Result<(), ()> {
|
||||
let mut ops_iter = ops.iter_mut();
|
||||
//TODO: gmbus is actually smbus, not fully i2c compatible!
|
||||
// The first operation MUST be a write of the index
|
||||
let index = match ops_iter.next() {
|
||||
Some(Operation::Write(buf)) if buf.len() == 1 => buf[0],
|
||||
unsupported => {
|
||||
log::error!("GMBUS unsupported first operation {:?}", unsupported);
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
// Reset
|
||||
self.regs[1].write(0);
|
||||
|
||||
// Set pin pair, enabling interface
|
||||
self.regs[0].write(self.pin_pair as u32);
|
||||
|
||||
for op in ops_iter {
|
||||
// Start operation
|
||||
let (addr8, size) = match op {
|
||||
Operation::Read(buf) => ((addr7 << 1) | 1, buf.len() as u32),
|
||||
Operation::Write(buf) => (addr7 << 1, buf.len() as u32),
|
||||
};
|
||||
if size >= 512 {
|
||||
log::error!("GMBUS transaction size {} too large", size);
|
||||
return Err(());
|
||||
}
|
||||
self.regs[1].write(
|
||||
GMBUS1_SW_RDY |
|
||||
GMBUS1_CYCLE_INDEX |
|
||||
GMBUS1_CYCLE_WAIT |
|
||||
(size << GMBUS1_SIZE_SHIFT) |
|
||||
(index as u32) << GMBUS1_INDEX_SHIFT |
|
||||
(addr8 as u32)
|
||||
);
|
||||
|
||||
// Perform transaction
|
||||
match op {
|
||||
Operation::Read(buf) => {
|
||||
for chunk in buf.chunks_mut(4) {
|
||||
{
|
||||
//TODO: ideal timeout for gmbus read?
|
||||
let timeout = Timeout::from_millis(10);
|
||||
while !self.regs[2].readf(GMBUS2_HW_RDY) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::debug!("timeout on GMBUS read 0x{:08x}", self.regs[2].read());
|
||||
()
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
let bytes = self.regs[3].read().to_le_bytes();
|
||||
chunk.copy_from_slice(&bytes[..chunk.len()]);
|
||||
}
|
||||
},
|
||||
Operation::Write(buf) => {
|
||||
log::warn!("TODO: GMBUS WRITE");
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop transaction
|
||||
self.regs[1].write(GMBUS1_SW_RDY | GMBUS1_CYCLE_STOP);
|
||||
|
||||
// Wait idle
|
||||
let timeout = Timeout::from_millis(10);
|
||||
while self.regs[2].readf(GMBUS2_ACTIVE) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::debug!("timeout on GMBUS active 0x{:08x}", self.regs[2].read());
|
||||
()
|
||||
})?;
|
||||
}
|
||||
|
||||
// Disable GMBUS interface
|
||||
self.regs[0].write(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> i2c::WriteRead for GmbusPinPair<'a> {
|
||||
type Error = ();
|
||||
fn write_read(
|
||||
&mut self,
|
||||
addr7: SevenBitAddress,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8]
|
||||
) -> Result<(), ()> {
|
||||
self.exec(addr7, &mut [
|
||||
Operation::Write(bytes),
|
||||
Operation::Read(buffer),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use common::io::{Io, MmioPtr};
|
||||
use embedded_hal::{
|
||||
digital,
|
||||
blocking::i2c::{self, SevenBitAddress, Operation},
|
||||
};
|
||||
|
||||
use super::MmioRegion;
|
||||
|
||||
const GPIO_DIR_MASK: u32 = 1 << 0;
|
||||
const GPIO_DIR_OUT: u32 = 1 << 1;
|
||||
const GPIO_VAL_MASK: u32 = 1 << 2;
|
||||
const GPIO_VAL_OUT: u32 = 1 << 3;
|
||||
const GPIO_VAL_IN: u32 = 1 << 4;
|
||||
const GPIO_CLOCK_SHIFT: u32 = 0;
|
||||
const GPIO_DATA_SHIFT: u32 = 8;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum GpioPort {
|
||||
Port0 = 0xC5010,
|
||||
Port1 = 0xC5014,
|
||||
Port2 = 0xC5018,
|
||||
Port3 = 0xC501C,
|
||||
Port4 = 0xC5020,
|
||||
Port5 = 0xC5024,
|
||||
Port6 = 0xC5028,
|
||||
Port7 = 0xC502C,
|
||||
Port8 = 0xC5030,
|
||||
Port9 = 0xC5034,
|
||||
Port10 = 0xC5038,
|
||||
Port11 = 0xC503C,
|
||||
Port12 = 0xC5040,
|
||||
Port13 = 0xC5044,
|
||||
Port14 = 0xC5048,
|
||||
Port15 = 0xC504C,
|
||||
}
|
||||
|
||||
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 struct GpioPin {
|
||||
ctl: MmioPtr<u32>,
|
||||
shift: u32,
|
||||
}
|
||||
|
||||
impl digital::InputPin for GpioPin {
|
||||
fn is_high(&self) -> bool {
|
||||
((self.ctl.read() >> self.shift) & GPIO_VAL_IN) == GPIO_VAL_IN
|
||||
}
|
||||
|
||||
fn is_low(&self) -> bool {
|
||||
((self.ctl.read() >> self.shift) & GPIO_VAL_IN) == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl digital::OutputPin for GpioPin {
|
||||
fn set_low(&mut self) {
|
||||
// Set GPIO to output with value 0
|
||||
let value = GPIO_DIR_MASK | GPIO_DIR_OUT | GPIO_VAL_MASK;
|
||||
self.ctl.write(value << self.shift);
|
||||
}
|
||||
|
||||
fn set_high(&mut self) {
|
||||
// Assuming external pull-up, set GPIO to input
|
||||
let value = GPIO_DIR_MASK;
|
||||
self.ctl.write(value << self.shift);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod timer;
|
||||
pub use self::timer::*;
|
||||
@@ -0,0 +1,38 @@
|
||||
use embedded_hal::timer;
|
||||
use std::time::{Duration, Instant};
|
||||
use void::Void;
|
||||
|
||||
pub struct HalTimer {
|
||||
instant: Instant,
|
||||
duration: Duration,
|
||||
}
|
||||
|
||||
impl HalTimer {
|
||||
pub fn new(duration: Duration) -> Self {
|
||||
Self {
|
||||
instant: Instant::now(),
|
||||
duration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl timer::CountDown for HalTimer {
|
||||
type Time = Duration;
|
||||
fn start<T: Into<Duration>>(&mut self, duration: T) {
|
||||
self.instant = Instant::now();
|
||||
self.duration = duration.into();
|
||||
}
|
||||
|
||||
fn wait(&mut self) -> nb::Result<(), Void> {
|
||||
if self.instant.elapsed() < self.duration {
|
||||
std::thread::yield_now();
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
// Since this is periodic it must trigger at the next duration
|
||||
self.instant += self.duration;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl timer::Periodic for HalTimer {}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,181 @@
|
||||
use common::io::{Io, MmioPtr};
|
||||
use syscall::error::Result;
|
||||
|
||||
use super::MmioRegion;
|
||||
|
||||
pub const PLANE_CTL_ENABLE: u32 = 1 << 31;
|
||||
|
||||
pub const PLANE_WM_ENABLE: u32 = 1 << 31;
|
||||
pub const PLANE_WM_LINES_SHIFT: u32 = 14;
|
||||
|
||||
pub struct Plane {
|
||||
pub name: &'static str,
|
||||
pub index: usize,
|
||||
pub buf_cfg: MmioPtr<u32>,
|
||||
pub color_ctl: Option<MmioPtr<u32>>,
|
||||
pub color_ctl_gamma_disable: u32,
|
||||
pub ctl: MmioPtr<u32>,
|
||||
pub ctl_source_rgb_8888: u32,
|
||||
pub ctl_source_mask: u32,
|
||||
pub offset: MmioPtr<u32>,
|
||||
pub pos: MmioPtr<u32>,
|
||||
pub size: MmioPtr<u32>,
|
||||
pub stride: MmioPtr<u32>,
|
||||
pub surf: MmioPtr<u32>,
|
||||
pub wm: [MmioPtr<u32>; 8],
|
||||
pub wm_trans: MmioPtr<u32>,
|
||||
}
|
||||
|
||||
impl Plane {
|
||||
pub fn dump(&self) {
|
||||
eprint!("Plane {}", self.name);
|
||||
eprint!(" buf_cfg {:08X}", self.buf_cfg.read());
|
||||
if let Some(reg) = &self.color_ctl {
|
||||
eprint!(" color_ctl {:08X}", reg.read());
|
||||
}
|
||||
eprint!(" ctl {:08X}", self.ctl.read());
|
||||
eprint!(" offset {:08X}", self.offset.read());
|
||||
eprint!(" pos {:08X}", self.offset.read());
|
||||
eprint!(" size {:08X}", self.size.read());
|
||||
eprint!(" stride {:08X}", self.stride.read());
|
||||
eprint!(" surf {:08X}", self.surf.read());
|
||||
for i in 0..self.wm.len() {
|
||||
eprint!(" wm_{} {:08X}", i, self.wm[i].read());
|
||||
}
|
||||
eprint!(" wm_trans {:08X}", self.wm_trans.read());
|
||||
eprintln!();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pipe {
|
||||
pub name: &'static str,
|
||||
pub index: usize,
|
||||
pub planes: Vec<Plane>,
|
||||
pub bottom_color: MmioPtr<u32>,
|
||||
pub misc: MmioPtr<u32>,
|
||||
pub srcsz: MmioPtr<u32>,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
pub fn dump(&self) {
|
||||
eprint!("Pipe {}", self.name);
|
||||
eprint!(" bottom_color {:08X}", self.bottom_color.read());
|
||||
eprint!(" misc {:08X}", self.misc.read());
|
||||
eprint!(" srcsz {:08X}", self.srcsz.read());
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
pub fn kabylake(gttmm: &MmioRegion) -> Result<Vec<Self>> {
|
||||
let mut pipes = Vec::with_capacity(3);
|
||||
for (i, name) in ["A", "B", "C"].iter().enumerate() {
|
||||
let mut planes = Vec::new();
|
||||
//TODO: cursor plane
|
||||
for (j, name) in ["1", "2", "3"].iter().enumerate() {
|
||||
planes.push(Plane {
|
||||
name,
|
||||
index: j,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_BUF_CFG
|
||||
buf_cfg: unsafe { gttmm.mmio(0x7027C + i * 0x1000 + j * 0x100)? },
|
||||
// N/A
|
||||
color_ctl: None,
|
||||
color_ctl_gamma_disable: 0,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_CTL
|
||||
ctl: unsafe { gttmm.mmio(0x70180 + i * 0x1000 + j * 0x100)? },
|
||||
ctl_source_rgb_8888: 0b0100 << 24,
|
||||
ctl_source_mask: 0b1111 << 24,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_OFFSET
|
||||
offset: unsafe { gttmm.mmio(0x701A4 + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_POS
|
||||
pos: unsafe { gttmm.mmio(0x7018C + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_SIZE
|
||||
size: unsafe { gttmm.mmio(0x70190 + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_STRIDE
|
||||
stride: unsafe { gttmm.mmio(0x70188 + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_SURF
|
||||
surf: unsafe { gttmm.mmio(0x7019C + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PLANE_WM
|
||||
wm: [
|
||||
unsafe { gttmm.mmio(0x70240 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70244 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70248 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x7024C + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70250 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70254 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70258 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x7025C + i * 0x1000 + j * 0x100)? },
|
||||
],
|
||||
wm_trans: unsafe { gttmm.mmio(0x70268 + i * 0x1000 + j * 0x100)? },
|
||||
});
|
||||
}
|
||||
pipes.push(Pipe {
|
||||
name,
|
||||
index: i,
|
||||
planes,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PIPE_BOTTOM_COLOR
|
||||
bottom_color: unsafe { gttmm.mmio(0x70034 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PIPE_MISC
|
||||
misc: unsafe { gttmm.mmio(0x70030 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PIPE_SRCSZ
|
||||
srcsz: unsafe { gttmm.mmio(0x6001C + i * 0x1000)? },
|
||||
})
|
||||
}
|
||||
Ok(pipes)
|
||||
}
|
||||
|
||||
pub fn tigerlake(gttmm: &MmioRegion) -> Result<Vec<Self>> {
|
||||
let mut pipes = Vec::with_capacity(4);
|
||||
for (i, name) in ["A", "B", "C", "D"].iter().enumerate() {
|
||||
let mut planes = Vec::new();
|
||||
//TODO: cursor plane
|
||||
for (j, name) in ["1", "2", "3", "4", "5", "6", "7"].iter().enumerate() {
|
||||
planes.push(Plane {
|
||||
name,
|
||||
index: j,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_BUF_CFG
|
||||
buf_cfg: unsafe { gttmm.mmio(0x7027C + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_COLOR_CTL
|
||||
color_ctl: Some(unsafe { gttmm.mmio(0x701CC + i * 0x1000 + j * 0x100)? }),
|
||||
color_ctl_gamma_disable: 1 << 13,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_CTL
|
||||
ctl: unsafe { gttmm.mmio(0x70180 + i * 0x1000 + j * 0x100)? },
|
||||
ctl_source_rgb_8888: 0b01000 << 23,
|
||||
ctl_source_mask: 0b11111 << 23,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_OFFSET
|
||||
offset: unsafe { gttmm.mmio(0x701A4 + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_POS
|
||||
pos: unsafe { gttmm.mmio(0x7018C + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_SIZE
|
||||
size: unsafe { gttmm.mmio(0x70190 + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_STRIDE
|
||||
stride: unsafe { gttmm.mmio(0x70188 + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_SURF
|
||||
surf: unsafe { gttmm.mmio(0x7019C + i * 0x1000 + j * 0x100)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PLANE_WM
|
||||
wm: [
|
||||
unsafe { gttmm.mmio(0x70240 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70244 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70248 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x7024C + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70250 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70254 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x70258 + i * 0x1000 + j * 0x100)? },
|
||||
unsafe { gttmm.mmio(0x7025C + i * 0x1000 + j * 0x100)? },
|
||||
],
|
||||
wm_trans: unsafe { gttmm.mmio(0x70268 + i * 0x1000 + j * 0x100)? },
|
||||
});
|
||||
}
|
||||
pipes.push(Pipe {
|
||||
name,
|
||||
index: i,
|
||||
planes,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PIPE_BOTTOM_COLOR
|
||||
bottom_color: unsafe { gttmm.mmio(0x70034 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PIPE_MISC
|
||||
misc: unsafe { gttmm.mmio(0x70030 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PIPE_SRCSZ
|
||||
srcsz: unsafe { gttmm.mmio(0x6001C + i * 0x1000)? },
|
||||
})
|
||||
}
|
||||
Ok(pipes)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
use common::{io::{Io, MmioPtr}, timeout::Timeout};
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use super::MmioRegion;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PowerWell {
|
||||
pub name: &'static str,
|
||||
pub depends: &'static [&'static str],
|
||||
pub ddis: &'static [&'static str],
|
||||
pub pipes: &'static [&'static str],
|
||||
pub transcoders: &'static [&'static str],
|
||||
pub request: u32,
|
||||
pub state: u32,
|
||||
pub fuse_status: u32,
|
||||
}
|
||||
|
||||
pub struct PowerWells {
|
||||
pub ctl: MmioPtr<u32>,
|
||||
pub ctl_aux: MmioPtr<u32>,
|
||||
pub ctl_ddi: MmioPtr<u32>,
|
||||
pub fuse_status: MmioPtr<u32>,
|
||||
pub fuse_status_pg0: u32,
|
||||
pub wells: Vec<PowerWell>,
|
||||
}
|
||||
|
||||
impl PowerWells {
|
||||
//TODO: return guard?
|
||||
pub fn enable_well(&mut self, name: &'static str) -> Result<()> {
|
||||
// Wait 20us for distribution of PG0
|
||||
{
|
||||
let timeout = Timeout::from_micros(20);
|
||||
while !self.fuse_status.readf(self.fuse_status_pg0) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::warn!("timeout on distribution of power well 0");
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
// self.wells iter copied to allow mutable self.enable_well later
|
||||
for well in self.wells.iter().copied() {
|
||||
if well.name == name {
|
||||
// Enable dependent wells
|
||||
for depend in well.depends.iter() {
|
||||
self.enable_well(depend)?;
|
||||
}
|
||||
|
||||
if !self.ctl.readf(well.request) {
|
||||
log::info!("enabling power well {}", well.name);
|
||||
}
|
||||
|
||||
// Set request bit
|
||||
self.ctl.writef(well.request, true);
|
||||
|
||||
// Wait 100us for enabled state
|
||||
{
|
||||
let timeout = Timeout::from_micros(100);
|
||||
while !self.ctl.readf(well.state) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::warn!("timeout enabling power well {}", well.name);
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait 20us for distribution
|
||||
{
|
||||
let timeout = Timeout::from_micros(20);
|
||||
while !self.fuse_status.readf(well.fuse_status) {
|
||||
timeout.run().map_err(|()| {
|
||||
log::warn!("timeout on distribution of power well {}", well.name);
|
||||
Error::new(EIO)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
log::warn!("power well {} not found", name);
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
|
||||
pub fn enable_well_by_ddi(&mut self, name: &'static str) -> Result<()> {
|
||||
for well in self.wells.iter() {
|
||||
if well.ddis.contains(&name) {
|
||||
return self.enable_well(well.name);
|
||||
}
|
||||
}
|
||||
log::warn!("power well for DDI {} not found", name);
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
|
||||
pub fn enable_well_by_pipe(&mut self, name: &'static str) -> Result<()> {
|
||||
for well in self.wells.iter() {
|
||||
if well.pipes.contains(&name) {
|
||||
return self.enable_well(well.name);
|
||||
}
|
||||
}
|
||||
log::warn!("power well for pipe {} not found", name);
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
|
||||
pub fn enable_well_by_transcoder(&mut self, name: &'static str) -> Result<()> {
|
||||
for well in self.wells.iter() {
|
||||
if well.transcoders.contains(&name) {
|
||||
return self.enable_well(well.name);
|
||||
}
|
||||
}
|
||||
log::warn!("power well for transcoder {} not found", name);
|
||||
Err(Error::new(EIO))
|
||||
}
|
||||
|
||||
pub fn kabylake(gttmm: &MmioRegion) -> Result<Self> {
|
||||
// IHD-OS-KBL-Vol 2c-1.17 PWR_WELL_CTL
|
||||
let ctl = unsafe { gttmm.mmio(0x45404)? };
|
||||
// Hack since these power ctl registers are combined
|
||||
let ctl_aux = unsafe { gttmm.mmio(0x45404)? };
|
||||
let ctl_ddi = unsafe { gttmm.mmio(0x45404)? };
|
||||
// IHD-OS-KBL-Vol 2c-1.17 FUSE_STATUS
|
||||
let fuse_status = unsafe { gttmm.mmio(0x42000)? };
|
||||
let fuse_status_pg0 = 1 << 27;
|
||||
let wells = vec![
|
||||
PowerWell {
|
||||
name: "1",
|
||||
depends: &[],
|
||||
ddis: &["A"],
|
||||
pipes: &["A"],
|
||||
transcoders: &["EDP"],
|
||||
request: 1 << 29,
|
||||
state: 1 << 28,
|
||||
fuse_status: 1 << 26,
|
||||
},
|
||||
PowerWell {
|
||||
name: "2",
|
||||
depends: &["1"],
|
||||
ddis: &["B", "C", "D", "E"],
|
||||
pipes: &["B", "C"],
|
||||
transcoders: &["A", "B", "C"],
|
||||
request: 1 << 31,
|
||||
state: 1 << 30,
|
||||
fuse_status: 1 << 25,
|
||||
},
|
||||
];
|
||||
Ok(Self {
|
||||
ctl,
|
||||
ctl_aux,
|
||||
ctl_ddi,
|
||||
fuse_status,
|
||||
fuse_status_pg0,
|
||||
wells,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tigerlake(gttmm: &MmioRegion) -> Result<Self> {
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL
|
||||
let ctl = unsafe { gttmm.mmio(0x45404)? };
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_AUX
|
||||
let ctl_aux = unsafe { gttmm.mmio(0x45444)? };
|
||||
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_DDI
|
||||
let ctl_ddi = unsafe { gttmm.mmio(0x45454)? };
|
||||
// IHD-OS-TGL-Vol 2c-12.21 FUSE_STATUS
|
||||
let fuse_status = unsafe { gttmm.mmio(0x42000)? };
|
||||
let fuse_status_pg0 = 1 << 27;
|
||||
let wells = vec![
|
||||
// DBUF functionality, Pipe A, Transcoder A and DSI, DDI A-C, FBC, DSS
|
||||
PowerWell {
|
||||
name: "1",
|
||||
depends: &[],
|
||||
ddis: &["A", "B", "C"],
|
||||
pipes: &["A"],
|
||||
transcoders: &["A"],
|
||||
request: 1 << 1,
|
||||
state: 1 << 0,
|
||||
fuse_status: 1 << 26,
|
||||
},
|
||||
// VDSC for pipe A
|
||||
PowerWell {
|
||||
name: "2",
|
||||
depends: &["1"],
|
||||
ddis: &[],
|
||||
pipes: &[],
|
||||
transcoders: &[],
|
||||
request: 1 << 3,
|
||||
state: 1 << 2,
|
||||
fuse_status: 1 << 25,
|
||||
},
|
||||
// Pipe B, Audio, Transcoder WD, VGA, Transcoder B, DDI USBC1-6, KVMR
|
||||
PowerWell {
|
||||
name: "3",
|
||||
depends: &["2"],
|
||||
ddis: &["USBC1", "USBC2", "USBC3", "USBC4", "USBC5", "USBC6"],
|
||||
pipes: &["B"],
|
||||
transcoders: &["B"],
|
||||
request: 1 << 5,
|
||||
state: 1 << 4,
|
||||
fuse_status: 1 << 24,
|
||||
},
|
||||
// Pipe C, Transcoder C
|
||||
PowerWell {
|
||||
name: "4",
|
||||
depends: &["3"],
|
||||
ddis: &[],
|
||||
pipes: &["C"],
|
||||
transcoders: &["C"],
|
||||
request: 1 << 7,
|
||||
state: 1 << 6,
|
||||
fuse_status: 1 << 23,
|
||||
},
|
||||
// Pipe D, Transcoder D
|
||||
PowerWell {
|
||||
name: "5",
|
||||
depends: &["4"],
|
||||
ddis: &[],
|
||||
pipes: &["D"],
|
||||
transcoders: &["D"],
|
||||
request: 1 << 9,
|
||||
state: 1 << 8,
|
||||
fuse_status: 1 << 22,
|
||||
},
|
||||
];
|
||||
Ok(Self {
|
||||
ctl,
|
||||
ctl_aux,
|
||||
ctl_ddi,
|
||||
fuse_status,
|
||||
fuse_status_pg0,
|
||||
wells,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn alchemist(gttmm: &MmioRegion) -> Result<Self> {
|
||||
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL
|
||||
let ctl = unsafe { gttmm.mmio(0x45404)? };
|
||||
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_AUX
|
||||
let ctl_aux = unsafe { gttmm.mmio(0x45444)? };
|
||||
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_DDI
|
||||
let ctl_ddi = unsafe { gttmm.mmio(0x45454)? };
|
||||
// IHD-OS-ACM-Vol 2c-3.23 FUSE_STATUS
|
||||
let fuse_status = unsafe { gttmm.mmio(0x42000)? };
|
||||
let fuse_status_pg0 = 1 << 27;
|
||||
let wells = vec![
|
||||
// DBUF functionality, Transcoder A, DDI A-B
|
||||
PowerWell {
|
||||
name: "1",
|
||||
depends: &[],
|
||||
ddis: &["A", "B"],
|
||||
pipes: &[],
|
||||
transcoders: &["A"],
|
||||
request: 1 << 1,
|
||||
state: 1 << 0,
|
||||
fuse_status: 1 >> 26,
|
||||
},
|
||||
// Audio playback, Transcoder WD, VGA, DDI C-E, Type-C, KVMR
|
||||
PowerWell {
|
||||
name: "2",
|
||||
depends: &["1"],
|
||||
ddis: &["C", "D", "E", "USBC1", "USBC2", "USBC3", "USBC4"],
|
||||
pipes: &[],
|
||||
transcoders: &[],
|
||||
request: 1 << 3,
|
||||
state: 1 << 2,
|
||||
fuse_status: 1 << 25,
|
||||
},
|
||||
// Pipe A, FBC
|
||||
PowerWell {
|
||||
name: "A",
|
||||
depends: &["1"],
|
||||
ddis: &[],
|
||||
pipes: &["A"],
|
||||
transcoders: &[],
|
||||
request: 1 << 11,
|
||||
state: 1 << 10,
|
||||
fuse_status: 1 << 21,
|
||||
},
|
||||
// Pipe B, Transcoder B
|
||||
PowerWell {
|
||||
name: "B",
|
||||
depends: &["2"],
|
||||
ddis: &[],
|
||||
pipes: &["B"],
|
||||
transcoders: &["B"],
|
||||
request: 1 << 13,
|
||||
state: 1 << 12,
|
||||
fuse_status: 1 << 20,
|
||||
},
|
||||
// Pipe C, Transcoder C
|
||||
PowerWell {
|
||||
name: "C",
|
||||
depends: &["2"],
|
||||
ddis: &[],
|
||||
pipes: &["C"],
|
||||
transcoders: &["C"],
|
||||
request: 1 << 15,
|
||||
state: 1 << 14,
|
||||
fuse_status: 1 << 19,
|
||||
},
|
||||
// Pipe D, Transcoder D
|
||||
PowerWell {
|
||||
name: "D",
|
||||
depends: &["2"],
|
||||
ddis: &[],
|
||||
pipes: &["D"],
|
||||
transcoders: &["D"],
|
||||
request: 1 << 17,
|
||||
state: 1 << 16,
|
||||
fuse_status: 1 << 18,
|
||||
},
|
||||
];
|
||||
Ok(Self {
|
||||
ctl,
|
||||
ctl_aux,
|
||||
ctl_ddi,
|
||||
fuse_status,
|
||||
fuse_status_pg0,
|
||||
wells,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
//TODO: this is copied from vesad and should be adapted
|
||||
|
||||
use std::alloc::{self, Layout};
|
||||
use std::convert::TryInto;
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
use driver_graphics::{CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter};
|
||||
use graphics_ipc::v1::Damage;
|
||||
use graphics_ipc::v2::ipc::{DRM_CAP_DUMB_BUFFER, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT};
|
||||
use syscall::{error::EINVAL, PAGE_SIZE};
|
||||
|
||||
use super::Device;
|
||||
|
||||
//TODO: use hardware cursor
|
||||
pub enum Cursor {}
|
||||
|
||||
impl CursorFramebuffer for Cursor {}
|
||||
|
||||
impl GraphicsAdapter for Device {
|
||||
type Framebuffer = DumbFb;
|
||||
type Cursor = Cursor;
|
||||
|
||||
fn name(&self) -> &'static [u8] {
|
||||
b"ihdgd"
|
||||
}
|
||||
|
||||
fn desc(&self) -> &'static [u8] {
|
||||
b"Intel HD Graphics"
|
||||
}
|
||||
|
||||
fn get_cap(&self, cap: u32) -> syscall::Result<u64> {
|
||||
match cap {
|
||||
DRM_CAP_DUMB_BUFFER => Ok(1),
|
||||
_ => Err(syscall::Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_client_cap(&self, cap: u32, _value: u64) -> syscall::Result<()> {
|
||||
match cap {
|
||||
// FIXME hide cursor plane unless this client cap is set
|
||||
DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT => Ok(()),
|
||||
_ => Err(syscall::Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn display_count(&self) -> usize {
|
||||
self.framebuffers.len()
|
||||
}
|
||||
|
||||
fn display_size(&self, display_id: usize) -> (u32, u32) {
|
||||
(
|
||||
self.framebuffers[display_id].width as u32,
|
||||
self.framebuffers[display_id].height as u32,
|
||||
)
|
||||
}
|
||||
|
||||
fn create_dumb_framebuffer(&mut self, width: u32, height: u32) -> Self::Framebuffer {
|
||||
DumbFb::new(width as usize, height as usize)
|
||||
}
|
||||
|
||||
fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8 {
|
||||
framebuffer.ptr.as_ptr().cast::<u8>()
|
||||
}
|
||||
|
||||
fn update_plane(&mut self, display_id: usize, framebuffer: &Self::Framebuffer, damage: Damage) {
|
||||
framebuffer.sync(&mut self.framebuffers[display_id], damage)
|
||||
}
|
||||
|
||||
fn supports_hw_cursor(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn create_cursor_framebuffer(&mut self) -> Self::Cursor {
|
||||
unimplemented!("ihdgd does not support this function");
|
||||
}
|
||||
|
||||
fn map_cursor_framebuffer(&mut self, _cursor: &Self::Cursor) -> *mut u8 {
|
||||
unimplemented!("ihdgd does not support this function");
|
||||
}
|
||||
|
||||
fn handle_cursor(&mut self, _cursor: &CursorPlane<Self::Cursor>, _dirty_fb: bool) {
|
||||
unimplemented!("ihdgd does not support this function");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceFb {
|
||||
pub onscreen: *mut [u32],
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub stride: usize,
|
||||
}
|
||||
|
||||
impl DeviceFb {
|
||||
pub unsafe fn new(virt: *mut u32, width: usize, height: usize, stride: usize, clear: bool) -> Self {
|
||||
let onscreen = ptr::slice_from_raw_parts_mut(virt, stride * height);
|
||||
if clear {
|
||||
(&mut *onscreen).fill(0);
|
||||
}
|
||||
Self {
|
||||
onscreen,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DumbFb {
|
||||
width: usize,
|
||||
height: usize,
|
||||
ptr: NonNull<[u32]>,
|
||||
}
|
||||
|
||||
impl DumbFb {
|
||||
fn new(width: usize, height: usize) -> DumbFb {
|
||||
let len = width * height;
|
||||
let layout = Self::layout(len);
|
||||
let ptr = unsafe { alloc::alloc_zeroed(layout) };
|
||||
let ptr = ptr::slice_from_raw_parts_mut(ptr.cast(), len);
|
||||
let ptr = NonNull::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout));
|
||||
|
||||
DumbFb { width, height, ptr }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn layout(len: usize) -> Layout {
|
||||
// optimizes to an integer mul
|
||||
Layout::array::<u32>(len)
|
||||
.unwrap()
|
||||
.align_to(PAGE_SIZE)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DumbFb {
|
||||
fn drop(&mut self) {
|
||||
let layout = Self::layout(self.ptr.len());
|
||||
unsafe { alloc::dealloc(self.ptr.as_ptr().cast(), layout) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Framebuffer for DumbFb {
|
||||
fn width(&self) -> u32 {
|
||||
self.width as u32
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.height as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl DumbFb {
|
||||
fn sync(&self, framebuffer: &mut DeviceFb, sync_rect: Damage) {
|
||||
let sync_rect = sync_rect.clip(
|
||||
self.width.try_into().unwrap(),
|
||||
self.height.try_into().unwrap(),
|
||||
);
|
||||
|
||||
let start_x: usize = sync_rect.x.try_into().unwrap();
|
||||
let start_y: usize = sync_rect.y.try_into().unwrap();
|
||||
let w: usize = sync_rect.width.try_into().unwrap();
|
||||
let h: usize = sync_rect.height.try_into().unwrap();
|
||||
|
||||
let offscreen_ptr = self.ptr.as_ptr() as *mut u32;
|
||||
let onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable
|
||||
|
||||
for row in start_y..start_y + h {
|
||||
unsafe {
|
||||
ptr::copy(
|
||||
offscreen_ptr.add(row * self.width + start_x),
|
||||
onscreen_ptr.add(row * framebuffer.stride + start_x),
|
||||
w,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
use common::io::{Io, MmioPtr};
|
||||
use syscall::error::Result;
|
||||
|
||||
use super::{MmioRegion, Pipe};
|
||||
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_CONF
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_CONF
|
||||
pub const TRANS_CONF_ENABLE: u32 = 1 << 31;
|
||||
pub const TRANS_CONF_STATE: u32 = 1 << 30;
|
||||
pub const TRANS_CONF_MODE_MASK: u32 = 0b11 << 21;
|
||||
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_DDI_FUNC_CTL
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_DDI_FUNC_CTL
|
||||
pub const TRANS_DDI_FUNC_CTL_ENABLE: u32 = 1 << 31;
|
||||
pub const TRANS_DDI_FUNC_CTL_MODE_HDMI: u32 = 0b000 << 24;
|
||||
pub const TRANS_DDI_FUNC_CTL_MODE_DVI: u32 = 0b001 << 24;
|
||||
pub const TRANS_DDI_FUNC_CTL_MODE_DP_SST: u32 = 0b010 << 24;
|
||||
pub const TRANS_DDI_FUNC_CTL_MODE_DP_MST: u32 = 0b011 << 24;
|
||||
pub const TRANS_DDI_FUNC_CTL_BPC_8: u32 = 0b000 << 20;
|
||||
pub const TRANS_DDI_FUNC_CTL_BPC_10: u32 = 0b001 << 20;
|
||||
pub const TRANS_DDI_FUNC_CTL_BPC_6: u32 = 0b010 << 20;
|
||||
pub const TRANS_DDI_FUNC_CTL_BPC_12: u32 = 0b011 << 20;
|
||||
pub const TRANS_DDI_FUNC_CTL_SYNC_POLARITY_HSHIGH: u32 = 0b01 << 16;
|
||||
pub const TRANS_DDI_FUNC_CTL_SYNC_POLARITY_VSHIGH: u32 = 0b10 << 16;
|
||||
pub const TRANS_DDI_FUNC_CTL_DSI_INPUT_PIPE_SHIFT: u32 = 12;
|
||||
pub const TRANS_DDI_FUNC_CTL_PORT_WIDTH_1: u32 = 0b000 << 1;
|
||||
pub const TRANS_DDI_FUNC_CTL_PORT_WIDTH_2: u32 = 0b001 << 1;
|
||||
pub const TRANS_DDI_FUNC_CTL_PORT_WIDTH_3: u32 = 0b010 << 1;
|
||||
pub const TRANS_DDI_FUNC_CTL_PORT_WIDTH_4: u32 = 0b011 << 1;
|
||||
|
||||
pub struct Transcoder {
|
||||
pub name: &'static str,
|
||||
pub index: usize,
|
||||
pub clk_sel: MmioPtr<u32>,
|
||||
pub clk_sel_shift: u32,
|
||||
pub conf: MmioPtr<u32>,
|
||||
pub ddi_func_ctl: MmioPtr<u32>,
|
||||
pub ddi_func_ctl_ddi_shift: u32,
|
||||
pub ddi_func_ctl_hdmi_scrambling: u32,
|
||||
pub ddi_func_ctl_high_tmds_char_rate: u32,
|
||||
pub ddi_func_ctl2: Option<MmioPtr<u32>>,
|
||||
pub hblank: MmioPtr<u32>,
|
||||
pub hsync: MmioPtr<u32>,
|
||||
pub htotal: MmioPtr<u32>,
|
||||
pub msa_misc: MmioPtr<u32>,
|
||||
pub mult: MmioPtr<u32>,
|
||||
pub push: Option<MmioPtr<u32>>,
|
||||
pub space: MmioPtr<u32>,
|
||||
pub stereo3d_ctl: MmioPtr<u32>,
|
||||
pub vblank: MmioPtr<u32>,
|
||||
pub vrr_ctl: Option<MmioPtr<u32>>,
|
||||
pub vrr_flipline: Option<MmioPtr<u32>>,
|
||||
pub vrr_status: Option<MmioPtr<u32>>,
|
||||
pub vrr_status2: Option<MmioPtr<u32>>,
|
||||
pub vrr_vmax: Option<MmioPtr<u32>>,
|
||||
pub vrr_vmaxshift: Option<MmioPtr<u32>>,
|
||||
pub vrr_vmin: Option<MmioPtr<u32>>,
|
||||
pub vrr_vtotal_prev: Option<MmioPtr<u32>>,
|
||||
pub vsync: MmioPtr<u32>,
|
||||
pub vsyncshift: MmioPtr<u32>,
|
||||
pub vtotal: MmioPtr<u32>,
|
||||
}
|
||||
|
||||
impl Transcoder {
|
||||
pub fn dump(&self) {
|
||||
eprint!("Transcoder {} {}", self.name, self.index);
|
||||
eprint!(" clk_sel {:08X}", self.clk_sel.read());
|
||||
eprint!(" conf {:08X}", self.conf.read());
|
||||
eprint!(" ddi_func_ctl {:08X}", self.ddi_func_ctl.read());
|
||||
if let Some(reg) = &self.ddi_func_ctl2 {
|
||||
eprint!(" ddi_func_ctl2 {:08X}", reg.read());
|
||||
}
|
||||
eprint!(" hblank {:08X}", self.hblank.read());
|
||||
eprint!(" hsync {:08X}", self.hsync.read());
|
||||
eprint!(" htotal {:08X}", self.htotal.read());
|
||||
eprint!(" msa_misc {:08X}", self.msa_misc.read());
|
||||
eprint!(" mult {:08X}", self.mult.read());
|
||||
if let Some(reg) = &self.push {
|
||||
eprint!(" push {:08X}", reg.read());
|
||||
}
|
||||
eprint!(" space {:08X}", self.space.read());
|
||||
eprint!(" stereo3d_ctl {:08X}", self.stereo3d_ctl.read());
|
||||
eprint!(" vblank {:08X}", self.vblank.read());
|
||||
if let Some(reg) = &self.vrr_ctl {
|
||||
eprint!(" vrr_ctl {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_flipline {
|
||||
eprint!(" vrr_flipline {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_status {
|
||||
eprint!(" vrr_status {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_status2 {
|
||||
eprint!(" vrr_status2 {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_vmax {
|
||||
eprint!(" vrr_vmax {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_vmaxshift {
|
||||
eprint!(" vrr_vmaxshift {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_vmin {
|
||||
eprint!(" vrr_vmin {:08X}", reg.read());
|
||||
}
|
||||
if let Some(reg) = &self.vrr_vtotal_prev {
|
||||
eprint!(" vrr_vtotal_prev {:08X}", reg.read());
|
||||
}
|
||||
eprint!(" vsync {:08X}", self.vsync.read());
|
||||
eprint!(" vsyncshift {:08X}", self.vsyncshift.read());
|
||||
eprint!(" vtotal {:08X}", self.vtotal.read());
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
pub fn modeset(&mut self, pipe: &mut Pipe, timing: &edid::DetailedTiming) {
|
||||
let hactive = (timing.horizontal_active_pixels as u32) - 1;
|
||||
let htotal = hactive + (timing.horizontal_blanking_pixels as u32);
|
||||
let hsync_start = hactive + (timing.horizontal_front_porch as u32);
|
||||
let hsync_end = hsync_start + (timing.horizontal_sync_width as u32);
|
||||
let vactive = (timing.vertical_active_lines as u32) - 1;
|
||||
let vtotal = vactive + (timing.vertical_blanking_lines as u32);
|
||||
let vsync_start = vactive + (timing.vertical_front_porch as u32);
|
||||
let vsync_end = vsync_start + (timing.vertical_sync_width as u32);
|
||||
|
||||
// Configure horizontal sync
|
||||
self.htotal.write(hactive | (htotal << 16));
|
||||
self.hblank.write(hactive | (htotal << 16));
|
||||
self.hsync.write(hsync_start | (hsync_end << 16));
|
||||
|
||||
// Configure vertical sync
|
||||
self.vtotal.write(vactive | (vtotal << 16));
|
||||
self.vblank.write(vactive | (vtotal << 16));
|
||||
self.vsync.write(vsync_start | (vsync_end << 16));
|
||||
|
||||
// Configure pipe
|
||||
pipe.srcsz.write(vactive | (hactive << 16));
|
||||
}
|
||||
|
||||
pub fn kabylake(gttmm: &MmioRegion) -> Result<Vec<Self>> {
|
||||
let mut transcoders = Vec::with_capacity(4);
|
||||
//TODO: Transcoder EDP
|
||||
for (i, name) in ["A", "B", "C"].iter().enumerate() {
|
||||
transcoders.push(Transcoder {
|
||||
name,
|
||||
index: i,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_CLK_SEL
|
||||
clk_sel: unsafe { gttmm.mmio(0x46140 + i * 0x4)? },
|
||||
clk_sel_shift: 29,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_CONF
|
||||
conf: unsafe { gttmm.mmio(0x70008 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_DDI_FUNC_CTL
|
||||
ddi_func_ctl: unsafe { gttmm.mmio(0x60400 + i * 0x1000)? },
|
||||
ddi_func_ctl_ddi_shift: 28,
|
||||
// HDMI scrambling not supported on Kaby Lake
|
||||
ddi_func_ctl_hdmi_scrambling: 0,
|
||||
ddi_func_ctl_high_tmds_char_rate: 0,
|
||||
// N/A
|
||||
ddi_func_ctl2: None,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_HBLANK
|
||||
hblank: unsafe { gttmm.mmio(0x60004 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_HSYNC
|
||||
hsync: unsafe { gttmm.mmio(0x60008 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_HTOTAL
|
||||
htotal: unsafe { gttmm.mmio(0x60000 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_MSA_MISC
|
||||
msa_misc: unsafe { gttmm.mmio(0x60410 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_MULT
|
||||
mult: unsafe { gttmm.mmio(0x6002C + i * 0x1000)? },
|
||||
// N/A
|
||||
push: None,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_SPACE
|
||||
space: unsafe { gttmm.mmio(0x60020 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_STEREO3D_CTL
|
||||
stereo3d_ctl: unsafe { gttmm.mmio(0x70020 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_VBLANK
|
||||
vblank: unsafe { gttmm.mmio(0x60010 + i * 0x1000)? },
|
||||
// N/A
|
||||
vrr_ctl: None,
|
||||
vrr_flipline: None,
|
||||
vrr_status: None,
|
||||
vrr_status2: None,
|
||||
vrr_vmax: None,
|
||||
vrr_vmaxshift: None,
|
||||
vrr_vmin: None,
|
||||
vrr_vtotal_prev: None,
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_VSYNC
|
||||
vsync: unsafe { gttmm.mmio(0x60014 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_VSYNCSHIFT
|
||||
vsyncshift: unsafe { gttmm.mmio(0x60028 + i * 0x1000)? },
|
||||
// IHD-OS-KBL-Vol 2c-1.17 TRANS_VTOTAL
|
||||
vtotal: unsafe { gttmm.mmio(0x6000C + i * 0x1000)? },
|
||||
});
|
||||
}
|
||||
Ok(transcoders)
|
||||
}
|
||||
|
||||
pub fn tigerlake(gttmm: &MmioRegion) -> Result<Vec<Self>> {
|
||||
let mut transcoders = Vec::with_capacity(4);
|
||||
for (i, name) in ["A", "B", "C", "D"].iter().enumerate() {
|
||||
transcoders.push(Transcoder {
|
||||
name,
|
||||
index: i,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_CLK_SEL
|
||||
clk_sel: unsafe { gttmm.mmio(0x46140 + i * 0x4)? },
|
||||
clk_sel_shift: 28,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_CONF
|
||||
conf: unsafe { gttmm.mmio(0x70008 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_DDI_FUNC_CTL
|
||||
ddi_func_ctl: unsafe { gttmm.mmio(0x60400 + i * 0x1000)? },
|
||||
ddi_func_ctl_ddi_shift: 27,
|
||||
ddi_func_ctl_hdmi_scrambling: 1 << 0,
|
||||
ddi_func_ctl_high_tmds_char_rate: 1 << 4,
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_DDI_FUNC_CTL2
|
||||
ddi_func_ctl2: Some(unsafe { gttmm.mmio(0x60404 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_HBLANK
|
||||
hblank: unsafe { gttmm.mmio(0x60004 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_HSYNC
|
||||
hsync: unsafe { gttmm.mmio(0x60008 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_HTOTAL
|
||||
htotal: unsafe { gttmm.mmio(0x60000 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_MSA_MISC
|
||||
msa_misc: unsafe { gttmm.mmio(0x60410 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_MULT
|
||||
mult: unsafe { gttmm.mmio(0x6002C + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_PUSH
|
||||
push: Some(unsafe { gttmm.mmio(0x60A70 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_SPACE
|
||||
space: unsafe { gttmm.mmio(0x60020 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_STEREO3D_CTL
|
||||
stereo3d_ctl: unsafe { gttmm.mmio(0x70020 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VBLANK
|
||||
vblank: unsafe { gttmm.mmio(0x60010 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_CTL
|
||||
vrr_ctl: Some(unsafe { gttmm.mmio(0x60420 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_FLIPLINE
|
||||
vrr_flipline: Some(unsafe { gttmm.mmio(0x60438 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_STATUS
|
||||
vrr_status: Some(unsafe { gttmm.mmio(0x6042C + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_STATUS2
|
||||
vrr_status2: Some(unsafe { gttmm.mmio(0x6043C + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_VMAX
|
||||
vrr_vmax: Some(unsafe { gttmm.mmio(0x60424 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_VMAXSHIFT
|
||||
vrr_vmaxshift: Some(unsafe { gttmm.mmio(0x60428 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_VMIN
|
||||
vrr_vmin: Some(unsafe { gttmm.mmio(0x60434 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VRR_VTOTAL_PREV
|
||||
vrr_vtotal_prev: Some(unsafe { gttmm.mmio(0x60480 + i * 0x1000)? }),
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VSYNC
|
||||
vsync: unsafe { gttmm.mmio(0x60014 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VSYNCSHIFT
|
||||
vsyncshift: unsafe { gttmm.mmio(0x60028 + i * 0x1000)? },
|
||||
// IHD-OS-TGL-Vol 2c-12.21 TRANS_VTOTAL
|
||||
vtotal: unsafe { gttmm.mmio(0x6000C + i * 0x1000)? },
|
||||
})
|
||||
}
|
||||
Ok(transcoders)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
use driver_graphics::GraphicsScheme;
|
||||
use event::{user_data, EventQueue};
|
||||
use inputd::DisplayHandle;
|
||||
use pcid_interface::{PciFunctionHandle, irq_helpers::pci_allocate_interrupt_vector};
|
||||
use redox_scheme::{RequestKind, SignalBehavior, Socket};
|
||||
use std::{io::{Read, Write}, os::fd::AsRawFd};
|
||||
|
||||
mod device;
|
||||
use self::device::Device;
|
||||
|
||||
fn main() {
|
||||
pcid_interface::pci_daemon(daemon);
|
||||
}
|
||||
|
||||
fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! {
|
||||
let pci_config = pcid_handle.config();
|
||||
|
||||
let mut name = pci_config.func.name();
|
||||
name.push_str("_ihdg");
|
||||
|
||||
common::setup_logging(
|
||||
"graphics",
|
||||
"pci",
|
||||
&name,
|
||||
log::LevelFilter::Debug, //common::output_level(),
|
||||
common::file_level(),
|
||||
);
|
||||
|
||||
log::info!("IHDG {}", pci_config.func.display());
|
||||
|
||||
let device = Device::new(&pci_config.func).expect("ihdgd: failed to initialize device");
|
||||
|
||||
let irq_file = pci_allocate_interrupt_vector(&mut pcid_handle, "ihdgd");
|
||||
|
||||
let mut inputd_display_handle = DisplayHandle::new(format!("ihdg.{}", name)).unwrap();
|
||||
|
||||
let mut scheme = GraphicsScheme::new(device, format!("display.ihdg.{}", name));
|
||||
|
||||
user_data! {
|
||||
enum Source {
|
||||
Input,
|
||||
Irq,
|
||||
Scheme,
|
||||
}
|
||||
}
|
||||
|
||||
let event_queue: EventQueue<Source> =
|
||||
EventQueue::new().expect("ihdgd: failed to create event queue");
|
||||
event_queue
|
||||
.subscribe(
|
||||
inputd_display_handle.inner().as_raw_fd() as usize,
|
||||
Source::Input,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
event_queue
|
||||
.subscribe(
|
||||
irq_file.irq_handle().as_raw_fd() as usize,
|
||||
Source::Irq,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Source::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
libredox::call::setrens(0, 0).expect("ihdgd: failed to enter null namespace");
|
||||
|
||||
daemon.ready();
|
||||
|
||||
let all = [Source::Input, Source::Irq, Source::Scheme];
|
||||
for event in all
|
||||
.into_iter()
|
||||
.chain(event_queue.map(|e| e.expect("ihdgd: failed to get next event").user_data))
|
||||
{
|
||||
match event {
|
||||
Source::Input => {
|
||||
while let Some(vt_event) = inputd_display_handle
|
||||
.read_vt_event()
|
||||
.expect("ihdgd: failed to read display handle")
|
||||
{
|
||||
scheme.handle_vt_event(vt_event);
|
||||
}
|
||||
}
|
||||
Source::Irq => {
|
||||
let mut irq = [0; 8];
|
||||
irq_file.irq_handle().read(&mut irq).unwrap();
|
||||
if scheme.adapter_mut().handle_irq() {
|
||||
irq_file.irq_handle().write(&mut irq).unwrap();
|
||||
|
||||
scheme.adapter_mut().handle_events();
|
||||
scheme.tick().unwrap();
|
||||
}
|
||||
}
|
||||
Source::Scheme => {
|
||||
scheme
|
||||
.tick()
|
||||
.expect("ihdgd: failed to handle scheme events");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
@@ -229,12 +229,12 @@ impl Disk for AtaDisk {
|
||||
|
||||
if self.lba_48 {
|
||||
// Set high sector count and LBA
|
||||
chan.control.writef(0x80, true);
|
||||
chan.control.write(0x80);
|
||||
chan.sector_count.write((sectors >> 8) as u8);
|
||||
chan.lba_0.write((block >> 24) as u8);
|
||||
chan.lba_1.write((block >> 32) as u8);
|
||||
chan.lba_2.write((block >> 40) as u8);
|
||||
chan.control.writef(0x80, false);
|
||||
chan.control.write(0x00);
|
||||
}
|
||||
|
||||
// Set low sector count and LBA
|
||||
@@ -373,12 +373,12 @@ impl Disk for AtaDisk {
|
||||
|
||||
if self.lba_48 {
|
||||
// Set high sector count and LBA
|
||||
chan.control.writef(0x80, true);
|
||||
chan.control.write(0x80);
|
||||
chan.sector_count.write((sectors >> 8) as u8);
|
||||
chan.lba_0.write((block >> 24) as u8);
|
||||
chan.lba_1.write((block >> 32) as u8);
|
||||
chan.lba_2.write((block >> 40) as u8);
|
||||
chan.control.writef(0x80, false);
|
||||
chan.control.write(0x00);
|
||||
}
|
||||
|
||||
// Set low sector count and LBA
|
||||
|
||||
Reference in New Issue
Block a user