From 1a8432395c1071259649cb27d28ba6530bf34526 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 18 Apr 2023 19:14:31 +0200 Subject: [PATCH] kms: Handle max bpc property --- src/backend/kms/drm_helpers.rs | 44 ++++++++++++++++++++++++++++++++-- src/backend/kms/mod.rs | 12 ++++++++++ src/config/mod.rs | 3 +++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/backend/kms/drm_helpers.rs b/src/backend/kms/drm_helpers.rs index 8eba6bc4..d5115a2d 100644 --- a/src/backend/kms/drm_helpers.rs +++ b/src/backend/kms/drm_helpers.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use anyhow::Result; +use anyhow::{anyhow, Result}; use smithay::reexports::drm::control::{ atomic::AtomicModeReq, connector::{self, State as ConnectorState}, @@ -9,7 +9,7 @@ use smithay::reexports::drm::control::{ property, AtomicCommitFlags, Device as ControlDevice, Mode, ModeFlags, PlaneType, ResourceHandle, }; -use std::collections::HashMap; +use std::{collections::HashMap, ops::Range}; pub fn display_configuration( device: &mut impl ControlDevice, @@ -360,3 +360,43 @@ pub fn set_vrr( Ok(false) } } + +pub fn get_max_bpc( + dev: &impl ControlDevice, + conn: connector::Handle, +) -> Result)>> { + let Some(handle) = get_prop(dev, conn, "max bpc").ok() else { + return Ok(None); + }; + + let info = dev.get_property(handle)?; + let range = match info.value_type() { + property::ValueType::UnsignedRange(x, y) => (x as u32)..(y as u32), + _ => return Err(anyhow!("max bpc has wrong value type")), + }; + + let value = get_property_val(dev, conn, "max bpc").map(|(val_type, val)| { + match val_type.convert_value(val) { + property::Value::UnsignedRange(res) => res as u32, + _ => unreachable!(), + } + })?; + + Ok(Some((value, range))) +} + +pub fn set_max_bpc(dev: &impl ControlDevice, conn: connector::Handle, bpc: u32) -> Result { + let (_, range) = + get_max_bpc(dev, conn)?.ok_or(anyhow!("max bpc does not exist for connector"))?; + dev.set_property( + conn, + get_prop(dev, conn, "max bpc")?, + property::Value::UnsignedRange(bpc.clamp(range.start, range.end) as u64).into(), + ) + .map_err(Into::::into) + .and_then(|_| get_property_val(dev, conn, "max bpc")) + .map(|(val_type, val)| match val_type.convert_value(val) { + property::Value::UnsignedRange(val) => val as u32, + _ => unreachable!(), + }) +} diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index fd5c06f9..5ce5338f 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -826,6 +826,7 @@ impl Device { let crtc_info = drm.get_crtc(crtc)?; let conn_info = drm.get_connector(conn, false)?; let vrr = drm_helpers::set_vrr(drm, crtc, conn, false).unwrap_or(false); + let max_bpc = drm_helpers::get_max_bpc(drm, conn)?.map(|(_val, range)| range.end.min(16)); let interface = drm_helpers::interface_name(drm, conn)?; let edid_info = drm_helpers::edid_info(drm, conn); let mode = crtc_info.mode().unwrap_or_else(|| { @@ -879,6 +880,7 @@ impl Device { mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)), vrr, position, + max_bpc, ..Default::default() }) }); @@ -1268,6 +1270,16 @@ impl KmsState { } else { surface.vrr = drm_helpers::set_vrr(drm, *crtc, conn, output_config.vrr) .unwrap_or(false); + if let Some(bpc) = output_config.max_bpc { + if let Err(err) = drm_helpers::set_max_bpc(drm, conn, bpc) { + warn!( + ?bpc, + ?err, + "Failed to set max_bpc on connector: {}", + output.name() + ); + } + } surface.refresh_rate = drm_helpers::calculate_refresh_rate(*mode); let drm_surface = drm.create_surface(*crtc, *mode, &[conn])?; diff --git a/src/config/mod.rs b/src/config/mod.rs index 9e34d709..25aff4c0 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -99,6 +99,8 @@ pub struct OutputConfig { pub position: (i32, i32), #[serde(default = "default_enabled")] pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub max_bpc: Option, } impl Default for OutputConfig { @@ -110,6 +112,7 @@ impl Default for OutputConfig { transform: Transform::Normal, position: (0, 0), enabled: true, + max_bpc: None, } } }