Wednesday, November 5, 2014

Creating a Custom EDID for Arcade Monitor

Since I got my arcade cabinet up and running with my J-PAC-connected PC running RetroArch, the last finishing touch I wanted was to make it boot directly to the right resolution in a text-mode console and launch RetroArch in KMS mode, which provides the lowest latency and best experience. This ended up being easier than I expected, but it did require some steps I hadn't messed with in the past.

First thing you'll need is a working modeline. I created an ultra-wide 1920x240/60 modeline using this online calculator. Using a wide resolution like this leverages the natural blurriness of CRTs to hide fractional scaling artifacts on the horizontal axis, while the 240 vertical resolution allows perfect 1:1 scaling on the vertical axis. This provides a beautiful, "pixel-perfect" image for a large variety of games, including my favorites--Capcom's CPS-1/2.

This is the resulting modeline (negative sync options added by me):
Modeline "1920x240@60" 31.96 1920 1952 2072 2104 240 245 248 253 -HSync -VSync
I recommend testing your modeline out in a standard desktop environment using xrandr first, since it's pretty low-stakes. If something messes up, you just reboot and everything goes back to normal.

Once you've verified that your modeline works, you're ready to create your custom EDID. There are several good writeups about the process online, but I found this one most helpful. I won't rehash all the steps here, but you essentially just copy the appropriate files from the kernel tree and modify/rename one of the existing EDID source files (I used 1024x768.S) with the values from your modeline (I named mine 1920x240.S).

Here's the important part (i.e., license boilerplate removed for brevity; it's standard GPL2) from mine:
/* EDID */
#define VERSION 1
#define REVISION 3
/* Display */
#define CLOCK 31960 /* kHz */
#define XPIX 1920
#define YPIX 240
#define XY_RATIO XY_RATIO_4_3
#define XBLANK 184
#define YBLANK 13
#define XOFFSET 32
#define XPULSE 120
#define YOFFSET (63+5)
#define YPULSE (63+3)
#define DPI 72
#define VFREQ 60 /* Hz */
#define TIMING_NAME "Ultrawide"
#define ESTABLISHED_TIMING2_BITS 0x08 /* Bit 3 -> 1024x768 @60 Hz */
#define HSYNC_POL 0
#define VSYNC_POL 0
#define CRC 0xf7
#include "edid.S"
Next, compile your source files, which should leave you with 1920x240.bin and 1920x240.bin.ihex (the *.ihex one is unneeded, AFAICT). Open 1920x240.bin with the edid-decode utility (available from the standard Ubuntu repos) and it should tell you something about the checksum being wrong (assuming you're making your own; mine already has the corrected checksum). Reopen your custom *.S and replace the existing, incorrect checksum where it says "#define CRC [whatever]" with the value it says it should have and then re-compile. It shouldn't complain this time.

Here's my compiled 1920x240.bin EDID, which should work for any standard-res 15 khz arcade monitor.

Once you have your shiny new EDID *.bin file, you'll need to create a new directory in /lib/firmware called 'edid,' which will require elevated privileges:
sudo mkdir /lib/firmware/edid
Then copy your *.bin file into it.

Next, you'll need to create a file named drm-kms-helper.conf, which contains only one line:
options drm_kms_helper edid_firmware=edid/1920x240.bin
and move it into your /etc/modprobe.d/ directory (again, needs elevated privs). Of course, you'll need to replace '1920x240.bin' with whatever you've named yours.

At this point, your custom EDID should be usable by your system, so a reboot will get you the desired resolution. If something goes wrong and you need to revert, just delete /etc/modprobe.d/drm-kms-helper.conf and it will put everything back the way it was.

If--like me--you'd like to go all the way and boot to a command line (instead of a standard GUI environment) that uses the new res, you'll want to edit your /etc/default/grub (needs elevated privs again) and replace the line:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
with
GRUB_CMDLINE_LINUX_DEFAULT="text"
Then run 'sudo update-grub' to put it into effect. Now, on subsequent reboots, it won't try to load an X-Server and desktop environment and will instead go straight to console.

Finally, I don't plan on having a keyboard connected to my cabinet all the time (kinda kills the mood, y'know?), so I wanted it to login to my user account automatically. To do this, edit /etc/init/tty1.conf in a text editor and comment out the last line:
#exec /sbin/getty -8 38400 tty1
and add this below it instead:
exec /bin/login -f username < /dev/tty1 > /dev/tty1 2>&1
Replace 'username' with the name of the user account you want to login automatically. And, if you want to have it load a frontend--like RetroArch--as soon as it finishes logging in, you can add the launch command to the end of that user account's ~/.bashrc file.

26 comments:

Anonymous said...

Will this work for Nvidia hardware?

I'd probably need a RGB->YPbPr transcoder too since my TV doesn't have RGB inputs

Hunter K. said...

Which part? The KMS stuff or the EDID in general?

If you're using the open source Nouveau driver, you should be able to use KMS just fine, though I don't have any experience with it (and your GPU performance will be greatly reduced vs the proprietary driver).

