1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-11-05 11:17:12 +02:00
iris/boot-programs/lcd.ccp

319 lines
8.7 KiB
Plaintext
Raw Normal View History

2009-06-10 22:42:01 +03:00
#pypp 0
// Iris: micro-kernel for a capability-based operating system.
2009-06-24 01:47:13 +03:00
// boot-programs/lcd.ccp: Display driver.
2009-06-10 22:42:01 +03:00
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
//
// 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 <http://www.gnu.org/licenses/>.
2009-06-10 23:54:12 +03:00
#include "devices.hh"
2009-06-22 18:48:05 +03:00
#define ARCH
#include "arch.hh"
2009-06-10 22:42:01 +03:00
2009-08-24 22:02:35 +03:00
__asm__ volatile (".section .rodata\n.globl charset\ncharset:\n.incbin \"boot-programs/charset.data\"\n.section .text")
// charset is really the first character in the array. Its address is used as the start of the array.
extern unsigned char const charset[127-32][6]
2009-07-21 13:17:52 +03:00
2009-07-23 13:06:32 +03:00
#define assert(x) do { while (!(x)) kdebug ("assertion failed " #x); } while (0)
2009-07-20 01:23:45 +03:00
2009-10-10 02:31:10 +03:00
#if defined (TRENDTAC)
2009-10-31 10:32:23 +02:00
static unsigned h = 800, v = 480, fps = 60, Bpp = 2
#elif defined (NANONOTE)
static unsigned h = 320, v = 240, fps = 70, Bpp = 4
2009-10-10 02:31:10 +03:00
#else
#error unknown board
#endif
2009-07-20 01:23:45 +03:00
#define frame_size (v * h * Bpp)
2009-08-24 22:02:35 +03:00
static unsigned physical_descriptor
2009-10-31 10:32:23 +02:00
/**/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_B = 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):
gpio_clear (SP_PORT, 1 << SPEN)
udelay (1)
unsigned value = (reg << 8) | (val & 0xff)
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 (1)
gpio_set (SP_PORT, 1 << SPEN)
udelay(4)
#endif
2009-07-20 01:23:45 +03:00
2009-08-24 22:02:35 +03:00
static void reset ():
2009-10-31 10:32:23 +02:00
#if defined (TRENDTAC)
// Note that the sync pulse is part of the pre-display region.
2009-10-10 02:31:10 +03:00
// Vertical timings.
2009-10-31 10:32:23 +02:00
unsigned vsync = 20, vpre = 20, vpost = 0
2009-10-10 02:31:10 +03:00
// Horizontal timings.
2009-10-31 10:32:23 +02:00
unsigned hsync = 80, hpre = 80, hpost = 0
// One clock pulse per pixel.
2009-10-10 02:31:10 +03:00
unsigned extra = 0
// Bits per pixel.
unsigned bpp = LCD_CTRL_BPP_16
// Configuration.
2009-07-05 11:52:44 +03:00
#define MODE_TFT_GEN 0
#define VSYNC_N (1 << 8)
2009-10-10 02:31:10 +03:00
unsigned cfg = MODE_TFT_GEN | VSYNC_N
2009-10-31 10:32:23 +02:00
#elif defined (NANONOTE)
// Note that the sync pulse is part of the pre-display region.
2009-10-10 02:31:10 +03:00
// Vertical timings.
2009-10-31 10:32:23 +02:00
unsigned vsync = 1, vpre = 21, vpost = 2
2009-10-10 02:31:10 +03:00
// Horizontal timings.
2009-10-31 10:32:23 +02:00
unsigned hsync = 1, hpre = 70, hpost = 686
2009-10-10 02:31:10 +03:00
// 3 bytes per pixel, so for the display area 2 extra clocks are sent.
unsigned extra = 2
// Bits per pixel.
unsigned bpp = LCD_CTRL_BPP_18_24
// Configuration.
2009-10-31 10:32:23 +02:00
unsigned cfg = LCD_CFG_MODE_SERIAL_TFT | LCD_CFG_HSP | LCD_CFG_VSP
// 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
2009-10-10 02:31:10 +03:00
2009-10-31 10:32:23 +02:00
// Note that the sync pulse is part of the pre-display region.
unsigned vps = 0, vpe = vps + vsync, vds = vps + vpre, vde = vds + v, vt = vde + vpost
unsigned hps = 0, hpe = hps + hsync, hds = hps + hpre, hde = hds + h, ht = hde + hpost
LCD_CFG = cfg
2009-10-10 02:31:10 +03:00
LCD_HSYNC = (hps << 16) | hpe
2009-10-31 10:32:23 +02:00
LCD_VSYNC = (vps << 16) | vpe
2009-10-10 02:31:10 +03:00
LCD_VAT = (ht << 16) | vt
2009-10-31 10:32:23 +02:00
LCD_DAH = (hds << 16) | hde
LCD_DAV = (vds << 16) | vde
LCD_CTRL = (bpp << LCD_CTRL_BPP_BIT) | LCD_CTRL_BST_16
2009-07-05 11:52:44 +03:00
cpm_stop_lcd ()
2009-06-10 22:42:01 +03:00
2009-10-31 10:32:23 +02:00
unsigned pixclock = fps * (ht + extra * h) * vt
2009-10-10 02:31:10 +03:00
#if defined (TRENDTAC)
2009-07-20 01:23:45 +03:00
unsigned pllout = cpm_get_pllout ()
2009-07-05 11:52:44 +03:00
CPM_CFCR2 = pllout / pixclock - 1
unsigned val = pllout / (pixclock * 4) - 1
2009-07-20 01:23:45 +03:00
assert (pllout / (val + 1) <= 150000000)
assert (val <= 0xf)
2009-07-05 11:52:44 +03:00
cpm_set_lcdclk_div (val)
CPM_CFCR |= CPM_CFCR_UPE
2009-10-10 02:31:10 +03:00
#elif defined (NANONOTE)
2009-10-31 10:32:23 +02:00
unsigned val = cpm_get_pllout2 () / pixclock - 1
kdebug_num (val)
kdebug ("\n")
assert (val < 0x400)
2009-10-10 02:31:10 +03:00
cpm_set_pixdiv (val);
2009-10-31 10:32:23 +02:00
2009-10-10 02:31:10 +03:00
val = cpm_get_pllout () / (pixclock * 3)
2009-10-31 10:32:23 +02:00
assert (val < 0x20)
2009-10-10 02:31:10 +03:00
cpm_set_ldiv (val)
// Update dividers.
CPM_CPCCR |= CPM_CPCCR_CE
#endif
2009-07-05 11:52:44 +03:00
cpm_start_lcd ()
2009-07-20 01:23:45 +03:00
LCD_DA0 = physical_descriptor
2009-07-05 11:52:44 +03:00
lcd_set_ena ()
2009-10-31 10:32:23 +02:00
//lcd_enable_eof_intr ()
#ifdef NANONOTE
// Reset registers.
write_reg (BACKLIGHT1, 0)
// Enable display.
write_reg (BACKLIGHT1, 0x5f)
#endif
2009-06-10 22:42:01 +03:00
2009-07-23 13:06:32 +03:00
static void putchar (unsigned x, unsigned y, unsigned ch, unsigned fg = 0xffff, unsigned bg = 0x0000):
if ch < 32 || ch > 126:
ch = 127
ch -= 32
2009-07-21 13:17:52 +03:00
unsigned lookup[2] = { bg, fg }
for unsigned k = 0; k < 6; ++k:
for unsigned r = 0; r < 8; ++r:
2009-10-10 02:31:10 +03:00
LCD_FRAMEBUFFER_BASE[(y * 8 + r) * h + x * 6 + k] = lookup[charset[ch][k] & (1 << r) ? 1 : 0]
2009-07-21 13:17:52 +03:00
static unsigned log_x = 1, log_y = 1
static void inc_logx ():
2009-10-10 02:31:10 +03:00
if ++log_x >= h / 6:
2009-07-21 13:17:52 +03:00
log_x = 1
2009-10-10 02:31:10 +03:00
if ++log_y >= v / 8:
2009-07-21 13:17:52 +03:00
log_y = 1
2009-07-23 13:06:32 +03:00
static void log_char (unsigned ch):
switch ch:
case '\n':
2009-10-10 02:31:10 +03:00
while log_x < h / 6:
2009-07-23 13:06:32 +03:00
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++)
2009-09-06 12:04:09 +03:00
static void log_num (Kernel::Num n):
2009-07-23 13:06:32 +03:00
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])
2009-07-23 13:06:32 +03:00
log_char (']')
2009-09-06 12:04:09 +03:00
static void log_msg ():
log_str ("prot:")
log_num (Kernel::recv.protected_data)
2009-07-23 13:06:32 +03:00
log_str ("data:")
for unsigned i = 0; i < 2; ++i:
2009-09-06 12:04:09 +03:00
log_num (Kernel::recv.data[i])
2009-07-23 13:06:32 +03:00
log_char ('\n')
2009-07-21 13:17:52 +03:00
2009-10-31 10:32:23 +02:00
enum captype:
LOG
SET_EOF_CB
2009-09-06 12:04:09 +03:00
Kernel::Num start ():
2009-06-10 23:54:12 +03:00
map_lcd ()
map_cpm ()
2009-10-31 10:32:23 +02:00
#ifdef NANONOTE
map_gpio ()
#endif
2009-06-10 22:42:01 +03:00
2009-07-23 13:06:32 +03:00
Descriptor descriptor __attribute__ ((aligned (16)))
unsigned pages = (frame_size + ~PAGE_MASK) >> PAGE_BITS
2009-09-06 12:04:09 +03:00
unsigned physical = Kernel::my_memory.alloc_range (pages)
2009-08-24 22:02:35 +03:00
assert (physical & PAGE_MASK && ~physical)
for unsigned i = 0; i < pages; ++i:
2009-09-06 12:04:09 +03:00
Kernel::Page p = Kernel::my_memory.create_page ()
p.alloc_physical (physical + i * PAGE_SIZE, false, true)
2009-09-06 12:04:09 +03:00
Kernel::my_memory.map (p, (unsigned)LCD_FRAMEBUFFER_BASE + i * PAGE_SIZE)
Kernel::free_cap (p)
2009-10-10 02:31:10 +03:00
for unsigned y = 0; y < v; ++y:
2009-10-31 10:32:23 +02:00
unsigned g = (y << 8) / v
2009-10-10 02:31:10 +03:00
for unsigned x = 0; x < h; ++x:
2009-10-31 10:32:23 +02:00
unsigned r = (x << 8) / h
unsigned b = ((x + y) << 8) / (h + v)
#if defined (TRENDTAC)
LCD_FRAMEBUFFER_BASE[y * h + x] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
#elif defined (NANONOTE)
LCD_FRAMEBUFFER_BASE[(y * h + x) * Bpp + 2] = r
LCD_FRAMEBUFFER_BASE[(y * h + x) * Bpp + 1] = g
LCD_FRAMEBUFFER_BASE[(y * h + x) * Bpp + 0] = b
#else
#error "Define your framebuffer format."
#endif
2009-09-06 12:04:09 +03:00
Kernel::Page p = Kernel::my_memory.mapping (&descriptor)
2009-10-31 10:32:23 +02:00
unsigned paddr = p.physical_address ()
physical_descriptor = paddr + ((unsigned)&descriptor & ~PAGE_MASK)
2009-09-06 12:04:09 +03:00
Kernel::free_cap (p)
2009-07-23 13:06:32 +03:00
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")
2009-08-24 22:02:35 +03:00
reset ()
2009-06-22 17:13:00 +03:00
2009-10-31 10:32:23 +02:00
Kernel::Cap logcap = Kernel::my_receiver.create_capability (LOG)
2009-10-10 02:31:10 +03:00
#if defined (TRENDTAC)
__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory")
2009-10-10 02:31:10 +03:00
#endif
2009-07-23 13:06:32 +03:00
2009-10-10 02:31:10 +03:00
Kernel::Cap eof_cb
bool have_eof = false
2009-06-10 22:42:01 +03:00
while true:
2009-09-06 12:04:09 +03:00
Kernel::wait ()
//log_msg ()
switch Kernel::recv.protected_data.l:
2009-07-05 11:52:44 +03:00
case IRQ_LCD:
2009-07-20 01:23:45 +03:00
lcd_clr_eof ()
2009-08-24 22:02:35 +03:00
eof_cb.invoke ()
2009-06-10 22:42:01 +03:00
break
2009-10-31 10:32:23 +02:00
case SET_EOF_CB:
2009-10-10 02:31:10 +03:00
if have_eof:
Kernel::free_cap (eof_cb)
else:
have_eof = true
eof_cb = Kernel::get_arg ()
Kernel::Cap reply = Kernel::get_reply ()
Kernel::register_interrupt (IRQ_LCD)
2009-10-10 02:31:10 +03:00
reply.invoke ()
Kernel::free_cap (reply)
2009-07-23 13:06:32 +03:00
break
2009-10-31 10:32:23 +02:00
case LOG:
2009-09-06 12:04:09 +03:00
log_char (Kernel::recv.data[0].l)
break
default:
log_char ('~')
2009-06-10 22:42:01 +03:00
break