mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-11-05 06:43:44 +02:00
245 lines
7.0 KiB
COBOL
245 lines
7.0 KiB
COBOL
#pypp 0
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
// boot-programs/lcd.ccp: Display driver.
|
|
// 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/>.
|
|
|
|
#include "devices.hh"
|
|
#include "init.hh"
|
|
#define ARCH
|
|
#include "arch.hh"
|
|
|
|
__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]
|
|
|
|
#define assert(x) do { while (!(x)) kdebug ("assertion failed " #x); } while (0)
|
|
|
|
#if defined (TRENDTAC)
|
|
// For now, support only 16 bpp.
|
|
// Screen is 800x480 tft.
|
|
static unsigned h = 800, v = 480, hs = 80, vs = 20, fps = 60, Bpp = 2
|
|
#else if defined (NANONOTE)
|
|
static unsigned h = 320, v = 240, fps = 70, Bpp = 3
|
|
#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
|
|
|
|
static void reset ():
|
|
#if defined (TRENDTAC)
|
|
unsigned fps = 60
|
|
// Vertical timings.
|
|
unsigned vps = 0, vpe = vps + 20, vds = vpe, vde = vds + v, vt = vde
|
|
// Horizontal timings.
|
|
unsigned hps = 0, hpe = hps + 80, hds = hpe, hde = hds + h, ht = hde
|
|
unsigned extra = 0
|
|
// 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)
|
|
unsigned fps = 70
|
|
// Vertical timings.
|
|
unsigned vps = 0, vpe = vps + 1, vds = vpe + 20, vde = vds + v, vt = vde + 1
|
|
// Horizontal timings.
|
|
unsigned hps = 0, hpe = hps + 1, hds = hpe + 140, hde = hds + h, ht = hde + 273
|
|
// 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.
|
|
unsigned cfg = LCD_CFG_MODE_TFT_SERIAL_TFT | LCD_CFG_PCP | LCD_CFG_HSP | LCD_CFG_VSP
|
|
#else
|
|
#error unknown board
|
|
#endif
|
|
|
|
LCD_CTRL = bpp | LCD_CTRL_BST_16
|
|
LCD_VSYNC = (vps << 16) | vpe
|
|
LCD_HSYNC = (hps << 16) | hpe
|
|
LCD_DAV = (vds << 16) | vde
|
|
LCD_DAH = (hds << 16) | hde
|
|
LCD_VAT = (ht << 16) | vt
|
|
LCD_CFG = cfg
|
|
|
|
cpm_stop_lcd ()
|
|
|
|
unsigned pixclock = fps * (ht + extra * (hde - hds)) * 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 () / pclk - 1
|
|
if val > 0x3ff:
|
|
kdebug ("pixel clock too large\n")
|
|
Kernel::panic ()
|
|
return
|
|
cpm_set_pixdiv (val);
|
|
|
|
val = cpm_get_pllout () / (pixclock * 3)
|
|
if val > 0x1f:
|
|
kdebug ("pixel divider too large\n")
|
|
Kernel::panic ()
|
|
return
|
|
cpm_set_ldiv (val)
|
|
// Update dividers.
|
|
CPM_CPCCR |= CPM_CPCCR_CE
|
|
#endif
|
|
|
|
cpm_start_lcd ()
|
|
|
|
LCD_DA0 = physical_descriptor
|
|
lcd_set_ena ()
|
|
lcd_enable_eof_intr ()
|
|
|
|
static void putchar (unsigned x, unsigned y, unsigned ch, unsigned fg = 0xffff, unsigned bg = 0x0000):
|
|
if ch < 32 || ch > 126:
|
|
ch = 127
|
|
ch -= 32
|
|
unsigned lookup[2] = { bg, fg }
|
|
for unsigned k = 0; k < 6; ++k:
|
|
for unsigned r = 0; r < 8; ++r:
|
|
LCD_FRAMEBUFFER_BASE[(y * 8 + r) * h + x * 6 + k] = lookup[charset[ch][k] & (1 << r) ? 1 : 0]
|
|
|
|
static unsigned log_x = 1, log_y = 1
|
|
static void inc_logx ():
|
|
if ++log_x >= h / 6:
|
|
log_x = 1
|
|
if ++log_y >= v / 8:
|
|
log_y = 1
|
|
|
|
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 (Kernel::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 (Kernel::recv.protected_data)
|
|
log_str ("data:")
|
|
for unsigned i = 0; i < 2; ++i:
|
|
log_num (Kernel::recv.data[i])
|
|
log_char ('\n')
|
|
|
|
Kernel::Num start ():
|
|
map_lcd ()
|
|
map_cpm ()
|
|
|
|
Descriptor descriptor __attribute__ ((aligned (16)))
|
|
unsigned pages = (frame_size + ~PAGE_MASK) >> PAGE_BITS
|
|
unsigned physical = Kernel::my_memory.alloc_range (pages)
|
|
assert (physical & PAGE_MASK && ~physical)
|
|
for unsigned i = 0; i < pages; ++i:
|
|
Kernel::Page p = Kernel::my_memory.create_page ()
|
|
p.alloc_physical (physical + i * PAGE_SIZE, false, true)
|
|
Kernel::my_memory.map (p, (unsigned)LCD_FRAMEBUFFER_BASE + i * PAGE_SIZE)
|
|
Kernel::free_cap (p)
|
|
for unsigned y = 0; y < v; ++y:
|
|
unsigned g = (y << 6) / v
|
|
unsigned olr = 0, ob = ((25 * y * y) << 5) / (9 * h * h + 25 * v * v)
|
|
for unsigned x = 0; x < h; ++x:
|
|
unsigned r = (x << 5) / h
|
|
unsigned b = ((9 * x * x + 25 * y * y) << 5) / (9 * h * h + 25 * v * v)
|
|
if r != olr:
|
|
olr = r
|
|
r = 0x1f
|
|
unsigned oyb = b
|
|
if y > 0:
|
|
oyb = ((9 * x * x + 25 * (y - 1) * (y - 1)) << 5) / (9 * h * h + 25 * v * v)
|
|
if b != ob || b != oyb:
|
|
ob = b
|
|
b = 0x1f
|
|
LCD_FRAMEBUFFER_BASE[y * h + x] = (r << 11) | (g << 5) | (b)
|
|
Kernel::Page p = Kernel::my_memory.mapping (&descriptor)
|
|
physical_descriptor = p.physical_address () + ((unsigned)&descriptor & ~PAGE_MASK)
|
|
Kernel::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 ()
|
|
|
|
Kernel::Cap logcap = Kernel::my_receiver.create_capability (Init::LCD_LOG)
|
|
#if defined (TRENDTAC)
|
|
__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory")
|
|
#endif
|
|
|
|
Kernel::Cap eof_cb
|
|
bool have_eof = false
|
|
while true:
|
|
Kernel::wait ()
|
|
//log_msg ()
|
|
switch Kernel::recv.protected_data.l:
|
|
case IRQ_LCD:
|
|
lcd_clr_eof ()
|
|
eof_cb.invoke ()
|
|
break
|
|
case Init::LCD_SET_EOF_CB:
|
|
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)
|
|
reply.invoke ()
|
|
Kernel::free_cap (reply)
|
|
break
|
|
case Init::LCD_LOG:
|
|
log_char (Kernel::recv.data[0].l)
|
|
break
|
|
default:
|
|
log_char ('~')
|
|
break
|