However, even the proprietary driver should respect the custom EDID, whether you use KMS or not. Any well-behaving driver should respect it, in fact, even in Windows, as long as you know how to make your driver see it.

Yes, if you're using a standard TV with component inputs, the VGA/RGB>YPbPr transcoder should get you fixed up, though again I don't have any personal experience with it.

Anonymous said...

How do you handle arcade games that need special modelines to work correctly, such as R-Type that needs 384 x 256 @ 55.018Hz? Forcing such a game to run at 60Hz would cause either stuttery scrolling or make it run at an incorrect speed with vsync on

Hunter K. said...

@Anon
Thankfully, I don't usually play any games that have weird refresh rates. However, RetroArch has an option to increase the amount of audio deviation it will allow such that you can run those games at 60 fps with smooth scrolling and the audio will just be slightly out of pitch.

If you need to keep everything at the original speed (practicing for leaderboards, etc.), GroovyMAME is probably a better option, since it's designed around matching those weird refresh rates while RetroArch is not. Likewise, I think GroovyArcade handles all of the EDID stuff on its own.

Anonymous said...

How many custom modelines can you define like this? On Windows, you can define 4 custom modelines in the EDID.

Hunter K. said...

I'm not sure how many modelines you can setup like this. Based on the structure of the files, I would assume only one per file, but you can probably put a few (at least) files in the directory.

Anonymous said...

Would it be possible to use a shader to convert an image to YPbPr color space, and just wire the PC's RGB output into the a TV's YPbPr Component input? That would eliminate the need for any kind of transcoder or converter.

Hunter K. said...

@Anon
Sorry for the delay replying! While that's a clever idea, unfortunately modern video cards always output in the RGB colorspace no matter what we do to the colors therein. There are also differences in sync that would likely pose problems (YPbPr uses sync on luma while VGA uses separate Hsync and Vsync).

Alberto said...

Hey there Hunter, does this require any particular video card? Also, do you think this possible using YPbPr out on a card? I have an old Nvidia card with Component video out, but don't actually own a CRT TV. I'm not in America and technology isn't as readily available or cheap so, obviously, I'd love to hear if someone has had any previous experience with this.

I love your idea of using 1920x240/60 to cover most horizontal resolutions available in common consoles, but I just don't know if a standard TV would complain about the horizontal res. Perhaps it's just a matter of Horizontal Frequency, but I don't really know enough. Also, one last question, is there anything in particular that I have to set in Retroarch to be able to achieve this? I should disable all post-processing, right?

Anyway, thanks for sharing info on your setup and thanks in advance for reading my questions. Cheers!

Hunter K. said...

@Alberto
This should work on any video card. Whether it works with YPbPr-out I don't actually know. I do know that the svideo-out on one of my cards doesn't respond to any attempts to change its output format. I only have (positive) experience using VGA.

Even if you have your computer send out an appropriate signal as soon as it can, it will still send a potentially-damaging signal at post/boot, so you'll need to put something like a J-PAC between your PC and your TV to filter out those bad signals or else get a monitor that won't get damaged by them, such as a CRT computer monitor. Or else just make sure you don't turn it on until that part of the process is over and it's fully booted.

The horizontal resolution doesn't matter to CRTs as long as the horizontal frequency is ~15.7 khz. As for setting up RetroArch, I use integer scaling, nearest neighbor filtering (that is, not linear filtering) and the first time you launch at the wide resolution, it will squished into a small area in the center of the screen, so you'll need to go into the settings > video settings and put the horizontal scale at 6x (I think; 5 or 6).

Alberto said...

Hello again,

So I built me a PC and installed Linux with KMS. I built an adapter to plug a videocard to an RGB Monitor (I got myself a Sony PVM) and everything is working. Except that I have horrible overscan issue on the resolution you propose. I configured everything as I should. I even modded the bios of my vga so that it would output 15.7Khz during boot. As I said, everything is working fine in 320x240 and 640x480 interlaced.

Do you have any suggestions? Did you have issues with overscan? I've tried building new modes myself, but I haven't managed to fix the overscan by tweaking the EDID timings. I grasping at straws here :(.

Thanks for the help so far and for the article.

Hunter K. said...

@alberto
Hey, sounds like you've made great progress! No overscan issues here. Is the overscan all around the edges or just on the sides? I believe the PVMs have an overscan/underscan toggle. Have you tried messing with that?

I did have to tweak some of my emulation cores, with either 'crop overscan' enabled or tweaks to the core options to only show the ~224 px image instead of the full-frame 240-px-with-borders to get them to fit on the screen properly.

Alberto said...

Ok, so after messing around a lot with the the mode by building new EDIDs based on the resolution you sent. I built a new one that modifies the front-porch and back-porch timings:

Modeline "1542x240@60" 31.96 1542 1726 1846 2104 240@60 245 248 253 -hsync -vsync

This is essentially the same number of horizontal pixels you sent but the videocard is drawing only the row of around 1542 pixels that are visible. I also modified the vertical timings to make it draw a little further down. I'm leaving this out there in case someone else stumbles on your blog and has similar issues to mine. I used this sites for help (dunno if it's ok to leave links, sorry if it's wrong, I'm not trying to spam):

