2018-04-19 13:24:26 +02:00
|
|
|
//---
|
|
|
|
// gint:t6k11 - Toshiba T6K11 driver
|
|
|
|
//---
|
|
|
|
|
|
|
|
#include <gint/drivers.h>
|
2019-02-21 20:58:38 +01:00
|
|
|
#include <gint/drivers/t6k11.h>
|
2018-04-19 13:24:26 +02:00
|
|
|
|
2019-02-21 20:58:38 +01:00
|
|
|
#include <gint/defs/attributes.h>
|
|
|
|
#include <gint/defs/types.h>
|
2019-07-04 18:11:43 +02:00
|
|
|
#include <gint/hardware.h>
|
2018-04-19 13:24:26 +02:00
|
|
|
|
|
|
|
//---
|
|
|
|
// Device specification sheet
|
|
|
|
//---
|
|
|
|
|
2020-02-23 15:49:55 +01:00
|
|
|
/* 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
|
2020-05-10 14:03:41 +02:00
|
|
|
version 1. Dumps of Bdisp_PutDisp_DD() are used to drive version 2. */
|
2020-02-23 15:49:55 +01:00
|
|
|
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! */
|
2018-04-19 13:24:26 +02:00
|
|
|
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 */
|
2019-02-21 20:58:38 +01:00
|
|
|
GINLINE static void command(uint8_t reg, uint8_t data)
|
2018-04-19 13:24:26 +02:00
|
|
|
{
|
|
|
|
*sel = reg;
|
|
|
|
*cmd = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* status() - read the status byte from the display driver */
|
2019-02-21 20:58:38 +01:00
|
|
|
GINLINE static uint8_t status(void)
|
2018-04-19 13:24:26 +02:00
|
|
|
{
|
|
|
|
return *sel;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write_row() - send 16 bytes to the display driver
|
|
|
|
@buf Buffer to take data from */
|
2019-02-21 20:58:38 +01:00
|
|
|
GINLINE static void write_row(const uint8_t *buf)
|
2018-04-19 13:24:26 +02:00
|
|
|
{
|
|
|
|
/* 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 */
|
2020-02-23 15:49:55 +01:00
|
|
|
void t6k11_display_v1(const void *vram, int y1, int y2, size_t stride)
|
2018-04-19 13:24:26 +02:00
|
|
|
{
|
|
|
|
for(int y = y1; y < y2; y++)
|
|
|
|
{
|
2020-02-23 15:49:55 +01:00
|
|
|
/* 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);
|
2018-04-19 13:24:26 +02:00
|
|
|
|
|
|
|
/* Send the row's data to the device */
|
2020-02-23 15:49:55 +01:00
|
|
|
*sel = reg_data;
|
2018-04-19 13:24:26 +02:00
|
|
|
write_row(vram);
|
|
|
|
vram += stride;
|
|
|
|
}
|
|
|
|
}
|
2020-02-23 15:49:55 +01:00
|
|
|
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);
|
|
|
|
}
|
2018-04-19 13:24:26 +02:00
|
|
|
|
|
|
|
/* 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);
|
2019-02-21 20:58:38 +01:00
|
|
|
|
|
|
|
/* 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. */
|
2018-04-19 13:24:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 */
|
2018-08-19 17:11:37 +02:00
|
|
|
if(isSH3())
|
|
|
|
{
|
2018-04-19 13:24:26 +02:00
|
|
|
port = (void *)0xa400012c;
|
|
|
|
mask = 0x80;
|
2018-08-19 17:11:37 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-19 13:24:26 +02:00
|
|
|
port = (void *)0xa4050138;
|
|
|
|
mask = 0x10;
|
2018-08-19 17:11:37 +02:00
|
|
|
}
|
2018-04-19 13:24:26 +02:00
|
|
|
|
|
|
|
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 */
|
|
|
|
|
2019-02-21 20:58:38 +01:00
|
|
|
} GPACKED(1) ctx_t;
|
2018-04-19 13:24:26 +02:00
|
|
|
|
|
|
|
/* Pre-allocate a context in gint's uninitialized section */
|
2020-05-10 14:03:41 +02:00
|
|
|
GBSS static ctx_t sys_ctx, gint_ctx;
|
2018-04-19 13:24:26 +02:00
|
|
|
|
|
|
|
static void ctx_save(void *buf)
|
|
|
|
{
|
2020-02-23 15:49:55 +01:00
|
|
|
if(gint[HWCALC] == HWCALC_G35PE2) return;
|
2019-05-03 17:12:41 +02:00
|
|
|
|
2018-04-19 13:24:26 +02:00
|
|
|
ctx_t *ctx = buf;
|
|
|
|
ctx->strd = status();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ctx_restore(void *buf)
|
|
|
|
{
|
2020-02-23 15:49:55 +01:00
|
|
|
if(gint[HWCALC] == HWCALC_G35PE2) return;
|
2019-05-03 17:12:41 +02:00
|
|
|
|
2018-04-19 13:24:26 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-04 18:11:43 +02:00
|
|
|
//---
|
|
|
|
// Driver initialization
|
|
|
|
//---
|
|
|
|
|
|
|
|
static void init(void)
|
|
|
|
{
|
|
|
|
gint[HWDD] = HW_LOADED | HWDD_LIGHT;
|
2020-02-23 15:49:55 +01:00
|
|
|
|
|
|
|
if(gint[HWCALC] == HWCALC_G35PE2) t6k11_version = 2;
|
2019-07-04 18:11:43 +02:00
|
|
|
}
|
|
|
|
|
2019-03-06 14:32:51 +01:00
|
|
|
//---
|
|
|
|
// 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 */
|
|
|
|
|
2018-04-19 13:24:26 +02:00
|
|
|
//---
|
|
|
|
// Driver structure definition
|
|
|
|
//---
|
|
|
|
|
|
|
|
gint_driver_t drv_t6k11 = {
|
2020-05-10 16:36:21 +02:00
|
|
|
.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,
|
2018-04-19 13:24:26 +02:00
|
|
|
};
|
|
|
|
|
2018-08-19 17:11:37 +02:00
|
|
|
GINT_DECLARE_DRIVER(5, drv_t6k11);
|