Switching Monitor Inputs With ddcutil

01 Jan 2020
Drew

ddcutil is a program for messing with monitor settings. Brightness and colour levels and all that and – the one I’m interested in – input source. It works by magic. Or, as ddcutil’s docs put it:

ddcutil primarily uses DDC/CI (Display Data Channel Command Interface) to communicate with monitors implementing MCCS (Monitor Control Command Set) over I2C.

Whatever that means.

Obviously ddcutil will only work if the monitor supports that stuff. Reading around suggests that most but not all do.

ddcutil will not work on laptops as they use a different interface.

Its usefulness for me is that I have a Nintendo Switch plugged into my main monitor as well as my PC. When I want to play on my Switch, rather than leaning over my desk and using the super fiddly buttons on the front of my monitor, I can just hit a key combo to switch between the DisplayPort input my PC uses and the HDMI used by my Switch.

The same approach could be applied to VFIO passthrough setups or just, as mentioned above, for adjusting brightness and so on.

Preparation

To use ddcutil the i2c-dev module must be loaded. On some distros it’s built into the kernel, if it’s not it’ll need to be explicitly loaded.

To check whether i2c-dev is built in, run:

grep i2c-dev.ko  /lib/modules/`uname -r`/modules.builtin

If you get output then you’re all good. If not then you need to explicitly load the module by adding the line i2c_dev to /etc/modules or create a file containing that line in /etc/modules-load.d/.

In my case, on Arch, the module was not included in the kernel so I created the file /etc/modules-load.d/i2c-dev.conf containing the single line:

i2c_dev

That’ll only take effect next time you reboot so for now you can load the module directly with:

sudo modprobe i2c_dev

Using ddcutil

Once the i2c_dev module is loaded we can do:

sudo ddcutil detect

To detect monitors. The output in my case is:

Display 1
   I2C bus:             /dev/i2c-5
   EDID synopsis:
      Mfg id:           DEL
      Model:            DELL U2715H
      Serial number:    GH85D67S05JS
      Manufacture year: 2016
      EDID version:     1.4
   VCP version:         2.1

If you’ve got multiple monitors, pay attention to that Display number – you’ll need to use that with subsequent commands. So if you want to operate on display 2, for example, wherever I use ddcutil, you’ll want to use ddcutil -d 2.

Once you’ve settled on a display, run:

sudo ddcutil capabilities

Which will output a load of stuff that ddcutil can mess with on the monitor.

The one I’m interested in is Feature 60 (Input Source) (I believe feature codes are common across devices but don’t take my word for that).

   Feature: 60 (Input Source)
      Values:
         0f: DisplayPort-1
         10: DisplayPort-2
         11: HDMI-1
         12: HDMI-2

There we see feature code 60 with its possible values and what they mean.

We can get the current value of any particular feature by using ddcutil getvcp. Feature codes are hexidecimal and need to be prepended with 0x when used in commands. So to get the current input source we can do:

sudo ddcutil getvcp 0x60

Which outputs:

VCP code 0x60 (Input Source                  ): DisplayPort-2 (sl=0x10)

We can change settings with ddcutil setvcp. So, if I want to switch to HDMI 2, which has the code 12 in the output up above, we can do:

sudo ddcutil setvcp 0x60 0x12

And my monitor will switch over to HDMI input. It takes a couple of seconds but it works. Magic.

So to make this super-useful to me I wrote a little bash script which toggles between DisplayPort 2 (my PC) and HDMI 2 (my Switch) and bound it to a key combo:

#!/usr/bin/env bash
notify-send "Switching..."
if sudo ddcutil getvcp 0x60 | grep "DisplayPort" > /dev/null 2>&1;then
    sudo ddcutil setvcp 0x60 0x12
else
    sudo ddcutil setvcp 0x60 0x10
fi

As you can see, ddcutil requires root permissions to do its thing. You can handle this in whatever way you like in any scripts you make – I just use sudo because I am not clever like you are.