summaryrefslogtreecommitdiff
path: root/src/screen/drm
diff options
context:
space:
mode:
Diffstat (limited to 'src/screen/drm')
-rw-r--r--src/screen/drm/card.zig45
-rw-r--r--src/screen/drm/connector.zig156
-rw-r--r--src/screen/drm/request.zig19
-rw-r--r--src/screen/drm/resources.zig76
4 files changed, 296 insertions, 0 deletions
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);
+ }
+};