任意の EDID を Linux Kernel に読み込ませる方法


日本語の情報があまり見つからなかったので記載してみました。
以下のようなニッチなユースケースくらいしかないと思いますので、ほぼ自分用の備忘録になるかと。。。

(2022/2/10 追記)
このページは自分の備忘録以外に需要がないと思っていたのですが、
予想外なことに、実開発で困っている方がこのページに辿り着いたらしいということを聞きました。
情報も古くなっていますし、せっかくですので比較的新しい Kernelで再確認しました。

* ディスプレイが部分的に壊れてEDIDが取得できない
* 強制的に任意の解像度を指定したい
* 仕事で開発中の評価ボード等で何か問題があって、ダミーのEDIDを読ませたい

ただし、Kernel v5.1.5 以前の環境では脆弱性が指摘されている（[CVE-2019-12382][1]）ので、
対策されていない環境での利用は推奨しません。

なお、2019/6/27 時点で [Upstream Kernel (Linus のTree)][2] の master ブランチには
対策パッチが入っていませんでしたが、
[DRM Subsystem][3] の drm-next ブランチには[対策パッチ][4]が入っていましたので、
じきにマージされるはずですし、シンプルな修正の為、容易に Cherry-pick できると思います。
**(2022/2/10 追記)**
Linux Kernel v5.10.41 にはマージされていることを確認しました。

##### 確認環境
* Linux Kernel v4.14.75 ltsi
* Linux Kernel v5.10.41 **(2022/2/10 追記)**

# 手順

## 1. menuconfig で CONFIG_DRM_LOAD_EDID_FIRMWARE を有効化してKernel Imageをビルドする
(ビルド手順は[こちら][5])

```shell
$ make menuconfig
```
Linux Kernel v4.14.75 ltsi の場合

```
Symbol: DRM_LOAD_EDID_FIRMWARE [=y]
Type  : boolean
Prompt: Allow to specify an EDID data set instead of probing for it
　
Location:
   -> Device Drivers
      -> Graphics support
         -> Allow to specify an EDID data set instead of probing for it
```
**(2022/2/10 追記)** Linux Kernel v5.10.41 の場合

```
Symbol: DRM_LOAD_EDID_FIRMWARE [=y]
Type  : bool
Prompt: Allow to specify an EDID data set instead of probing for it
　
Location:
   -> Device Drivers
      -> Graphics support
         -> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)
            -> Allow to specify an EDID data set instead of probing for it
```

## 2. Linux Kernel に読み込ませる EDID のバイナリを作成する
CONFIG_DRM_LOAD_EDID_FIRMWARE が有効になると、以下の解像度については
Kernel Image に bulit-in で組み込まれるため、それ以外の解像度を指定したいときに
自分で EDID のバイナリを指定することになります。

* 800x600
* 1024x768
* 1280x1024
* 1600x1200
* 1680x1050
* 1920x1080

作り方については Documentation/EDID/HOWTO.txt もご参照ください。
**(2022/2/10 追記)**
Kernel v5.7以降は Documentation/admin-guide/edid.rst に変更になりました。

```shell
$ cd ${WORK}/${KERNEL_TREE}/Documentation/EDID/
$ cp 1024x768.S WIDTHxHEIGHT.S
$ vi WIDTHxHEIGHT.S
$ make
```
作成したバイナリの中身については以下のコマンドで確認できます。

```shell
$ edid-decode WIDTHxHEIGHT.bin
```

<details><summary>例　(クリックすると展開されます)</summary><div>

