 if there's some time. So let's start now. This talk is going to be about the anatomy of a KMS driver. So we'll start by quickly explaining what KMS actually is, because most of you probably have at least heard about KMS DRM, but might not know exactly what the difference between those is and what exactly each API does handle. This will be fairly technical. So I will have to jump into code at some point, because I really want to show you how you can write a KMS driver. So there will be kernel code. There will be C code. Apologize for that. Please try not to fall asleep, at least too early. If anyone wants me to kick him if you fall asleep, just tell me before him. OK, so starting with DRM and KMS, DRM is the direct rendering manager. So that's a kernel to use a space API and a kernel infrastructure that started as a project to support GPUs in Linux. So it was mostly at that time about 3D acceleration. And we needed an API to expose that to user space. So we already had the FB Dev API on the kernel side to expose the display capabilities of the hardware. But for the 3D acceleration, we needed something new to manage methods, to manage the memory, to handle the common cues, to be able to handle synchronization. And all that went into KMS. That was quite a few years ago, into DRM, sorry. That was quite a few years ago. And KMS didn't exist at that time. KMS stands for kernel mode setting. So it's a fairly recent addition to DRM that's moving all the mode setting codes that lived in user space, mostly in the X server back then. It aimed at moving that inside kernel space, inside the kernel drivers, so that you could actually control your display hardware and modified display modes from the kernel. There were many reasons to do that. One of them was to be able to control that from inside the kernel at boot time when you want to have early display before X is available. Another reason was to actually arbitrate between the different user space components that might want to control the hardware. Because when you go directly to your PCR hardware from user space from the X server, then it becomes quite messy if you have different components in user space that want to access that directly. So nowadays, we have both DRM and KMS inside a kernel and both of them overlap. So we have two APIs that are exposed through a single device and that are used by user space. And you cannot really use DRM without using KMS or the other way around. We'll see that in a minute. So as I mentioned, DRM handles mostly memory, vertical blanking. And there's a couple of core tasks that nobody really wants to do because there's nothing fancy in that, but that needs to be available. So exposing a version number to user space, exposing authentication features, so that an unauthorized user space application will not mess up with your graphic hardware, handling the multiple master configuration when you want a user space application to hand over control of the hardware to another one. So that's really cool stuff that are quite boring. And I will not go into too many details about them in this talk because those are mainly in DRM. That's something that the DRM handles for you and you don't really have to care about that in your driver. On the KMS side, it's a bit more interesting, at least from my point of view, because I've been working on that. That shouldn't happen. OK. Right. OK. I have to stay close to the computer. So KMS handles expose a device model to your application to user space. We'll come to that in a minute as well. It handles frame buffers. So a frame buffer is a memory object that you can use to give to display hardware and that will contain information that you want to display on the screen so that will be scanned out to the screen. It handles mode setting. That's the kernel mode setting API, so that's more or less a given. It handles page flipping as well. Page flipping is a process in which you can atomically switch between different frame buffers, between different memory arrays to avoid tearing when you want to display video, for instance. It handles planes, overlays, and hails many other small details that, unfortunately, I will not have time to go into a harder cursor, gamma tables, things like that. Before diving into the kernel side, it's very important to explain what a KMS defined looks like and what the KMS model is, because that's really the core of the KMS operation and you need to understand that both from the user space side and the kernel side to understand how to write a kernel driver or how to write a KMS application in the space. So KMS handles five different kind of objects at its core. On the left, you have the memory objects. So I mentioned a frame buffer. A frame buffer is really a piece of memory in which you have graphics data that you want to push to display. That was the only memory object available when KMS was created. And the frame buffer is associated with a CRTC. So CRTC stands for CRT controller. There's probably no CRT anymore in use nowadays in any kind of Linux device, but still the name is still here. So that's a piece of hardware that will get the pointer to your memory frame buffer and that will scan the memory frame buffer out of the chip to an encoder. The encoder is then a piece of hardware that will take the digital signals that get out of the CRTC and encode that to whatever format you need for your display. So that could be analog and VGA, for instance, analog for TV. There are several different digital formats for HDMI, DVI, for flat panels as well. So we have a different set of encoders. Some of them are really simple. They don't really need to be controlled. They just take digital signals on the input and will output signals that are compatible with a flat panel. And that's it. Some of them are much more complex. So that's an object that's exposed by KMS API because it sometimes needs to be controlled precisely in the user space. And then on the far right hand, you have the connectors. We have to remember that KMS started as an x86 or PC API in which you have a graphics card and you have connectors to get the signals out of the graphic cards. There's usually no embedded LCD on the desktop. It's changed, obviously, with netbooks. And so the model started with connectors. But a connector can actually be something that handles a flat panel or any kind of display device. So it can be a physical connector. It can just be an interface on the bar to the real flat panel. So I forgot to mention about the planes. So planes are a recent addition to KMS API. So it's also memory objects, exactly that frame buffer. But when you have a single frame buffer, you need to scan out. That was pretty much all that was supported when we started by the hardware that we needed to support. But some hardware can overlay other images on top of the main frame buffer. So that's what the planes are for. They're pieces of memory that you can give to the CRTC and that you can overlay on top of your main frame buffer. So you have to part for different hardware overlays. If we had to redesign the API nowadays, we probably wouldn't separate the main frame buffer and the planes because there's no real difference between them except that the main frame buffer is at the bottom of the stack, if you think about the Z-order. But except that, it's a plane like the others. But for historical reasons, they're currently separated. On the embedded side, well, memory is memory. There's no question about that. The CRTC is what you will have on your SOC. That's the graphic control or the display control on your SOC. For encoders, it's a bit of a mixture here. You can have the encoders inside your SOC or you can have them outside on board. The connectors, obviously, are not part of the SOC. They are on the board and off-chip. So that's a pretty simple model. There are a couple of core objects. They can represent more or less all that you have in nowadays hardware. Well, really more or less because we have some current cases today that will be more common in the future. Things like deep pipelining, for instance, when you have a camera interface in your SOC that can deep pipeline data to your display controller. And that's not supported in this model. We have memory as inputs on the left. We don't have deep pipeline capabilities, for instance. So there are a couple of issues. We will need to extend that. But the basic model is pretty simple. The scandal process, as I mentioned, is about taking a frame buffer, so piece of memory, and pushing that to the display. See, you don't need to use the whole frame buffer, actually. You can use a part of the frame buffer. The frame buffer can be bigger than the display size. And you can take part of that and push that to the display. So that's smaller the scandal process. With the plane support, we have in KMS support for scaling the planes. That's not supported from the main frame buffer. And KMS can compose that. So the composition model is not part of the KMS API. KMS will just know there's a main frame buffer. There are planes I need to compose on top of that. And everything else, like configuring alpha blending, ZD, Z-order, things like that, that's left to draft a specific API. I mentioned a frame buffer. So a frame buffer is, well, I said it's a piece of memory. But actually, it can be a bit more complex than that. Because depending on your video format, you can actually have several pieces of memory. When you have a planner format, or you have an NV12 format, for instance, when you have one piece of memory with the Y data and one piece of memory with the chroma data, then you have two separate memory arrays that make your whole frame buffer. So the frame buffer in the KMS model has a couple of properties. It has a size. It has a pixel format to know what kind of data you have in there. It has pitches, offsets. Very importantly, it has memory objects that it uses to create that whole frame buffer. So in the usual case, you will have a single memory object. But you can have them in many cases when you need to. We could have formats where we need more than two memory objects. They support up to four of them, I think, in the frame buffer API. Yep? So when you say format, do you literally mean it's RGBA? It's YUV? Exactly. So it's not simply the case that it's single buffer, multi-buffer, or whatever? It's really RGB 16-bit with 565, things like that. So it's a format identifier that we have in the API in there. So yeah, we support up to four memory objects. They showed here as GAM objects. So GAM stands for Graphical Execution Manager. That's where it comes from. But nowadays, in the embedded world, we use GAM as a memory manager. It has no relationship with any kind of graphics execution. So when I'm talking about GAM objects, that's a memory object that's handled by the KMS API and by the DRM API. We have options to have different kind of memory objects, but we'll not go into that because that's never used in embedded drivers nowadays. And there's no reason for that, anyway. So a GAM object, that's really the piece of memory. It's a linear piece of memory, at least from a virtual point of view, virtual memory point of view. It also has a size. It has a number of bits per pixel. It has a pitch. And it's associated with a piece of physical memory. And several of those GAM objects are used to create a frame buffer. Briefly talk about handles. So when the kernel driver will allocate the memory for you. So user space will ask for memory to be located depending on user space needs. And then allocation is performed by the driver. When a GAM object is allocated by a driver on behalf of an application, the application has control on that object. But sometimes you want to actually share that object between several applications. Like you have an application that will allocate memory for a texture. You want to pass that to another application so that it will be filled. So you need to be able to share those pieces of memory between the applications. And you also need to share them between different drivers. So when you create a GAM object in the kernel, you have local handle that's available to your application that's just a number that's referenced the buffer. And then you can export that as global handle. So application can request a global handle that's a file descriptor that's created behind a scene for a given GAM object. And when it gets that, the application can send the file descriptor to a separate application over a unique socket. So there's an API to do that. Because the file descriptor is just an integer that's local to an application. You can just send the integer value that will not work. And one added benefit of that API is that it's not possible for the other application to just guess the ID of the buffer. It needs to receive it explicitly. So we have embedded security in there, which means that if you have a buffer that you don't want to have exposed to any application that will contain sensitive data. And by sensitive data and graphics, we're just talking about copyrighted data, unfortunately. I mean, I don't really care about that myself. But if we design an API that don't support that kind of use cases, then the industry will not use it. So it's not possible for only use the PACE process to just go and access a buffer it doesn't have access to. It needs to be explicitly allowed to do that by sending the buffer ID and the buffer handle to the application. And then the second process then can go back to the kernel and give the file descriptor to kernel driver and get back a local handle that it can use in all the APIs. Briefly talk about the modes as well, the way we represent the mode. When you have a video that's displayed, you have horizontal and vertical synchronization signals. So you have a number of lines, a number of pixel lines. And at the beginning of the line, you will have a synchronization pulse over here. Then you have a viable delay, the start of the active area, then a viable inactive area here, and then you go back to the next line. And it's the same for vertical synchronization signals. So we have the sync area. We have a delay that's called the back porch, because at the back of the sync, we have the active area of the image, and we have the front porch. Because if you think about it, the synchronization signals is produced here as well. So the front porch is actually right in the front of the synchronization signal. That's more or less what a display mode is. The way we represent that in KMS is that we actually move things around a bit. So you have your back porch here, your front porch here, your synchronization, and then your back porch. And we have four values that are used to represent the mode. So you have the width of the active area, the position of the synchronization pulse start and end, and the total number of pixel cloud pulses that you have on a single line. It's the same for the vertical synchronization. And then last part of the model in the API that is exposed, and we need to understand, is the mode setting API. KMS is about mode setting, so that's really the core of it. Mode setting is about taking a frame buffer with X and Y offsets inside the frame buffer with an eighth of an active area in there, giving that to a CRTC to be scanned out on one or more connectors. And those connectors should be configured with a given video mode. So that's what the mode setting API does. You give all that information from user space and the driver needs to do the rest. And we'll now see how the driver actually handles that. There's documentation inside the kernel, documentation.book directory. It's not complete. It's better than it was a year ago because there was nothing a year ago, so the synchronization is definitely much better now. If you're going to write a KMS driver, read that documentation, try to write the code, and if there's something that's in clean the documentation, something that's incomplete, if you need to actually have a look in the code in the DRM car or in example drivers, then please contribute it to the documentation. If the documentation isn't clean enough, I mean, I've been working on KMS for some time now. There are things I know and understand because I'm familiar with them, but they might not be clear from the documentation. So if they're not clear for you, please tell me, send a patch if possible, or just tell me that there's something that's not good with the documentation and I will try to help with that. I'm going to dive into the code now. That's going to be the boring part of the talk. Well, I hope you're not bored yet, otherwise this will be terrible. I've omitted locking and handling because otherwise it would be completely unreadable. Don't do that in your code. Have a look at the drivers that we have in the kernel if you want to see how errors are managed, how we need to handle locking. It's mentioned documentation as well. So just a couple of conventions. Every function that will start by DRM and this car, that's part of DRM car. Every function that will start by our car, DU, that's the driver I've used as a basis for this presentation, that's Renes' display unit driver, that's specific to the driver so that you know what part of the code is implemented in the DRM car and what part is implemented in the driver itself. So we all start, well, you get your kernel module. Your kernel module is initialized. It probes your platform device. We're talking about embedded devices here so I'll talk about the platform device case only. Obviously the support for PCI and USB-CMS devices as well in the kernel, but that's not the topic of this talk here. So you have your platform driver and you have your probe and remove functions. In those functions, you just have to call DRM platform unit and DRM platform exit and that's it. So that's pretty easy. And you give to those functions a pointer to a structure that's a struct DRM driver and that will contain all information that DRM will need to actually process all the use space calls and forward that to your driver. So let's get to that DRM driver structure. There's lots of fields. I'm going to present them as they used. There's a couple of starting information first. There's a flag field that is used to tell the DRM car what your driver needs and what it can do. So your driver probably needs to have an interrupt. Otherwise it's a bit difficult to react to vertical blanking even so that's more or something that you always need to do. You want to tell that your driver use the GEM marry manager because there's no point in using another one. You want to tell that your driver can do mode setting. It's not an old legacy driver with the user space access to the hardware from the Xserver. So you're using mode setting. And you also want to tell that it's using the prime API. The prime API is the API I talked about to that allows sharing buffers between different applications and different drivers. You just give a name to your driver description or date. The date is actually supposed to tell user space, report it to user space and it's supposed to tell when the last update to the driver happened. We all know that's something that you just write in your driver the first time and then you always forget to update the date. So it's more or less pointless. Nowadays it just tells when the driver was treated for the first time. You get a major, minor and patch level version number. Patch level, that's something you always forget. To update as well. Major and minor are actually useful because the version of the driver API exposed to user space can change. So you have the KMS standard API but you allow to expose private driver your controls. And at some point you might decide that you need a new version of those. So you need to increase your minor number when you make additions to your driver specific API. So the user space will know about that. And you want to increase your major number when you make modifications that are completely incompatible with the previous version. And the DRM car will handle that is a code to use from user spacing. I want to use this API version and the DRM car will handle that for you. Then you have the file operations. They're all that's quite, usually that's quite easy in this case actually. So in the DRM driver structure you have a pointer to file operations. And if you look at them, most of them except the last one, they're calling DRM car functions. So you allow to override that. You allow to use your own functions if you need to. But except maybe for the compatibility control that's used for 32 and 64 bit compatibility, you shouldn't need to do that. And even that, I mean, that problem, we will need to wait from ARM 64 to be you, who's KMS drivers before that's really useful. That's just the M-Map file operation that you will need to implement yourself. We'll get to that. Then you have the load function. That's the main entry point of your driver. That's something that the DRM car will call. So when we call the DRM platform in it, it will perform a couple of initialization and call your load function. So that's a bit like your probe function in a non-KMS driver. And in there you will do all your driver, your device specific initialization. You need to locate memory for your device, device private data. You need to get the memory sources, the clocks, the regulators, everything that your device needs. That's where it's handled. That's pretty boring. That's definitely device specific. So I won't go into details of what needs to be done there. And you just start a pointer to your private data inside the depth private field of the DRM device. Then you want to handle IQ registration. There's a helper function that helps you doing that in DRM. It will get the IQ resource for the platform resources. It will perform a couple of steps. So you just want to call DRM IQ install. And that will do all the magic. There's a caveat in that. If you have more than one interrupt that's needed by your driver, you need to do it yourself. So that only supports a single interrupt. You also have pre-installed and post-installed callback functions that can be used while as the name implies before and after installing the IQ handler. And in pre-installed, the optional, if you use pre-installed, you should make sure that you will clear like interrupt flags there so that you will not have any spurious interrupts that will be triggered. And in post-installed, you usually want to enable the interrupts if needed. And it's obviously the IQ handler that you need to provide. Then we go, we're going to the mode configuration initialization. So KMS is about mode setting. And you need to call the DRM mode config init function. That's going to initialize while mostly all the lists, mutixes, everything that's needed by KMS internally. Just have to add that and you have to fill a couple of fields with the minimum and maximum size of the frame buffers that you can support. So that's specific to your hardware, you should know about that. And you also need to provide a pointer to a structure with mode config functions. And there's only one that I'm going to talk about here. That's the fbcreate function that is used to create a frame buffer. So from user space application, we'll ask the driver to create a frame buffer because it will want to put data in there and push that to the display. And that's a function that's called to create that frame buffer. And I'll explain how that works. We're using GEM and because of that, we have to fill the mmap file operation and two driver functions, callback functions, to free GEM object. And the other one is not function, it's actually a pointer to a structure with VM virtual memory operations. That's something you need to implement yourself, except if you can live with the helpers that we have in the kernel in KMS. We have lots of helper layers that handles the complex tasks in most cases. So in most embedded cases, you will want to locate your memory buffers contiguously in memory, at least contiguously in the device memory space if you have an IUMMU. And in that case, we have the DRM GEM CMA helper layer that handles all the memory management for you. So you just give the pointer to destruction and the functions from the GEM CMA helper. CMA stands for contiguous memory allocation, but there's actually nothing that's specific in there to the CMA API. So behind the scene, it uses DMA unlock right combine. And if you want that kind of memory buffers, that's what you want to use. If you have a more complex hardware with different requirements on memory management, then you will have to implement that yourself. But in most cases, that's really the simplest solution. GEM has support for dump objects as well. So if we look back at the previous slide, you see that the operations can actually free an object, map it to user space, but there's no operation to allocate the object. That's because memory allocation was left to driver-specific APIs. The reason for that is, was that on the desktop world, the memory allocation requirements really varied between the different devices. So that was left to a device-specific API. But then we realized that there was a caveat with that, is that it was impossible to have a generic user space application that would be able to allocate memory. That's not really a problem when you have a complete graphics stack in user space with X away land, because you will have user space drivers for that. But when you have an early boot application that you want to display as touch screen, then you don't want in your early boot application to support all the different memory allocations API possible. So we've added in KMS a dumb memory buffer allocation API that can be used to locate a memory object that can be used as a frame buffer so that you can push that to the CRTC to be displayed. It cannot be used to allocate a memory buffer that's usable for OpenGL texture, for instance, but that's not the top topic of this talk. So we have those three functions that are handled by the game CMA helpers to allocate the memory for you. And if your hardware is just a display hardware without an integrated GPU, so if your GPU is a separate IP from an SG GPU, a Mali GPU, whatever, then that's what you want to use because all your display, all your display buffers, they will be dumb buffers anyway. So there's no need to add anything there. Last topic about game memory allocation. So that's something I mentioned, the prime API to be able to share buffers. There are two functions that you need to implement. They call prime handle to FD and FD to handle to convert local handle to global FD and global FD to a local handle. When you're using game objects, you just use two helper functions here. We'll always use game objects, so that's just the function that you need to use. And those two functions will use, will call the game prime import and game prime export functions that also provided by the game CMA helpers. So all that is heavy for you. So if we summarize that, allocation of the buffers, mapping them to user space, freeing them, exporting them, that's all handled by the helpers and there's nothing you have to do for the memory management. Frame buffer creation. So the FB create function, that's something that you need to implement in your driver. And what you need to do in there, when user space will request a frame buffer creation, then it will pass several parameters like a frame buffer size, pitches, things like that. And you need to validate that. If user space asks for YUV frame buffer and your hardware canal and URGB, you need to validate that and return an error there. That's something that's driver-specific. So you need to implement that. And when you're done validating the information, you can just call the DRM FB CMA create. That's a helper function that's also provided by the game CMA helper and that will create the frame buffer for you with game CMA objects behind the scene. So all you need to do to create a frame buffer is to validate the parameters to make sure that they will work with your hardware and that's it. Now we're getting to the CRTC. So the CRTC is the main piece of the display controller that will handle the scan out of the frame buffer to the display. You need at least one CRTC per driver. If there's no CRTC, there's nothing you can do with your device anyway. So you need at least one. You need to create the CRTC. You can dynamically allocate that. You can embed that in a bigger structure. It can be embedded in your driver private data, whatever, but you need to have a pointer to your DRM CRTC and initialize that and pass a pointer to a structure with CRTC functions. So the first one, I mentioned the other one afterwards, the first one is the destroy function. KMS manages the life, the lifetime and the life cycle of all the objects I mentioned. So your driver doesn't need to care about reference counting the object itself. That's KMS job. That's a core job. And when an object is not needed anymore, then the destroy function will be called. So there's a function provided by the core that's just DRM CRTC cleanup that will perform the exact opposite of DRM CRTC init function. If your CRTC is embedded in a bigger structure and you want to free that or you want to clean up anything you've done at any time, then you can provide your own function and call DRM CRTC cleanup yourself. The encoders, well, it's pretty similar. You initialize your encoder with functions. There's a destroy function. The encoders has a list of possible CRTC it can be connected to. So you have a single CRTC, a single encoder, it's pretty easy. When you have multiple of them, you just have to tell, okay, this encoder on the board can be connected to only the first CRTC or the first and the second CRTC. That's just a big mask of the CRTCs. And you have to tell about the encoder type. So in this case, that's for a VG encoder. So it's a digital to another converter. And there's a list of types that are defined in the API. And then you have to initialize your connectors. So you also have a connector init function. You have connector functions that you need to provide. And it's a bit more complex for the connector because you need to attach that to an encoder. So every connector needs to be attached to one or several encoders. Otherwise, if it's not attached to anything, it's just useless. When you have a simple hardware with one-to-one mapping between encoders and connectors, like you have this encoder that's always connected to one connector, then that's pretty easy. You can just attach them at initialization time. If you have one connector that can actually be attached to different encoders, not at the same time, obviously, then you need to manage that at runtime. Mode setting, then. That's the most interesting part. So as I mentioned, mode setting is about telling a CRTC, he's a frame buffer that I want you to scan out. He's an offset inside the frame buffer and here's the mode that I want to have on the connectors. So there's a single function in the CRTC function that's called set config that takes the DRM mode set structure pointer and that lists all those information. It's fairly complex actually because you need in your hardware, in your driver, to validate all the parameters, to handle the states of the CRTC, the encoder, all the pieces of the hardware. And yeah, that's something that can be a big piece of code. But in most of the cases, we can actually break that in simple operations and we have a helper layer inside KMS that can be used to do that. So you can use the DRM CRTC helper set config function as a handler for the set configuration and then you just need to install by calling the helper add functions for the CRTC connector and encoder, helper function callbacks. You can just use the helpers for the CRTC and the encoder and not the connector. It's either all of them or none of them but if you use that helper function and you should use that otherwise it's really pretty complex. You need to install the helper functions and all of all of those. The helper functions on the CRTC side is quite a couple of them. The first one will be mode fix up. Use a space one to display that mode and kind of hardware support that. So that's what the function does. It will take the mode as a parameter and check if it can be supported by a CRTC. If it cannot, it's rejected. If it can but with minor tweaks like pixel clock frequency modification and things like that, then the mode will be fixed up. It will modify slightly. When that's done, the prepare function of the CRTC is called and that's really device specific. That's why you should perform a device specific operation that needs to be done before setting the mode and usually that's disabling the CRTC and the output. Then we have the mode set function that is called with a new mode. That's why you reprogram the hardware and there's the commit function that is called at the end where you should apply all the settings to the hardware and just enable the CRTC again and then you will have your new mode set up. There's a mode set based function that is a simpler version of mode set and that's used when you don't want to do a complete mode set but just want to change the frame buffer. So you have a bigger frame buffer and you want to pan into that. If you want to just change the offset, there's no need to disable the output and re-enable that, that would just make the screen flicker. And so mode set based in that case is called and prepare and commit functions are not called. So it just sets the offset, the base address in memory from which you want to scan out. And lastly, there's the disable function that's called to disable the CRTC. So when you use the space when to disable the output, that's a function that it gets called. So all that needs to be implemented in driver. It's usually simple code at least for most of them. The mode set function that will reprogram all the register that might be a bit complex depending on your hardware complexity but it still splits into fairly simple functions. Then you also have those helper functions for the encoder and for the connector. There's also a mode fix up function for the encoder because the encoder can have different mode requirements as a CRTC. There's a prepare and commit function and a mode set function. And for the connector, they're just a best encoder function. If we go back to the mode set API, you see that there's a list of connectors that are given. And so your driver needs to find out which encoder to use between the CRTC and the connectors that you want to use. And that's what the best encoder function will do. In most cases, when you connect are just connected to a single encoder, you return a pointer to that encoder and you're done. Mode setting is nice, but you also want to discover the modes. You want to be able to list from user space the mode as supported by your display. And that's done by a connector function because modes are handled at the connector level. That's where you display is plugged in. And that's a fill mode function. So the fill mode function will take a pointer to the connector, a maximum within eight. So it needs to filter all the modes that are larger than that in fill the connector structure with the list of the different modes that are supported. There's also a helper for that. You can use the DLM helper probe single connector modes. That's a bit long. And provide two helper functions. Get modes that will give a list of all the modes supported by your display. And then the car will handle all the filtering for all the features that are all the modes that are bigger than maximum size or not supported for different reasons. And then there's a mode valid function that is used to actually validate a mode that you want to set and make sure that the connector can use that. So that's pretty simple as well. DPMS is about power management and display. So that's a pretty old standard that defined four different power managed power states for the display device. So you have the on state that's pretty easy. You have the off state that's easy as well. It's completely off. It draws the least possible power. And you have two intermediate states that are standby in suspense state. They draw less power than on. So the power consumption decreases when you go down. But the time it takes to recover from standby or suspend or off increases when you go down as well. So when you displays off, the specification actually allows, I think it was the original version up to 10 or 30 seconds for the display to come up, which is according to today's standards, that's insane. So we still have those four power states in the API and that's handled at a connector level. So from user space, you want to tell, okay, on this connector for this screen, I want to put it in standby or suspend. I want to turn it on and off. And there's a function that you need to implement there. But that function will lead to touch the encoder connected to the connector. And also the CRTC. If you want to turn all the displays off, you want to turn your CRTC and all the hardware off as well. So there's a helper layer for that as well. There's a function called dnm helper connector dpms. You should use that. And that relies on dpms callbacks on both the CRTC and on the encoder. So the function will, for instance, if you have two encoders connected to one CRTC, so you have cloned displays, you want to turn one of them off, you still want the other to be on. You turn the second one off, then at that point you can disable completely your display device. And that's what the core handles. It will check what's used in the whole pipeline and then call the two callback functions to set the power state on the encoders and on the CRTCs. Vertical blanking, that allows tier three rendering, so it's pretty important. That needs to be initialized at load time and it takes the number of CRTCs available in your device. Then in your interrupt handler, you need to call dnhandle vblank with the CRTC number and that will account for the vblank interrupt. Counting vblank interrupts sounds like something pretty simple, but you need to make sure that it's race free, that you will handle the counter wraparounds. There are many small caveats that actually make it pretty complex. So the dnhandle handles that for you and you just have to call this dnhandle vblank function. And there are three functions that you need to implement in your driver. There's the enable vblank, disable vblank that will enable and disable the vertical blanking interrupt. And there's the get vblank counter. So if your hardware has a hardware vblank counter, that's the function you need to implement to return the value of the counter. If it doesn't, you just use the dnhandle vblank count function as the handler and that will handle everything for you behind the scene and count the interrupt. Pitch flipping is taking another piece of memory, giving it to the CRTC to replace the frame buffer being scanned out. And you want that to be down at the vertical blanking time so that you get no tearing on the screen. So the page flip function is the function that you need to implement to set a new frame buffer for the CRTC. It takes a pointer to the new frame buffer you want to use, pointer to CRTC and a pointer to a vblank event. If there's already a page flip queued, then you need to tell I'm busy. The KMS API only supports queuing one page flip and you need to wait until it's down before queuing the other one. Then you do whatever you need for your device to handle that. And if there's an event supply by the application, then you enable the vblanking interrupt to report the page flip complication to user space when it will be done. So page flip complication will be down at interrupt time. So when you get your vblank interrupt, you have to implement a piece of code that will fill the event structure with a sequence number. There's a helper function to get that, timestamp also from the helper function. Then you add that to a list of event, wake up the call and then disable the vertical blanking interrupts. All the vertical blanking interrupts are reference counted. So the dmvblank get and put can be called from your driver from the dmcar and you will only be asked to enable and disable the interrupt when there's no user or when there's a new user. Planes, that's the last topic I think. So it's a bit similar to the CRTC frame buffer API. You create a plane. You give it a list of CRTC it can be associated with. That's a bit mask. You give it function pointers and a list of formats that the plane can support. So that's really the list of all the formats that are defined by DRAM and that you hardly can use. Then you need to provide three callback functions. There's still a destroyer function as pretty usual and a plane update and plane disable functions. Plane update is the function that is called when you want to, when you space want to enable a plane, want to enable an overlay or change the overlay parameters. It takes lots of parameters. It takes the plane, the CRTC, the frame buffer. It takes enough sets in the CRTC with an A. It takes enough sets in the frame buffer with an A as well so it can handle scaling. And then you just need to program your hardware to do that. There's a plane disable function that's called when user space want to turn off an overlay and you just have to turn it out there. There's much more than that. I haven't mentioned the properties. Properties are an API that can be used to expose any kind of device-specific properties to user space. So that's how you can extend a KMS API. There's an FB dev emulation layer I haven't mentioned. There's a connector status polling system that I haven't mentioned either. That can be used to report hot plugins to user space. There's an atomic page flip API that can be used to actually do page flip on CRTC and on planes at the same time. Otherwise, when you have an overlay that's moving inside your main frame buffer and you want to grow a border around that so you have your overlay that displays the video and the border is displayed on the main frame buffer. You need to keep them in sync. If you don't do that atomically, then they will move slightly. So all that is not mentioned is though. Some part of those topics are available in documentation I mentioned, not all of them because we need to complete the documentation. So if you're using any of those and if there's no documentation, please send a patch. What's coming next? Well, there's one pretty important topic which is the generic display framework. It started a generic panel framework to have a single framework to expose the single API so that you can write panel drivers in a device-independent way. It grew more or less in a generic display framework that can be used to support not only panels but encoders as well and different pieces of hardware than you have in your display pipeline. It's still not done yet. There's a buff session tomorrow at 4 p.m. So if you're interested in that, Jesse's has been kind enough to organize that so that will be just brainstorming and getting feedback from everybody to make sure that all the use cases we have will be supported. In a nutshell, yeah, 30 seconds. That's the idea behind the display framework. So you have a single panel driver that needs to communicate with the display controller that has an underlying control bus that it uses to talk with the panel. It gets video from the display controller so it needs to control the video stream as well. And so it sits in the middle of lots of blocks and we want to make something that extensible that will cover the panel use cases but also the encoder use cases when you have a more complex chain with really a display panel at the end but different encoders in the chain. All that needs to be handled somehow as well. Most of the use cases I've been told about are more theoretical use cases. Some of them are real. We're not sure yet how complex we'll make DEPR at the moment so that's something that will be discussed tomorrow. Contact information, mailing list, my email address.