LCD Framebuffer

The framebuffer is quite a complex thing to control. It doesn't do anything fancy like 3D rendering, which is certainly much harder still, but even without it the LCD driver is a lot more complex than for example the keyboard driver.

The device does most of its work by reading from main memory (uncached). There are two things it reads there: descriptors with control data, and the framebuffer data for displaying.

The Linux driver supports all kinds of displays. I'll shortly summarize the details for the Trendtac. For more details, see the source. The important files are drivers/video/Jzlcd.c and .h.

The Linux driver resets interrupts on all GPIO pins and sets them to output, but that is actually not needed, since the pins are configured as their "alternate function". Only LCD_RET may be required on other boards, because it is connected to the reset pin externally, so the chip doesn't know about it. On the Trendtac, however, it doesn't seem to do any harm to omit it. This means that the lcd framebuffer does not need to do anything with gpio, which is nice for a microkernel like Iris, which would otherwise need to implement locking. For Linux, it doesn't make much difference.

The Trendtac has a screen of 800x480 pixels. It should run at 60 Hz, with the horizontal sync length 80 pixels and the vertical sync length 20 lines. With this information the registers can be filled:

  • LCD_CTRL = LCD_CTRL_BPP_16 | LCD_CTRL_BST_16: 16 bits per pixel (as opposed to 15; this doesn't say anything about using palette mode) and 16 byte burst mode (no idea what it means, but Linux uses it).
  • LCD_VSYNC and LCD_HSYNC should be set to their values (80 and 20 respectively).
  • LCD_DAV and LCD_DAH are two half-words, the upper contains the sync value again; the lower the sync plus the number of pixels (800 or 480, leading to 880 or 500).
  • LCD_VAT contains the information of lower halfwords of LCD_DAV and LCD_DAH in two halfwords: (880 << 16) | 500.
  • LCD_CFG says something about the type of device. It must be MODE_TFT_GEN, which is 0, plus VSYNC_N, which is 256.

Then the clocks are set up. Before this is done, the lcd must be stopped. Setting up the clocks means setting the divider. There are 880 * 500 * 60 = 26400000 pixels (real plus sync) per second. The clock must run at 4 pulses per pixel, and the divider must be set to one less than the division ratio. So if the main clock runs at 335462400 Hz, as it does in Linux, this means that the ratio is about 12, so the divider must be 3, and the value to set in the register is 2.

When this has been done, CPM_CFCR_UPE must be set in CPM_CFCR. I have no idea what it does, or what UPE means...

After this, the lcd can be started again. After a millisecond delay, LCD_DA0 can be filled with the descriptor's physical address, and the lcd can be enabled. After that, any desired interrupts should be requested. Don't forget to unmask them in the interrupt controller as well.

The descriptor contains the values for 4 registers:

  • LCD_DA0: The location of the next descriptor. It seems to read a new one for each screen update. Linux just points it at this descriptor itself (except for palette modes). Note that it points at physical memory, which starts at 0, not 0x80000000 where kseg0 starts.
  • LCD_SA0: The location of the framebuffer in memory. Also a physical address.
  • LCD_FID0: Frame ID. This is not actually used by the device, it seems. Anyway, with only one frame, it doesn't matter what value it has.
  • LCD_CMD0: Some commands: which interrupts should be emitted, is it a palette mode or not, and what is the size of the frame.

The value in LCD_CMD0 should be LCD_CMD_SOFINT or LCD_CMD_EOFINT if interrupts for start of frame and end of frame are desired, respectively. In the lower bits is the size of the frame divided by 4 (so the number of words, not the number of bytes).

Before letting the device read its descriptor (or framebuffer), it is important that the data is really in memory and not in the cache. For this, it must either be written to an uncached address (0xa.... in kernel mode, or mapped as uncached), or the cache must be flushed before pointing the device to it.

When everything works, the usual 5-6-5 bit layout can be used to write data to the framebuffer (the upper 5 bits for red, the middle 6 bits for green and the lower 5 bits for blue).

-- BasWijnen - 19 Jul 2009

Topic revision: r2 - 20 Jul 2009 - 23:22:39 - BasWijnen
 
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback