From 85bcada8cf78bdf2bfb3be583289686026e0f25e Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Sat, 1 Feb 2025 12:47:35 +0100 Subject: screen: start drm implementation --- src/screen/drm/card.zig | 45 +++++++++++++ src/screen/drm/connector.zig | 156 +++++++++++++++++++++++++++++++++++++++++++ src/screen/drm/request.zig | 19 ++++++ src/screen/drm/resources.zig | 76 +++++++++++++++++++++ 4 files changed, 296 insertions(+) create mode 100644 src/screen/drm/card.zig create mode 100644 src/screen/drm/connector.zig create mode 100644 src/screen/drm/request.zig create mode 100644 src/screen/drm/resources.zig (limited to 'src/screen/drm') diff --git a/src/screen/drm/card.zig b/src/screen/drm/card.zig new file mode 100644 index 0000000..67f81f3 --- /dev/null +++ b/src/screen/drm/card.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const os = std.os.linux; + +const cerror = @import("../cerror.zig"); + +const Resources = @import("resources.zig").Resources; +const Connector = @import("connector.zig").Connector; + + +pub const Card = struct { + const Self = @This(); + + file: std.fs.File, + allocator: std.mem.Allocator, + + pub fn open(name: []const u8, allocator: std.mem.Allocator) !Self { + var dri_dir = try std.fs.openDirAbsolute("/dev/dri", .{}); + defer dri_dir.close(); + + return .{ + .file = try dri_dir.openFile(name, .{ + .mode = .read_write, + .lock_nonblocking = true, + }), + .allocator = allocator, + }; + } + + pub fn close(self: *Card) void { + self.file.close(); + } + + pub fn is_kms(self: *Card) !bool { + const raw_info = Resources.raw_without_ids(self); + return raw_info.count_crtcs > 0 and raw_info.count_connectors > 0 and raw_info.count_encoders > 0; + } + + pub fn resources(self: *Card) !Resources { + return Resources.init(self); + } + + pub fn connector(self: *Card, id: u32) !Connector { + return Connector.init(self, id); + } +}; diff --git a/src/screen/drm/connector.zig b/src/screen/drm/connector.zig new file mode 100644 index 0000000..9d7f9ad --- /dev/null +++ b/src/screen/drm/connector.zig @@ -0,0 +1,156 @@ +const std = @import("std"); +const os = std.os.linux; +const Drm = @import("request.zig").Drm; +const Card = @import("card.zig").Card; + +const RawConnector = extern struct { + encoder_ids: ?*u32, + modes: ?*ModeInfo, + prop_ids: ?*u32, + prop_value_ids: ?*u64, + count_modes: u32, + count_props: u32, + count_encoders: u32, + encoder_id: u32, + connector_id: u32, + connector_type: u32, + connector_type_id: u32, + connection: Connection, + mm_width: u32, + mm_height: u32, + subpixel: u32, + pad: u32, +}; + +const Connection = enum(u32) { + undefined_state = 0, + connected = 1, + disconnected = 2, + unknown = 3, +}; + +const ModeInfo = extern struct { + const Self = @This(); + const Flags = packed struct(u32) { + phsync: bool, + nhsync: bool, + pvsync: bool, + nvsync: bool, + interlace: bool, + dblscan: bool, + csync: bool, + pcsync: bool, + ncsync: bool, + hskew: bool, + bcast: bool, + pixmux: bool, + dblclk: bool, + clkdiv2: bool, + _padding: u18, + }; + + clock: u32, + hdisplay: u16, + hsync_start: u16, + hsync_end: u16, + htotal: u16, + hskew: u16, + vdisplay: u16, + vsync_start: u16, + vsync_end: u16, + vtotal: u16, + vscan: u16, + + vrefresh: u32, + + flags: Flags, + type: u32, + name: [32]u8, + + pub fn frame_rate(self: *const Self) f32 { + var rate = (@as(u64, @intCast(self.clock)) * 1000000 / self.htotal + self.vtotal / 2) / self.vtotal; + + if (self.flags.interlace) { + rate *= 2; + } + + if (self.flags.dblscan) { + rate /= 2; + } + + if (self.vscan > 1) { + rate /= self.vscan; + } + + return @as(f32, @floatFromInt(rate)) / 1000.0; + } +}; + +pub const Connector = struct { + const Self = @This(); + + card: *Card, + + encoder_ids: []u32, + modes: []ModeInfo, + prop_ids: []u32, + prop_value_ids: []u64, + encoder_id: u32, + connector_id: u32, + connector_type: u32, + connector_type_id: u32, + connection: Connection, + mm_width: u32, + mm_height: u32, + subpixel: u32, + pad: u32, + + pub fn raw_without_ids(card: *Card, id: u32) !RawConnector { + var result = std.mem.zeroInit(RawConnector, .{ .connector_id = id }); + try Drm.get_connector.request(card.file.handle, RawConnector, &result); + return result; + } + + // NOTE: This function does not take in account + // that there might be some hot-plugging going + // on. This might have to change in the future. + pub fn init(card: *Card, id: u32) !Self { + var raw = try Self.raw_without_ids(card, id); + const resources = .{ + .encoder_ids = try card.allocator.alloc(u32, raw.count_encoders), + .modes = try card.allocator.alloc(ModeInfo, raw.count_modes), + .prop_ids = try card.allocator.alloc(u32, raw.count_props), + .prop_value_ids = try card.allocator.alloc(u64, raw.count_props), + .encoder_id = raw.encoder_id, + .connector_id = raw.connector_id, + .connector_type = raw.connector_type, + .connector_type_id = raw.connector_type_id, + .connection = raw.connection, + .mm_width = raw.mm_width, + .mm_height = raw.mm_height, + .subpixel = raw.subpixel, + .pad = raw.pad, + .card = card, + }; + + @memset(resources.encoder_ids, 0); + @memset(resources.modes, std.mem.zeroes(ModeInfo)); + @memset(resources.prop_ids, 0); + @memset(resources.prop_value_ids, 0); + + raw.encoder_ids = @ptrCast(resources.encoder_ids); + raw.modes = @ptrCast(resources.modes); + raw.prop_ids = @ptrCast(resources.prop_ids); + raw.prop_value_ids = @ptrCast(resources.prop_value_ids); + + try Drm.get_connector.request(card.file.handle, RawConnector, &raw); + + return resources; + } + + pub fn deinit(self: *Self) void { + self.card.allocator.free(self.encoder_ids); + self.card.allocator.free(self.modes); + self.card.allocator.free(self.prop_ids); + } +}; diff --git a/src/screen/drm/request.zig b/src/screen/drm/request.zig new file mode 100644 index 0000000..bf9701c --- /dev/null +++ b/src/screen/drm/request.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const os = std.os.linux; +const cerror = @import("../cerror.zig"); + +fn ioctl(fd: os.fd_t, request: u32, arg: usize) !void { + try cerror.from_usize(os.ioctl(fd, request, arg)); +} + +pub const Drm = enum(u8) { + const Self = @This(); + + get_resources = 0xA0, + get_connector = 0xA7, + + pub fn request(self: Self, fd: os.fd_t, T: type, arg: *T) !void { + const id = os.IOCTL.IOWR('d', @intFromEnum(self), T); + try ioctl(fd, id, @intFromPtr(arg)); + } +}; diff --git a/src/screen/drm/resources.zig b/src/screen/drm/resources.zig new file mode 100644 index 0000000..6128f37 --- /dev/null +++ b/src/screen/drm/resources.zig @@ -0,0 +1,76 @@ +const std = @import("std"); +const os = std.os.linux; +const cerror = @import("../cerror.zig"); +const Card = @import("card.zig").Card; +const Drm = @import("request.zig").Drm; + +const RawResources = extern struct { + fb_ids: ?*u32, + crtc_ids: ?*u32, + connector_ids: ?*u32, + encoder_ids: ?*u32, + count_fbs: u32, + count_crtcs: u32, + count_connectors: u32, + count_encoders: u32, + min_width: u32, + max_width: u32, + min_height: u32, + max_height: u32, +}; + +pub const Resources = struct { + const Self = @This(); + const Range = struct { min: u32, max: u32 }; + + card: *Card, + + fb_ids: []u32, + crtc_ids: []u32, + connector_ids: []u32, + encoder_ids: []u32, + width: Range, + height: Range, + + pub fn raw_without_ids(card: *Card) !RawResources { + var result = std.mem.zeroes(RawResources); + try Drm.get_resources.request(card.file.handle, RawResources, &result); + return result; + } + + // NOTE: This function does not take in account + // that there might be some hot-plugging going + // on. This might have to change in the future. + pub fn init(card: *Card) !Self { + var raw = try Self.raw_without_ids(card); + const resources = .{ + .fb_ids = try card.allocator.alloc(u32, raw.count_fbs), + .crtc_ids = try card.allocator.alloc(u32, raw.count_crtcs), + .connector_ids = try card.allocator.alloc(u32, raw.count_connectors), + .encoder_ids = try card.allocator.alloc(u32, raw.count_encoders), + .width = .{ .min = raw.min_width, .max = raw.max_width }, + .height = .{ .min = raw.min_height, .max = raw.max_height }, + .card = card, + }; + + @memset(resources.fb_ids, 0); + @memset(resources.crtc_ids, 0); + @memset(resources.connector_ids, 0); + @memset(resources.encoder_ids, 0); + + raw.fb_ids = @ptrCast(resources.fb_ids); + raw.crtc_ids = @ptrCast(resources.crtc_ids); + raw.connector_ids = @ptrCast(resources.connector_ids); + raw.encoder_ids = @ptrCast(resources.encoder_ids); + try Drm.get_resources.request(card.file.handle, RawResources, &raw); + + return resources; + } + + pub fn deinit(self: *Self) void { + self.card.allocator.free(self.fb_ids); + self.card.allocator.free(self.crtc_ids); + self.card.allocator.free(self.connector_ids); + self.card.allocator.free(self.encoder_ids); + } +}; -- cgit v1.2.3-70-g09d2