gint/src/t6k11/t6k11.c

264 lines
5.7 KiB
C

//---
// gint:t6k11 - Toshiba T6K11 driver
//---
#include <gint/drivers.h>
#include <gint/drivers/t6k11.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/hardware.h>
//---
// Device specification sheet
//---
/* This version number is 1 for the old T6K11 everyone knows, and 2 for the
newer one found in the Graph 35+E II. Documentation is available only for
version 1. Dumps of Bdisp_PutDisp_DD() are used to drive version 2. */
static int t6k11_version = 1;
/* Screen registers on the original T6K11. Registers 8..11 and 13..15 are test
registers and must not be used! */
enum {
reg_display = 0,
reg_counter = 1,
reg_analog = 2,
reg_alternate = 3,
reg_yaddr = 4, /* These two use the same register */
reg_xaddr = 4, /* (interpretation depends on count mode) */
reg_zaddr = 5,
reg_contrast = 6,
reg_data = 7,
reg_daconv = 12,
};
/* Automatic counter increment during read/write */
enum {
cnt_ydown = 0,
cnt_yup = 1,
cnt_xdown = 2,
cnt_xup = 3,
};
//---
// Device communication primitives
//---
/* I/O may be performed either with RS = 0 or RS = 1. The memory-mapping of the
device I/O maps bit 16 of the address to pin RS. There may be other mapped
pins in the address. (?) */
/* RS = 0: Register selection */
GDATA static volatile uint8_t *sel = (void *)0xb4000000;
/* RS = 1: Command data or vram data */
GDATA static volatile uint8_t *cmd = (void *)0xb4010000;
/* command() - send a command to set the value of a register
@reg Register number
@data Value to set in reg */
GINLINE static void command(uint8_t reg, uint8_t data)
{
*sel = reg;
*cmd = data;
}
/* status() - read the status byte from the display driver */
GINLINE static uint8_t status(void)
{
return *sel;
}
/* write_row() - send 16 bytes to the display driver
@buf Buffer to take data from */
GINLINE static void write_row(const uint8_t *buf)
{
/* Unroll the loop for more speed */
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
*cmd = *buf++;
}
//---
// Driver functions
//---
/* t6k11_display() - send vram data to the LCD device */
void t6k11_display_v1(const void *vram, int y1, int y2, size_t stride)
{
for(int y = y1; y < y2; y++)
{
/* Set the X-address register for this row */
command(reg_xaddr, y | 0xc0);
/* Use Y-Up mode */
command(reg_counter, cnt_yup);
/* Start counting Y from 0 */
command(reg_yaddr, 0);
/* Send the row's data to the device */
*sel = reg_data;
write_row(vram);
vram += stride;
}
}
void t6k11_display_v2(const void *vram, int y1, int y2, size_t stride)
{
for(int y = y1; y < y2; y++)
{
command(8, y | 0x80);
command(8, 4);
*sel = 10;
write_row(vram);
vram += stride;
}
}
void t6k11_display(const void *vram, int y1, int y2, size_t stride)
{
if(t6k11_version == 1) t6k11_display_v1(vram, y1, y2, stride);
if(t6k11_version == 2) t6k11_display_v2(vram, y1, y2, stride);
}
/* t6k11_contrast() - change the contrast setting */
void t6k11_contrast(int contrast)
{
if(contrast < 0) contrast = 0;
if(contrast > 32) contrast = 32;
/* Reasonable values for contrast, taken from the diagnostic mode of an
fx9860G II with OS 02.05.2201, are in range 0x97 .. 0xb7.
This means VLC0 = 2 and CONTRAST in [23..55] */
command(reg_contrast, 0x97 + contrast);
/* TODO: Turns out that different models, notably screens without
TODO: backlight, will have different ranges. Plus we might want to
TODO: use transitions to extreme contrast settings for visual
TODO: effects.
TODO: Extend the available range of contrast settings. */
}
/* t6k11_backlight() - manage the screen backlight */
void t6k11_backlight(int setting)
{
volatile uint8_t *port;
uint8_t mask;
/* This setting is mapped to an I/O port:
- On SH3, bit 7 of port G
- On SH4, bit 4 of port N */
if(isSH3())
{
port = (void *)0xa400012c;
mask = 0x80;
}
else
{
port = (void *)0xa4050138;
mask = 0x10;
}
if(!setting) *port &= ~mask;
if(setting > 0) *port |= mask;
if(setting < 0) *port ^= mask;
}
//---
// Context system for this driver
//---
typedef struct
{
/* Some status bits, obtained by using the STRD command */
uint8_t strd;
/* There *are* other parameters that are affected by the driver, but
they cannot be read, so I can't determine the system's setting */
} GPACKED(1) ctx_t;
/* Pre-allocate a context in gint's uninitialized section */
GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf)
{
if(gint[HWCALC] == HWCALC_G35PE2) return;
ctx_t *ctx = buf;
ctx->strd = status();
}
static void ctx_restore(void *buf)
{
if(gint[HWCALC] == HWCALC_G35PE2) return;
ctx_t *ctx = buf;
/* Set an X-address of 0 with the original display mode */
uint8_t nf = (ctx->strd & 0x04) >> 2;
command(reg_xaddr, 0x80 | (nf << 6));
/* Restore the counter mode */
uint8_t cnt = (ctx->strd & 0x03);
command(reg_counter, cnt);
}
//---
// Driver initialization
//---
static void init(void)
{
gint[HWDD] = HW_LOADED | HWDD_LIGHT;
if(gint[HWCALC] == HWCALC_G35PE2) t6k11_version = 2;
}
//---
// Driver status string
//---
#ifdef GINT_BOOT_LOG
/* t6k11_status() - status string of the driver */
static const char *t6k11_status(void)
{
/* TODO: t6k11: Detect backlight existence */
return "6K11-cB";
}
#endif /* GINT_BOOT_LOG */
//---
// Driver structure definition
//---
gint_driver_t drv_t6k11 = {
.name = "T6K11",
.init = init,
.status = GINT_DRIVER_STATUS(t6k11_status),
.sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save,
.ctx_restore = ctx_restore,
};
GINT_DECLARE_DRIVER(5, drv_t6k11);