#pypp 0 // Iris: micro-kernel for a capability-based operating system. // boot-programs/lcd.ccp: Display driver. // Copyright 2009 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "devices.hh" #define ARCH #include "arch.hh" __asm__ volatile (".section .rodata\n.globl charset\ncharset:\n.incbin \"userspace/data/charset.data\"\n.section .text") extern unsigned char const charset[127-32][8][6] #define assert(x) do { if (!(x)) { kdebug ("assertion failed " #x); while (true) {} } } while (0) #if defined (TRENDTAC) static unsigned h = 800, v = 480, fps = 60, Bpp = 2 #define LOG_X_BASE 1 #define LOG_Y_BASE 1 #elif defined (NANONOTE) static unsigned h = 320, v = 240, fps = 60, Bpp = 4 #define LOG_X_BASE 0 #define LOG_Y_BASE 0 #else #error unknown board #endif #define frame_size (v * h * Bpp) static unsigned physical_descriptor /**/struct Descriptor { unsigned next; unsigned frame; unsigned id; unsigned cmd; } __attribute__ ((packed)) #if defined (NANONOTE) #define SP_PORT 2 #define SPEN 21 #define SPDA 22 #define SPCK 23 static void udelay (unsigned num): for unsigned i = 0; i < num * (JZ_EXTAL / 1000000); ++i: // set 0 does nothing, but costs at least one clock pulse. gpio_set (SP_PORT, 0) // Register names. The registers are not named in the datasheet, and in some cases the name only describes a part of the bits in it. // The registers with bit 6 set have bit 7 set instead, because of the transfer method. enum spi_reg: AC = 0x00 DC = 0x01 BRIGHTNESS = 0x03 FORMAT = 0x04 BACKLIGHT1 = 0x05 VBLANK = 0x06 HBLANK = 0x07 BACKLIGHT2 = 0x08 SYNC = 0x0b POLARITY = 0x0c CONTRAST_RGB = 0x0d SUB_CONTRAST_R = 0x0e SUB_BRIGHTNESS_R= 0x0f SUB_CONTRAST_B = 0x10 SUB_BRIGHTNESS_B= 0x11 TRIM = 0x12 COLOR = 0x13 GAMMA = 0x16 GAMMA1 = 0x17 GAMMA2 = 0x18 GAMMA3 = 0x19 GAMMA4 = 0x1a INVERSION = 0xa5 HLEVEL = 0xa6 LLEVEL = 0xa7 FB = 0xb1 static void write_reg (unsigned reg, unsigned val): unsigned value = (reg << 8) | (val & 0xff) gpio_clear (SP_PORT, 1 << SPEN) udelay (1) for unsigned i = 0; i < 16; ++i, value <<= 1: gpio_clear (SP_PORT, 1 << SPCK) if value & 0x8000: gpio_set (SP_PORT, 1 << SPDA) else: gpio_clear (SP_PORT, 1 << SPDA) udelay (4) gpio_set (SP_PORT, 1 << SPCK) udelay (4) gpio_set (SP_PORT, 1 << SPEN) udelay(4) #endif static void reset (): #if defined (TRENDTAC) // Note that the sync pulse is part of the pre-display region. // Vertical timings. unsigned vsync = 20, vpre = 20, vpost = 0 // Horizontal timings. unsigned hsync = 80, hpre = 80, hpost = 0 // One clock pulse per pixel. unsigned cpp = 1 // Bits per pixel. unsigned bpp = LCD_CTRL_BPP_16 // Configuration. #define MODE_TFT_GEN 0 #define VSYNC_N (1 << 8) unsigned cfg = MODE_TFT_GEN | VSYNC_N #elif defined (NANONOTE) // Note that the sync pulse is part of the pre-display region. // Horizontal timings. unsigned hsync = 1, hpre = 70, hpost = 273 // Vertical timings. unsigned vsync = 1, vpre = 21, vpost = 2 // 3 clocks per pixel. unsigned cpp = 3 // Bits per pixel. unsigned bpp = LCD_CTRL_BPP_18_24 // Configuration. unsigned cfg = LCD_CFG_MODE_SERIAL_TFT | LCD_CFG_HSP | LCD_CFG_VSP | LCD_CFG_PCP // Set up SPI pins. gpio_as_output (SP_PORT, (1 << SPEN) | (1 << SPCK) | (1 << SPDA)) gpio_set (SP_PORT, (1 << SPEN) | (1 << SPCK)) #else #error unknown board #endif // Note that the sync pulse is part of the pre-display region. unsigned vpe = vsync, vds = vpre, vde = vds + v, vt = vde + vpost unsigned hpe = hsync, hds = hpre, hde = hds + h, ht = hde + hpost cpm_stop_lcd () unsigned pixclock = fps * ht * vt #if defined (TRENDTAC) unsigned pllout = cpm_get_pllout () CPM_CFCR2 = pllout / pixclock - 1 unsigned val = pllout / (pixclock * 4) - 1 assert (pllout / (val + 1) <= 150000000) assert (val <= 0xf) cpm_set_lcdclk_div (val) CPM_CFCR |= CPM_CFCR_UPE #elif defined (NANONOTE) unsigned val = cpm_get_pllout2 () / pixclock - 1 //assert (val < 0x400) //cpm_set_pixdiv (val) //cpm_set_pixdiv (12) val = cpm_get_pllout2 () / (pixclock * 3) - 1 assert (val < 0x20) //cpm_set_ldiv (val) // Update dividers. //CPM_CPCCR |= CPM_CPCCR_CE #else #error "Unknown board" #endif cpm_start_lcd () #ifdef NANONOTE // Reset the controller. write_reg (BACKLIGHT1, 0x1e) // Enable display. write_reg (BACKLIGHT1, 0x5e) // Set data to rgbrgbrgb input, with a delta color filter. write_reg (COLOR, 0x01) #endif LCD_CTRL = (bpp << LCD_CTRL_BPP_BIT) | LCD_CTRL_BST_16 | LCD_CTRL_EOFM LCD_CFG = cfg LCD_HSYNC = hpe LCD_VSYNC = vpe LCD_VAT = (ht << 16) | vt LCD_DAH = (hds << 16) | hde LCD_DAV = (vds << 16) | vde LCD_DA0 = physical_descriptor LCD_STATE = 0 LCD_CTRL = (bpp << LCD_CTRL_BPP_BIT) | LCD_CTRL_BST_16 | LCD_CTRL_EOFM | LCD_CTRL_ENA static void putchar (unsigned x, unsigned y, unsigned ch): if ch < 32 || ch > 126: ch = 127 ch -= 32 for unsigned k = 0; k < 6; ++k: for unsigned r = 0; r < 8; ++r: LCD_FRAMEBUFFER_BASE[(y * 8 + r) * h + x * 6 + k] = charset[ch][r][k] * 0x00010101 static unsigned log_x = LOG_X_BASE, log_y = LOG_Y_BASE static void inc_logx (): if ++log_x >= h / 6: log_x = LOG_X_BASE if ++log_y >= v / 8: log_y = LOG_Y_BASE static void log_char (unsigned ch): switch ch: case '\n': while log_x < h / 6: putchar (log_x++, log_y, ' ') inc_logx () break default: putchar (log_x, log_y, ch) inc_logx () static void log_str (char const *str): while *str: log_char (*str++) static void log_num (Iris::Num n): char const *encode = "0123456789abcdef" log_char ('[') for unsigned i = 0; i < 8; ++i: log_char (encode[(n.h >> (4 * (7 - i))) & 0xf]) log_char (' ') for unsigned i = 0; i < 8; ++i: log_char (encode[(n.l >> (4 * (7 - i))) & 0xf]) log_char (']') static void log_msg (): log_str ("prot:") log_num (Iris::recv.protected_data) log_str ("data:") for unsigned i = 0; i < 2; ++i: log_num (Iris::recv.data[i]) log_char ('\n') enum captype: LOG = 1 BACKLIGHT LCD static unsigned spot (unsigned x, unsigned y, unsigned cx, unsigned cy): unsigned dx2 = (x - cx) * (x - cx) unsigned dy2 = (y - cy) * (y - cy) unsigned d2 = dx2 + dy2 unsigned l = 120 if d2 >= l * l: return 0 return ((l * l - d2 - 1) << 8) / (l * l) static unsigned pages static Descriptor descriptor __attribute__ ((aligned (16))) static bool is_on static unsigned create (Iris::Memory mem): unsigned physical = mem.alloc_range (pages) unsigned address = 0x15000 if physical & ~PAGE_MASK: Iris::panic (0, "can't allocate framebuffer") assert (physical & PAGE_MASK && ~physical) for unsigned i = 0; i < pages; ++i: Iris::Page p = mem.create_page () p.alloc_physical (physical + (i << PAGE_BITS), false, true) if address != ~0: mem.map (p, address + (i << PAGE_BITS)) Iris::free_cap (p) return physical static void destroy (unsigned physical, Iris::Memory mem): unsigned address = 0x15000 if physical == ~0: Iris::panic (0, "unable to destroy framebuffer with wrong cap0") if descriptor.frame == physical && is_on: lcd_clr_ena () #ifdef NANONOTE write_reg (BACKLIGHT1, 0x5e) #endif if address != ~0: for unsigned i = 0; i < pages; ++i: Iris::Page p = mem.mapping ((void *)(address + (i << PAGE_BITS))) mem.destroy (p) Iris::free_cap (p) static void use (unsigned physical): if physical == ~0: Iris::panic (0, "unable to use framebuffer with wrong cap0") bool was_unused = descriptor.frame == 0 descriptor.frame = physical unsigned dptr = (unsigned)&descriptor __asm__ volatile ("lw $a0, %0\ncache 0x15, 0($a0)" :: "m"(dptr) : "memory", "a0") if was_unused && is_on: lcd_set_ena () #ifdef NANONOTE: write_reg (BACKLIGHT1, 0x5f) #endif static void unuse (unsigned physical): if physical == ~0: Iris::panic (0, "unable to unuse framebuffer with wrong cap0") if descriptor.frame == physical: lcd_clr_ena () #ifdef NANONOTE write_reg (BACKLIGHT1, 0x5e) #endif descriptor.frame = 0 Iris::Num start (): Iris::schedule () map_lcd () map_cpm () #ifdef NANONOTE map_gpio () #endif pages = (frame_size + ~PAGE_MASK) >> PAGE_BITS unsigned physical = 0 Iris::Page p = Iris::my_memory.mapping (&descriptor) unsigned paddr = p.physical_address () physical_descriptor = paddr + ((unsigned)&descriptor & ~PAGE_MASK) Iris::free_cap (p) descriptor.next = physical_descriptor descriptor.frame = physical descriptor.id = 0xdeadbeef descriptor.cmd = LCD_CMD_EOFINT | ((frame_size / 4) << LCD_CMD_LEN_BIT) unsigned dptr = (unsigned)&descriptor __asm__ volatile ("lw $a0, %0\ncache 0x15, 0($a0)" :: "m"(dptr) : "memory", "a0") reset () #if defined (TRENDTAC) Iris::Cap logcap = Iris::my_receiver.create_capability (LOG) __asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory") #endif // Register the backlight device. Iris::Setting backlight = Iris::my_receiver.create_capability (BACKLIGHT) Iris::my_parent.provide_capability (backlight.copy ()) Iris::free_cap (backlight) // Register the display device. Iris::Display display = Iris::my_receiver.create_capability (LCD) Iris::my_parent.provide_capability (display.copy ()) Iris::free_cap (display) Iris::my_parent.init_done () Iris::Cap eof_cb bool have_eof = false is_on = true while true: Iris::wait () //log_msg () switch Iris::recv.protected_data.l: case 0: have_eof = false eof_cb.invoke () Iris::free_cap (eof_cb) break #if defined (TRENDTAC) case LOG: log_char (Iris::recv.data[0].l) break #endif case BACKLIGHT: switch Iris::recv.data[0].l: case Iris::Device::RESET: Iris::recv.reply.invoke () break case Iris::Setting::SET: // TODO unsigned state = Iris::recv.data[1].l if !state: #if defined (NANONOTE) if is_on: write_reg (BACKLIGHT1, 0x5e) is_on = false #else #endif else: #if defined (NANONOTE) if !is_on: write_reg (BACKLIGHT1, 0x5f) is_on = true #else #endif Iris::recv.reply.invoke () break case Iris::Setting::GET_RANGE: Iris::recv.reply.invoke (~0) break default: Iris::panic (0, "invalid operation for backlight") break break case LCD: switch Iris::recv.data[0].l: case Iris::Device::RESET: Iris::recv.reply.invoke () break case Iris::Display::SET_EOF_CB: Iris::Cap reply = Iris::get_reply () Iris::Cap arg = Iris::get_arg () if have_eof: Iris::free_cap (eof_cb) Iris::panic (0, "replacing eof_cb") else: lcd_clr_eof () Iris::register_interrupt (IRQ_LCD) have_eof = true eof_cb = arg reply.invoke () Iris::free_cap (reply) break case Iris::Display::MAP_FB: unsigned addr = Iris::recv.data[1].l unsigned use = Iris::recv.data[0].h Iris::Cap reply = Iris::get_reply () Iris::Memory mem = Iris::get_arg () unsigned physical = mem.alloc_range (pages) assert (physical & PAGE_MASK && ~physical) Iris::Caps ret = mem.create_caps (pages / 63 + 1) unsigned slot = ret.use () for unsigned c = 0; c < pages / 63 + 1; ++c: Iris::Caps caps (Iris::Cap (slot, c)) unsigned num = pages - 63 * c >= 63 ? 63 : pages - 63 * c Iris::set_recv_arg (caps) mem.create_caps (num) unsigned slot2 = caps.use () for unsigned i = 0; i < num; ++i: Iris::Page p = Iris::Cap (slot2, i) Iris::set_recv_arg (p) mem.create_page () p.alloc_physical (physical + ((63 * c + i) << PAGE_BITS), false, true) mem.map (p, addr + ((63 * c + i) << PAGE_BITS)) Iris::free_slot (slot2) Iris::free_slot (slot) reply.invoke (0, 0, ret.copy ()) Iris::free_cap (ret) Iris::free_cap (mem) Iris::free_cap (reply) if !use: break bool was_unused = descriptor.frame == 0 descriptor.frame = physical unsigned dptr = (unsigned)&descriptor __asm__ volatile ("lw $a0, %0\ncache 0x15, 0($a0)" :: "m"(dptr) : "memory", "a0") if was_unused && is_on: lcd_set_ena () #ifdef NANONOTE: write_reg (BACKLIGHT1, 0x5f) #endif break case Iris::Display::UNMAP_FB: Iris::panic (0, "unmap_fb isn't implemented yet") case Iris::Display::GET_INFO: Iris::panic (0, "get_info isn't implemented yet.") default: Iris::panic (Iris::recv.data[0].l, "invalid operation for lcd") break default: Iris::panic (0, "invalid operation type for lcd") break