mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2025-07-03 19:16:36 +02:00
318 lines
7.1 KiB
C
318 lines
7.1 KiB
C
// Renesas R61523 driver-
|
|
|
|
#include <gint/defs/types.h>
|
|
#include <gint/defs/util.h>
|
|
// #include <gint/hardware.h>
|
|
#include <gint/drivers.h>
|
|
// #include <gint/dma.h>
|
|
#include <gint/drivers/r61523.h>
|
|
#include <gint/video.h>
|
|
#include <gint/image.h>
|
|
#include <gint/config.h>
|
|
|
|
#if GINT_HW_CP
|
|
|
|
/* Registers */
|
|
#define REG_HRANGE 0x2a
|
|
#define REG_VRANGE 0x2b
|
|
#define REG_DATA 0x2c
|
|
#define REG_BACKLIGHT_CONTROL 0xb9
|
|
#define REG_DEVICE_CODE_READ 0xbf
|
|
#define REG_DEVICE_CODE_VARIANT 0xda
|
|
|
|
/* Interface with the controller */
|
|
static volatile uint16_t *DISPLAY = (void *)0xb4000000;
|
|
/* Bit 4 of Port R controls the RS bit of the display driver */
|
|
static volatile uint8_t *PRDR = (void *)0xa405013c;
|
|
|
|
/* Screen variant information
|
|
* This number is 0x00 for the old R61523 that everyone knows, and 0x16 or
|
|
* 0x52 for the newer ones found in recent FXCP400 devices. */
|
|
static uint16_t r61523_variant = 0;
|
|
|
|
/* Select a register */
|
|
GINLINE static void select(uint16_t reg)
|
|
{
|
|
/* Clear RS and write the register number */
|
|
*PRDR &= ~0x10;
|
|
synco();
|
|
*DISPLAY = reg;
|
|
synco();
|
|
/* Set RS=1 to allow consecutive reads/writes after a select() */
|
|
*PRDR |= 0x10;
|
|
synco();
|
|
}
|
|
|
|
GINLINE static void write(uint16_t data)
|
|
{
|
|
*DISPLAY = data;
|
|
}
|
|
|
|
GINLINE static uint16_t read(void)
|
|
{
|
|
return *DISPLAY;
|
|
}
|
|
|
|
static void read_Nu16(uint16_t *array, int N)
|
|
{
|
|
for(int i = 0; i < N; i++)
|
|
array[i] = *DISPLAY;
|
|
}
|
|
|
|
//---
|
|
// Generic functions
|
|
//---
|
|
|
|
/* r61523_identify() - identify screen hardware information
|
|
*
|
|
* notes
|
|
* Since recent versions of the FXCP400, new screens are used that break
|
|
* the current driver. These screens are particular since they have the
|
|
* same manufacturer / device code, but they use an undocumented register
|
|
* (0xda) to determine the variant.
|
|
*
|
|
* The register 0xda should be read twice. Casio ignores the first reading
|
|
* and only keep track of the second. So, let's do the same here. */
|
|
void r61523_identify(
|
|
uint32_t *manufacturerCode,
|
|
uint16_t *deviceCode,
|
|
uint16_t *variant
|
|
) {
|
|
uint16_t packets[5];
|
|
|
|
if (manufacturerCode != NULL || deviceCode != NULL)
|
|
{
|
|
select(REG_DEVICE_CODE_READ);
|
|
read_Nu16(packets, 5);
|
|
if(manufacturerCode)
|
|
*manufacturerCode = (packets[1] << 16) | packets[2];
|
|
if(deviceCode)
|
|
*deviceCode = (packets[3] << 16) | packets[4];
|
|
}
|
|
if (variant)
|
|
{
|
|
select(REG_DEVICE_CODE_VARIANT);
|
|
*variant = read();
|
|
*variant = read();
|
|
}
|
|
}
|
|
|
|
//---
|
|
// Display control
|
|
//---
|
|
|
|
void r61523_get_backlight(bool *EN2, int *level, int *PWM_div, bool *dimming)
|
|
{
|
|
int8_t volatile *PNDR = (void *)0xa4050138;
|
|
*EN2 = *PNDR & 0x10;
|
|
|
|
uint16_t packets[5];
|
|
select(REG_BACKLIGHT_CONTROL);
|
|
read_Nu16(packets, 5);
|
|
|
|
*level = packets[2];
|
|
*PWM_div = packets[3];
|
|
*dimming = packets[4] & 1;
|
|
}
|
|
|
|
void r61523_set_backlight(bool EN2, int level, int PWM_div, bool dimming)
|
|
{
|
|
int8_t volatile *PNDR = (void *)0xa4050138;
|
|
if(EN2)
|
|
*PNDR |= 0x10;
|
|
else
|
|
*PNDR &= 0xef;
|
|
synco();
|
|
|
|
select(REG_BACKLIGHT_CONTROL);
|
|
|
|
/* Default value of PWMON */
|
|
write(0);
|
|
synco();
|
|
|
|
write(level);
|
|
synco();
|
|
write(PWM_div);
|
|
synco();
|
|
write(0b1000 | dimming);
|
|
synco();
|
|
}
|
|
|
|
void r61523_get_display_timing(bool mode, bool *waveform, int *CPL, int *BP, int *FP)
|
|
{
|
|
uint16_t packets[6];
|
|
select(mode ? 195 : 193);
|
|
read_Nu16(packets, 6);
|
|
|
|
*waveform = packets[1];
|
|
*CPL = packets[3];
|
|
*BP = packets[4];
|
|
*FP = packets[5];
|
|
}
|
|
|
|
void r61523_set_display_timing(bool mode, bool waveform, int CPL, int BP, int FP)
|
|
{
|
|
select(mode ? 195 : 193);
|
|
|
|
write(waveform);
|
|
synco();
|
|
read();
|
|
write(CPL);
|
|
synco();
|
|
write(BP);
|
|
synco();
|
|
write(FP);
|
|
synco();
|
|
}
|
|
|
|
//---
|
|
// Window management
|
|
//---
|
|
|
|
void r61523_win_set(int x1, int x2, int y1, int y2)
|
|
{
|
|
if (r61523_variant != 0x16 || r61523_variant != 0x52)
|
|
{
|
|
/* R61523 has a 360x640 area; the CP-400 uses the top-right corner
|
|
* for its 320x528 display, so skip over the first 40 columns */
|
|
x1 += 40;
|
|
x2 += 40;
|
|
}
|
|
|
|
uint16_t volatile *DISPLAY = (void *)0xb4000000;
|
|
|
|
select(REG_HRANGE);
|
|
|
|
/* Upper half has 2 bits (total 10 bits = 1024) */
|
|
*DISPLAY = (x1 >> 8) & 3;
|
|
synco();
|
|
*DISPLAY = x1 & 0xff;
|
|
|
|
*DISPLAY = (x2 >> 8) & 3;
|
|
synco();
|
|
*DISPLAY = x2 & 0xff;
|
|
synco();
|
|
|
|
select(REG_VRANGE);
|
|
|
|
*DISPLAY = (y1 >> 8) & 3;
|
|
synco();
|
|
*DISPLAY = y1 & 0xff;
|
|
synco();
|
|
|
|
*DISPLAY = (y2 >> 8) & 3;
|
|
synco();
|
|
*DISPLAY = y2 & 0xff;
|
|
synco();
|
|
}
|
|
|
|
void r61523_display(uint16_t *vram)
|
|
{
|
|
r61523_win_set(0, 319, 0, 527);
|
|
select(44);
|
|
|
|
int row_offset = 0;
|
|
uint16_t volatile *DISPLAY = (void *)0xb4000000;
|
|
|
|
for(int y = 0; y < 528; y++) {
|
|
uint16_t *r5 = (void *)vram + row_offset;
|
|
|
|
for(int x = 0; x < 320; x++)
|
|
*DISPLAY = *r5++;
|
|
|
|
row_offset += 2 * 320;
|
|
}
|
|
}
|
|
|
|
void r61523_display_rect(
|
|
uint16_t *vram, int xmin, int xmax, int ymin, int ymax)
|
|
{
|
|
// dma_transfer_wait(0);
|
|
r61523_win_set(xmin, xmax, ymin, ymax);
|
|
select(44);
|
|
|
|
vram += 320 * ymin + xmin;
|
|
uint16_t volatile *DISPLAY = (void *)0xb4000000;
|
|
|
|
for(int y = 0; y < ymax - ymin + 1; y++) {
|
|
for(int x = 0; x < xmax - xmin + 1; x++)
|
|
*DISPLAY = vram[x];
|
|
vram += 320;
|
|
}
|
|
}
|
|
|
|
void r61523_set_pixel(int x, int y, int color)
|
|
{
|
|
if((unsigned)x >= 320 || (unsigned)y >= 528)
|
|
return;
|
|
|
|
// dma_transfer_wait(0);
|
|
r61523_win_set(x, x, y, y);
|
|
select(44);
|
|
uint16_t volatile *DISPLAY = (void *)0xb4000000;
|
|
*DISPLAY = color;
|
|
}
|
|
|
|
static bool r61523_update(int x, int y, image_t const *fb, int flags)
|
|
{
|
|
if(fb->format != IMAGE_RGB565)
|
|
return false;
|
|
|
|
// TODO: r61523_update: DMA support
|
|
// unless VIDEO_UPDATE_FOREIGN_WORLD is set
|
|
(void)flags;
|
|
uint w = fb->width;
|
|
uint h = fb->height;
|
|
|
|
// TODO: r61523_update: sub-rectangle support
|
|
if(x != 0 || y != 0 || w != 320 || h != 528)
|
|
return false;
|
|
|
|
// TODO: r61523_update: stride support!
|
|
if(fb->stride != 320 * 2)
|
|
return false;
|
|
|
|
r61523_display(fb->data);
|
|
return true;
|
|
}
|
|
|
|
//---
|
|
// Driver metadata
|
|
//---
|
|
|
|
/* constructor() - determine which variant of screen we have */
|
|
static void constructor(void)
|
|
{
|
|
r61523_identify(NULL, NULL, &r61523_variant);
|
|
}
|
|
|
|
/* As far as I can tell there's no way to read the current window from the
|
|
controller so this driver is completely stateless for now. */
|
|
gint_driver_t drv_r61523 = {
|
|
.name = "R61523",
|
|
.constructor = constructor,
|
|
};
|
|
GINT_DECLARE_DRIVER(26, drv_r61523);
|
|
|
|
//---
|
|
// Video driver interface
|
|
//---
|
|
|
|
static video_mode_t r61523_modes[] = {
|
|
/* Standard full-screen full-color mode */
|
|
{ 320, 528, IMAGE_RGB565, -1 },
|
|
{ 0 }
|
|
};
|
|
|
|
video_interface_t r61523_video = {
|
|
.driver = &drv_r61523,
|
|
.modes = r61523_modes,
|
|
.mode_get = NULL, // TODO
|
|
.mode_set = NULL, // TODO
|
|
.brightness_min = 0, // TODO
|
|
.brightness_max = 0, // TODO
|
|
.brightness_set = NULL,
|
|
.update = r61523_update,
|
|
};
|
|
|
|
#endif /* GINT_HW_CP */
|