```shell
$ edid-decode 1920x1080.bin
1罹2x袰$YJ% PT兩:q8-@X,E・inux #0
    ・=BD
      鑅inux FHD
   Extracted contents:
header:          00 ff ff ff ff ff ff 00
serial number:   31 d8 00 00 00 00 00 00 05 16
version:         01 03
basic params:    6d 32 1c 78 ea
chroma info:     5e c0 a4 59 4a 98 25 20 50 54
established:     00 00 00
standard:        d1 c0 01 01 01 01 01 01 01 01 01 01 01 01 01 01
descriptor 1:    02 3a 80 18 71 38 2d 40 58 2c 45 00 f4 19 11 00 00 1e
descriptor 2:    00 00 00 ff 00 4c 69 6e 75 78 20 23 30 0a 20 20 20 20
descriptor 3:    00 00 00 fd 00 3b 3d 42 44 0f 00 0a 20 20 20 20 20 20
descriptor 4:    00 00 00 fc 00 4c 69 6e 75 78 20 46 48 44 0a 20 20 20
extensions:      00
checksum:        05

Manufacturer: LNX Model 0 Serial Number 0
Made week 5 of 2012
EDID version: 1.3
Analog display, Input voltage level: 0.7/0.7 V
Sync: Separate Composite Serration
Maximum image size: 50 cm x 28 cm
Gamma: 2.20
DPMS levels: Standby Suspend Off
RGB color display
First detailed timing is preferred timing
Established timings supported:
Standard timings supported:
  1920x1080@60Hz
Detailed mode: Clock 148.500 MHz, 500 mm x 281 mm
               1920 2008 2052 2200 hborder 0
               1080 1084 1089 1125 vborder 0
               +hsync +vsync
Serial number: Linux
Monitor ranges (GTF): 59-61Hz V, 66-68kHz H, max dotclock 150MHz
Monitor name: Linux
Checksum: 0x5 (valid)
EDID block does NOT conform to EDID 1.3!
        Detailed block string not properly terminated
```
</div></details>
　

また、PC や 評価ボード側に問題があって EDID が取得できない場合には
別の PC とディスプレイを取得して、 PC がディスプレイから取得した EDID を
そのままバイナリ化しても良いと思います。

```shell
$ find /sys/devices/ -name edid
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card1/card1-HDMI-A-2/edid
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card1/card1-DVI-D-1/edid
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card1/card1-DVI-I-1/edid
/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card1/card1-DP-1/edid
/sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-HDMI-A-1/edid
/sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-VGA-1/edid
$ cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/drm/card1/card1-DP-1/edid > DISPLAY.bin
```

## 3. 作成したバイナリを Rootfs にコピーする
```shell
$ sudo cp WIDTHxHEIGHT.bin ${ROOTFS}/lib/firmaware/edid/WIDTHxHEIGHT.bin
```

## 4. Kernel の bootargs に "drm_kms_helper.edid_firmware" を追加する
例：

```
drm_kms_helper.edid_firmware=edid/WIDTHxHEIGHT.bin
```
前述していますが、以下の指定をした場合には EDID が built-in されていますので、
Rootfs にバイナリが無くても動作します。

* drm_kms_helper.edid_firmware=800x600.bin
* drm_kms_helper.edid_firmware=1024x768.bin
* drm_kms_helper.edid_firmware=1280x1024.bin
* drm_kms_helper.edid_firmware=1600x1200.bin
* drm_kms_helper.edid_firmware=1680x1050.bin
* drm_kms_helper.edid_firmware=1920x1080.bin

**(2022/2/10 追記)**
Kernel v5.10.41 では "drm_kms_helper.edid_firmware" ではなく、"drm.edid_firmware"になるようです。

built-in されるバイナリについては "drivers/gpu/drm/drm_edid_load.c" 内の
"static const char * const generic_edid_name[]" で列挙されています。

# ~~制限事項 ?~~ マルチディスプレイ環境

シングルディスプレイの場合には問題ありませんが、
マルチディスプレイ環境で "drm_kms_helper.edid_firmware" を指定してしまうと
全てのディスプレイが指定した EDID を使用してしまうようで、
１つのディスプレイだけ設定したい、ということが出来ないようです。
~~（仕様なのか私の環境固有の問題なのかは未調査です）~~
**(2022/2/10 追記)**
改めて確認したところ、Kernel の bootargs に "drm_kms_helper.edid_firmware (drm.edid_firmware)" を追加する際に
以下のように"connector"を指定することで、ディスプレイ毎にEDIDを指定できるようです。
Documentation/admin-guide/kernel-parameters.txt に記載がありました。

```
drm.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
```

例 (Linux Kernel v5.10.41 の場合)：

```
drm.edid_firmware=HDMI-A-1:edid/1280x1024.bin,HDMI-A-2:edid/1920x1080.bin
```

[1]:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12382
[2]:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/
[3]:https://cgit.freedesktop.org/drm/drm
[4]:https://cgit.freedesktop.org/drm/drm/commit/drivers/gpu/drm/drm_edid_load.c?id=9f1f1a2dab38d4ce87a13565cf4dc1b73bef3a5f
[5]:https://qiita.com/tkomagata/items/bd18546d9c6efb0bccde