Патчим EDID телевизора из-под Linux
Мой телевизор («старая» модель LG 32LG5000) никогда не работал в Linux. Каждый раз, когда я подключал его через HDMI-порт, ядро сообщало мне ошибку:
kernel: [ 869.677850] [drm:drm_edid_block_valid] *ERROR* EDI has major version 2, instead of 1
и полностью игнорировало его.
Эта проблема меня не сильно-то и беспокоила, т.к. я не часто им пользовался, да и VGA-порт работал, хоть и с максимальным разрешением 1360×768. Однако сегодня, когда я решил посмотреть фильм, качество вывода через VGA было невероятно хреновым, и я решил с этим покончить.
Анализируем EDID
Для тех, кто не знает, EDID — небольшое количество данных, содержащих в себе информацию о возможностях монитора, в основном разрешения и тайминги, которые он поддерживает. Сообщение об ошибке интересно тем, что в нем говорится об EDID 2.0, которого (почти) никогда не существовало. Вот что нам говорит Wikipedia:
Структура EDID имеет версии от v1.0 до v1.4 размером в 128 байт, каждая последующая версия обратно совместима с предыдущей. Структура EDID v2.0 была размером 256 байт, однако позже была объявлена устаревшей и на замену ей пришла v1.3.
Похоже, EDID 2.0 был объявлен устаревшим где-то в 2000. Довольно забавно, что ТВ 2008 года использовал именно эту версию.
После непродолжительного гуглинга, я нашел сообщения об ошибке EDID в этой модели, и что на самом деле это EDID 1.3. Я попробовал обновить прошивку на последнюю версию, однако это никак не помогло. Итак, у меня было два выхода:
- Пропатчить ядро и убрать проверку версии. Это некрасиво, да и придется, как минимум, drm-модуль пересобирать с каждым обновлением ядра.
- Пропатчить EDID и заставить либо ядро, либо ТВ использовать его.
Я жаждил приключений, поэтому выбрал второе. Загружаем модуль i2c-dev получаем прямой i2c доступ к EDID, используем get-edid:
# get-edid > /tmp/edid.bin
This is read-edid version 3.0.1. Prepare for some fun.
Attempting to use i2c interface
No EDID on bus 0
No EDID on bus 1
No EDID on bus 2
No EDID on bus 4
No EDID on bus 5
No EDID on bus 7
2 potential busses found: 3 6
Will scan through until the first EDID is found.
Pass a bus number as an option to this program to go only for that one.
256-byte EDID successfully retrieved from i2c bus 3
Looks like i2c was successful. Have a good day.
Получили EDID из i2c bus 3. Теперь посмотрим, что же там.
- < .
.
.
-
-
,
,
,
,
. ...
+ +
- -
- +
. - -
. - -
. - -
. - -
. - -
. + +
. + +
. + +
. + +
. + +
. + +
. + +
. + +
. + +
+ +
+ +
- -
Убеждаемся, что это правильный EDID, по идентификатору модели. Видим EDID version 2.0, которую сообщает parse-edid, на что собственно ядро и ругается. Также можем видеть, что parse-edid нашел блок с таймингами и разрешениями, которые ТВ поддерживает. Теперь взглянем на RAW-данные:
.
..............
..........
.......
.........
...........
............
. ....
..... ..
.............
...........
....... ...
........ ..
.......... .,
.......... .
...........
............
EDID состоит из 2 секций, каждая размером по 128 байт (мы же помним, что это EDID 1.3, некорректно названый 2.0).
Первая секция основная, а вторая — дополнительная, EIA/CEA-861 extension block, которая содержит информацию о разрешениях и таймингах. Используя статью в Wikipedia понимаем, что байты по смещению 0x12 и 0x13 содержат версию EDID (0x02 и 0x00 в нашем случае), а смещение 0xf7 содержит контрольную сумму (0xf9).
Патчим EDID наживую
По идее, если мы заменим значения по смещению 0x12 и 0x13 на 0x01 и 0x03 соответственно и подправим контрольную сумму, мы получим правильный EDID. Это легко можно сделать в HEX-редакторе; контрольную сумму установим в 0xf7 (сумма всех 128 байт по модулю 256 должна быть равна нулю).
Попробуем распарсить измененный EDID:
$ diff -u <(parse-edid </tmp/edid.bin) <(parse-edid </tmp/edid-fixed.bin)
Checksum Correct
Checksum Correct
@@ -3,7 +3,7 @@
ModelName "32LG5000"
VendorName "GSM"
# Monitor Manufactured week 9 of 2008
- # EDID version 2.0
+ # EDID version 1.3
# Digital Display
DisplaySize 700 390
Gamma 2.20
Теперь, когда у нас есть правильный EDID, нам нужно как-то попробовать записать его в телевизор (да и, похоже, есть люди, у которых это получалось). В Debian есть пакет i2c-tools, в котором есть утилиты i2cget для чтения и i2cset для записи в I²C-устройство. Вспомним, что get-edid читал из шины 3, а EDID находится по адресу 0x50, так что давайте попробуем прочитать наши байты напрямую:
# for byte in 0x12 0x13 0x7f; do i2cget -y 3 0x50 $byte; done
0x02
0x00
0xf9
Получилось! Невзирая на риск брикнуть (и так бесполезный) HDMI-порт, я решил перезаписать байты напрямую.
Внимание! Повторяйте это действие на свой страх и риск и только если понимаете возможные последствия. Вас предупреждали.
# i2cset -y 3 0x50 0x12 0x01
# i2cset -y 3 0x50 0x13 0x03
# i2cset -y 3 0x50 0x7f 0xf7
Никаких ошибок нет. Проверим, получилось ли:
$ xrandr -q
Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 8192 x 8192
eDP1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 294mm x 165mm
1920x1080 60.0*+ 59.9 40.0
1680x1050 60.0 59.9
1600x1024 60.2
1400x1050 60.0
1280x1024 60.0
1440x900 59.9
1280x960 60.0
1360x768 59.8 60.0
1152x864 60.0
1024x768 60.0
800x600 60.3 56.2
640x480 59.9
VGA1 disconnected (normal left inverted right x axis y axis)
HDMI1 connected 1920x1080+0+0 (normal left inverted right x axis y axis) 700mm x 390mm
1920x1080 60.0*+ 60.0 50.0 59.9 30.0 25.0 24.0 30.0 24.0
1920x1080i 60.1 50.0 60.0
1360x768 59.8
1280x768 60.4
1280x720 60.0 50.0 59.9
1024x768 75.1 70.1 60.0
832x624 74.6
800x600 75.0 60.3
720x576 50.0
720x480 60.0 59.9
640x480 75.0 60.0 59.9 59.9
720x400 70.1
DP1 disconnected (normal left inverted right x axis y axis)
Ага! У меня получилось выставить разрешение 1080p через xrandr. Похоже, изменение постоянное, так что не придется каждый раз перезаписывать байты при каждом выключении ТВ.
От переводчика: на этом ТВ матрица с разрешением 1366×768, поэтому не совсем понятно, откуда такое желание выставить 1920×1080, оно все равно через скейлер пойдет.
http://habrahabr.ru/post/217583/
|