DDC/CI monitor control on Linux
DDC/CI is a set of protocols for controlling monitor features like brightness, contrast, color temperature, input source, ... over the display cable (VGA, DVI, HDMI, Display port, ...).
The protocol is fairly old (1998) and nowadays most devices support it.
I'm currently using it to:
- Adjust the brightness of my two monitors depending on my room lighting.
- Switch the input source of my monitors for different devices (my laptop, a tower PC and a virtual machine with dedicated GPU). The big advantage here is that you don't need to buy an expensive KVM switch to support 4K displays / high refresh rates.
How
Required packages
You must install the ddcutil
package for your Linux distribution. The official website contains extensive information for troubleshooting.
Kernel modules
Ddcutil connects to your screen over an I2C connection, and requires the i2c-dev
kernel module to be loaded.
You can load the module at runtime using sudo modprobe i2c-dev
.
To make it persistent across reboots, you need to add the module to /etc/modules-load.d/i2c-dev.conf
:
i2c-dev
Once the module is loaded, some files should appear in /dev/i2c-*
.
Allow the user to use DDC
By default the i2c dev files are owned by root, preventing other users to control them. One solution to allow your user to control DDC without using sudo
is to add a custom udev rule:
/etc/udev/rules.d/99-ddcutil-i2c.rules
KERNEL=="i2c-[0-9]*", GROUP="your-user", MODE="0660", PROGRAM="/usr/bin/ddcutil --bus=%n getvcp 0x10"
This rule automatically detects which i2c devices are DDC-capable, and allows members of the group "your-user" to control the file.
You can reload udev rules without rebooting by executing sudo udevadm trigger
If you have multiple users, you can create a new group and add your user to the group:
groupadd ddc
usermod -aG ddc $USER
Note:
ddcutil --bus=%n getvcp 0x10
is used to get the current brightness of the monitor. This only work with the assumption that all monitors supporting DDC/CI control can be queried for their brightness, which is likely to be true for the immense majority of them.
Identify your monitor info
How to address your monitor
The following command queries general information on your connected displays:
ddcutil detect
# You should see entries like:
# Display 1
# I2C bus: /dev/i2c-0
# EDID synopsis:
# Mfg id: DEL
# Model: DELL U2419H
# Serial number: 833L1N6
# Manufacture year: 2018
# EDID version: 1.4
# VCP version: 2.1
There are multiple methods to address your monitor:
- By display number using
--display
- By model name using
--model
- By serial number using
--sn
- By i2c bus ID using
--bus
The bus ID method is way faster than the others, but may be unreliable if your hardware changes often.
Which features can be controlled
The following command queries which display features can be get/set for a given monitor:
ddcutil capabilities --bus=0
# You should see something like:
# MCCS version: 2.1
# Commands:
# Command: 01 (VCP Request)
# Command: 02 (VCP Response)
# Command: 03 (VCP Set)
# Command: 07 (Timing Request)
# Command: 0c (Save Settings)
# Command: e3 (Capabilities Reply)
# Command: f3 (Capabilities Request)
# VCP Features:
# Feature: 10 (Luminosity)
# Feature: 12 (Contrast)
# Feature: 14 (Select color preset)
# Values:
# 04: 5000 K
# 05: 6500 K
# 06: 7500 K
# 08: 9300 K
# 09: 10000 K
# 0b: User 1
# 0c: User 2
# Feature: 16 (Video gain: Red)
# Feature: 18 (Video gain: Green)
# Feature: 1A (Video gain: Blue)
# Feature: 60 (Input Source)
# Values:
# 0f: DisplayPort-1
# 11: HDMI-1
# Feature: AA (Screen Orientation)
# Values:
# 01: 0 degrees
# 02: 90 degrees
# 03: 180 degrees
# 04: 270 degrees
You can get/set those features using:
- Get:
ddcutil --bus=0 getvcp $FEAT_ID
- Set:
ddcutil --bus=0 setvcp $FEAT_ID $VALUE
Script examples
Change brightness
ddc-setbrightness
#!/bin/bash
# Usage: ddc-setbrightness 50
ddcutil --bus=0 setvcp 10 "$1" &
ddcutil --bus=1 setvcp 10 "$1" &
wait
Since DDC commands can be slow to execute (especially without --bus
addressing), it is best to run them in parallel and wait for completion.
Switch input sources
Very useful when you need to change input sources very often, and don't have a dedicated button on the monitor (or for automating it).
ddc-switch-inputs
#!/bin/bash
# Usage: ddc-switch-inputs 1
case "$1" in
1 )
# Config 1: Main PC
OUT=("0x0f" "0x20")
;;
2 )
# Config 2: Virtual machine
OUT=("0x11" "0x21")
;;
* )
echo "Unknown input '$1'"
exit 1
;;
esac
ddcutil --bus=0 setvcp 60 ${OUT[0]} &
ddcutil --bus=1 setvcp 60 ${OUT[1]} &
wait
Reduce eyestrain
ddc-daylight
#!/bin/bash
# Usage: ddc-daylight night
case "$1" in
"day" )
BRIGHTNESS=100
TEMPERATURE=0x09
;;
"evening" | "morning" )
BRIGHTNESS=60
TEMPERATURE=0x06
;;
"night" )
BRIGHTNESS=30
TEMPERATURE=0x04
;;
"dark" )
BRIGHTNESS=0
TEMPERATURE=0x04
;;
* )
echo "Unknown time of day '$1'"
exit 1
;;
esac
ddcutil --bus=0 setvcp 10 $BRIGHTNESS &
ddcutil --bus=1 setvcp 10 $BRIGHTNESS &
ddcutil --bus=0 setvcp 14 $TEMPERATURE &
ddcutil --bus=1 setvcp 14 $TEMPERATURE &
wait
Updates:
2022-01-13: Replaced i2c-[0-9]+
with i2c-[0-9]*
since udev doesn't support the +
extension. Thanks Hendrik W. !