https://www.mythtv.org/wiki/Working_with_Modelines#Working_with_Modelines_by_Hand
http://www.epanorama.net/faq/vga2rgb/calc_v16.html

I have one last hurdle, though. Retroarch seems to be running at 120 Hz rather than 60 Hz. Even with your edid.bin file it's running at 120 Hz. This make the games run a two times the speed (at least SNES games). The gui show 120 FPS. I haven't been able to use a setting to make it run in sync with the tv, which I know is running at 60 Hz. The logs get filled with a message that says something to the tone of "missed vblank" or something like that. Have you had any issues like this?

PS: Sorry for asking you so many questions. I hope I'm not imposing. Thanks for the help either way, I appreciate the words of encouragement.

Hunter K. said...

No worries at all! This is great info. I'm planning to copy/paste some of it into the post above, actually, as I'm sure others have/will run into the same issues.

For the 120 fps thing, go into RetroArch's settings > frame throttle and set the 'maximum run speed' to 1.0x. That should confine it to 60 fps.

Anonymous said...

@hunterk what's not clear to me, is it possible atm to use an interlacing (or other) shader for a superwide resolution like 3840x480 for 15khz crt tv's in order to interlace 400+ content and scanline progressive content?
or is there another way in retroarch to, for example, switch in-game from 640x480i to 320x240p for 15khz crt's? perhaps by switching from a 640x480 modeline to a 320x240 modeline in-game?
if so, would this also be possible for kms too, so with edids instead of modelines?
all solutions seem to be focused only on 31khz monitors for some reason.

Hunter K. said...

Hi Anon,
All solutions are focused on 31 khz monitors because 15 khz monitors aren't capable of showing 480p content, which is necessary for using the interlacing shader to simulate switching between 480i and 240p. RetroArch may be able to function with 480i if you set a modeline/EDID to do it, I'm not sure. On my 15 khz display, I just don't play any 480i content and stick with "240p" content only.

I would recommend trying it with a modeline first and then following up with an EDID if that works.

Anonymous said...

480i modelines and edid (tried both 2560x480 and 3840x480) work fine with retroarch on a 15khz crt.
was hoping that a shader could also create scanlines for 480i content for 15kh crt's, but from what you say I gather that's not possible. Seems that for 15khz you're either stuck to an interlaced modeline/edid or a progressive modeline/edid for games, unfortunately.

Hunter K. said...

Oh, you've already tried 480i modeline/EDID and it worked? Sweet :D

In that case, we might be able to get something going with a shader. What happens if you apply the interlacing shader at 480i?

Anonymous said...

the screen starts flickering. hard to see if scanlines are applied.

Hunter K. said...

Is that on both 240p content and 480i content? (for testing, I use Super Mario World for 240p and R.P.M. Racing for 480i)

There's a parameter/option in the interlacing shader to change which fields it blanks first (even or odd), so that might help with the flickering.

Theoretically, 240p content should look correct with the interlacing shader at 2x scale, while 480i content will either look correct or flicker, depending on which field is blanked first.

If it's broken, though, hit me up on IRC sometime (#filthypants on Freenode IRC) and we can test some things out.

Anonymous said...

can't get it too work. cg shaders, including interlacing.cg, don't work and always result in immediate black screen, including cgp presets. converting to glsl is a workaround. when using interlacing.glsl with 2x scale there is no flicker on interlaced screen, only on progressive content. when rotating the screen 90degrees, interlaced has a light dark slightly moving overlay, but unevenly sized and distributed scanlines on progressive without flicker.
i saw that the blank field can be controlled as a parameter in cg, but not in glsl? how to do that exactly?

Hunter K. said...

Oh whoops, looks like I didn't port those parameters over from Cg. I can do that for you in the near future (maybe tonight or tomorrow).

Anonymous said...

that would be nice. thanks for all info and interesting blogs.

Hunter K. said...

Alright, the glsl version has parameters now, so you can play around with them to see if you can get it to act any better. Now that I've had longer to think about it, though, I don't actually think there's anything we can do to make it not flicker on non-interlaced stuff. If the modelines already exist, though, you may be able to use config overrides for 480i games that enable the 480i modeline by using video_fullscreen_x and video_fullscreen_y settings. However, this will only work for games that are interlaced all the time, like tekken 3 and R.P.M. Racing and won't do anything to fix interlaced menus with non-interlaced gameplay (which was very common during the PSX/N64 era). :(

Anonymous said...

have tried the updated interlacing.glsl and can now set parameters in retroarch, which actually work. however, it does indeed not solve the flicker on 240p content :(. here's to hoping true auto in-game resolution switching will be implemented in retroarch one day as is the case with groovy mame. thanks for looking into it.

Anonymous said...

Proper 240p timings must have 262 or 263 total lines. You're ten short.

Analytics Tracking Footer