diff options
-rw-r--r-- | drivers/gdisp/ED060SC4/gdisp_lld.c | 606 |
1 files changed, 0 insertions, 606 deletions
diff --git a/drivers/gdisp/ED060SC4/gdisp_lld.c b/drivers/gdisp/ED060SC4/gdisp_lld.c deleted file mode 100644 index fcc03944..00000000 --- a/drivers/gdisp/ED060SC4/gdisp_lld.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/* Low-level E-ink panel driver routines for ED060SC4. */ - -#include "gfx.h" -#include "ed060sc4.h" - -#if GFX_USE_GDISP - -#include "gdisp/lld/emulation.c" - -/* ================================= - * Default configuration - * ================================= */ - -#ifndef GDISP_SCREEN_HEIGHT -# define GDISP_SCREEN_HEIGHT 600 -#endif - -#ifndef GDISP_SCREEN_WIDTH -# define GDISP_SCREEN_WIDTH 800 -#endif - -/* Number of pixels per byte */ -#ifndef EINK_PPB -# define EINK_PPB 4 -#endif - -/* Delay for generating clock pulses. - * Unit is approximate clock cycles of the CPU (0 to 15). - * This should be atleast 50 ns. - */ -#ifndef EINK_CLOCKDELAY -# define EINK_CLOCKDELAY 0 -#endif - -/* Width of one framebuffer block. - * Must be divisible by EINK_PPB and evenly divide GDISP_SCREEN_WIDTH. */ -#ifndef EINK_BLOCKWIDTH -# define EINK_BLOCKWIDTH 20 -#endif - -/* Height of one framebuffer block. - * Must evenly divide GDISP_SCREEN_WIDTH. */ -#ifndef EINK_BLOCKHEIGHT -# define EINK_BLOCKHEIGHT 20 -#endif - -/* Number of block buffers to use for framebuffer emulation. */ -#ifndef EINK_NUMBUFFERS -# define EINK_NUMBUFFERS 40 -#endif - -/* Do a "blinking" clear, i.e. clear to opposite polarity first. - * This reduces the image persistence. */ -#ifndef EINK_BLINKCLEAR -# define EINK_BLINKCLEAR TRUE -#endif - -/* Number of passes to use when clearing the display */ -#ifndef EINK_CLEARCOUNT -# define EINK_CLEARCOUNT 10 -#endif - -/* Number of passes to use when writing to the display */ -#ifndef EINK_WRITECOUNT -# define EINK_WRITECOUNT 4 -#endif - -/* ==================================== - * Lower level driver functions - * ==================================== */ - -#include "gdisp_lld_board.h" - -/** Delay between signal changes, to give time for IO pins to change state. */ -static inline void clockdelay() -{ - #if EINK_CLOCKDELAY & 1 - asm("nop"); - #endif - #if EINK_CLOCKDELAY & 2 - asm("nop"); - asm("nop"); - #endif - #if EINK_CLOCKDELAY & 4 - asm("nop"); - asm("nop"); - asm("nop"); - asm("nop"); - #endif - #if EINK_CLOCKDELAY & 8 - asm("nop"); - asm("nop"); - asm("nop"); - asm("nop"); - asm("nop"); - asm("nop"); - asm("nop"); - asm("nop"); - #endif -} - -/** Fast vertical clock pulse for gate driver, used during initializations */ -static void vclock_quick() -{ - setpin_ckv(TRUE); - eink_delay(1); - setpin_ckv(FALSE); - eink_delay(4); -} - -/** Horizontal clock pulse for clocking data into source driver */ -static void hclock() -{ - clockdelay(); - setpin_cl(TRUE); - clockdelay(); - setpin_cl(FALSE); -} - -/** Start a new vertical gate driver scan from top. - * Note: Does not clear any previous bits in the shift register, - * so you should always scan through the whole display before - * starting a new scan. - */ -static void vscan_start() -{ - setpin_gmode(TRUE); - vclock_quick(); - setpin_spv(FALSE); - vclock_quick(); - setpin_spv(TRUE); - vclock_quick(); -} - -/** Waveform for strobing a row of data onto the display. - * Attempts to minimize the leaking of color to other rows by having - * a long idle period after a medium-length strobe period. - */ -static void vscan_write() -{ - setpin_ckv(TRUE); - setpin_oe(TRUE); - eink_delay(5); - setpin_oe(FALSE); - setpin_ckv(FALSE); - eink_delay(200); -} - -/** Waveform used when clearing the display. Strobes a row of data to the - * screen, but does not mind some of it leaking to other rows. - */ -static void vscan_bulkwrite() -{ - setpin_ckv(TRUE); - eink_delay(20); - setpin_ckv(FALSE); - eink_delay(200); -} - -/** Waveform for skipping a vertical row without writing anything. - * Attempts to minimize the amount of change in any row. - */ -static void vscan_skip() -{ - setpin_ckv(TRUE); - eink_delay(1); - setpin_ckv(FALSE); - eink_delay(100); -} - -/** Stop the vertical scan. The significance of this escapes me, but it seems - * necessary or the next vertical scan may be corrupted. - */ -static void vscan_stop() -{ - setpin_gmode(FALSE); - vclock_quick(); - vclock_quick(); - vclock_quick(); - vclock_quick(); - vclock_quick(); -} - -/** Start updating the source driver data (from left to right). */ -static void hscan_start() -{ - /* Disable latching and output enable while we are modifying the row. */ - setpin_le(FALSE); - setpin_oe(FALSE); - - /* The start pulse should remain low for the duration of the row. */ - setpin_sph(FALSE); -} - -/** Write data to the horizontal row. */ -static void hscan_write(const uint8_t *data, int count) -{ - while (count--) - { - /* Set the next byte on the data pins */ - setpins_data(*data++); - - /* Give a clock pulse to the shift register */ - hclock(); - } -} - -/** Finish and transfer the row to the source drivers. - * Does not set the output enable, so the drivers are not yet active. */ -static void hscan_stop() -{ - /* End the scan */ - setpin_sph(TRUE); - hclock(); - - /* Latch the new data */ - setpin_le(TRUE); - clockdelay(); - setpin_le(FALSE); -} - -/** Turn on the power to the E-Ink panel, observing proper power sequencing. */ -static void power_on() -{ - unsigned i; - - /* First the digital power supply and signal levels. */ - setpower_vdd(TRUE); - setpin_le(FALSE); - setpin_oe(FALSE); - setpin_cl(FALSE); - setpin_sph(TRUE); - setpins_data(0); - setpin_ckv(FALSE); - setpin_gmode(FALSE); - setpin_spv(TRUE); - - /* Min. 100 microsecond delay after digital supply */ - gfxSleepMicroseconds(100); - - /* Then negative voltages and min. 1000 microsecond delay. */ - setpower_vneg(TRUE); - gfxSleepMicroseconds(1000); - - /* Finally the positive voltages. */ - setpower_vpos(TRUE); - - /* Clear the vscan shift register */ - vscan_start(); - for (i = 0; i < GDISP_SCREEN_HEIGHT; i++) - vclock_quick(); - vscan_stop(); -} - -/** Turn off the power, observing proper power sequencing. */ -static void power_off() -{ - /* First the high voltages */ - setpower_vpos(FALSE); - setpower_vneg(FALSE); - - /* Wait for any capacitors to drain */ - gfxSleepMilliseconds(100); - - /* Then put all signals and digital supply to ground. */ - setpin_le(FALSE); - setpin_oe(FALSE); - setpin_cl(FALSE); - setpin_sph(FALSE); - setpins_data(0); - setpin_ckv(FALSE); - setpin_gmode(FALSE); - setpin_spv(FALSE); - setpower_vdd(FALSE); -} - -/* ==================================== - * Framebuffer emulation layer - * ==================================== */ - -#if EINK_PPB == 4 -#define PIXELMASK 3 -#define PIXEL_WHITE 2 -#define PIXEL_BLACK 1 -#define BYTE_WHITE 0xAA -#define BYTE_BLACK 0x55 -#else -#error Unsupported EINK_PPB value. -#endif - -#if GDISP_SCREEN_HEIGHT % EINK_BLOCKHEIGHT != 0 -#error GDISP_SCREEN_HEIGHT must be evenly divisible by EINK_BLOCKHEIGHT -#endif - -#if GDISP_SCREEN_WIDTH % EINK_BLOCKWIDTH != 0 -#error GDISP_SCREEN_WIDTH must be evenly divisible by EINK_BLOCKWIDTH -#endif - -#if EINK_BLOCKWIDTH % EINK_PPB != 0 -#error EINK_BLOCKWIDTH must be evenly divisible by EINK_PPB -#endif - -#if EINK_NUMBUFFERS > 254 -#error EINK_NUMBUFFERS must be at most 254. -#endif - -#define BLOCKS_Y (GDISP_SCREEN_HEIGHT / EINK_BLOCKHEIGHT) -#define BLOCKS_X (GDISP_SCREEN_WIDTH / EINK_BLOCKWIDTH) -#define WIDTH_BYTES (EINK_BLOCKWIDTH / EINK_PPB) - -/* Buffers that store the data for a small area of the display. */ -typedef struct { - uint8_t data[EINK_BLOCKHEIGHT][WIDTH_BYTES]; -} block_t; - -static uint8_t g_next_block; /* Index of the next free block buffer. */ -static block_t g_blocks[EINK_NUMBUFFERS]; - -/* Map that stores the buffers associated to each area of the display. - * Value of 0 means that the block is not allocated. - * Other values are the index in g_blocks + 1. - */ -static uint8_t g_blockmap[BLOCKS_Y][BLOCKS_X]; - -/** Check if the row contains any allocated blocks. */ -static bool_t blocks_on_row(unsigned by) -{ - unsigned bx; - for (bx = 0; bx < BLOCKS_X; bx++) - { - if (g_blockmap[by][bx] != 0) - { - return TRUE; - } - } - return FALSE; -} - -/** Write out a block row. */ -static void write_block_row(unsigned by) -{ - unsigned bx, dy, dx; - for (dy = 0; dy < EINK_BLOCKHEIGHT; dy++) - { - hscan_start(); - for (bx = 0; bx < BLOCKS_X; bx++) - { - if (g_blockmap[by][bx] == 0) - { - for (dx = 0; dx < WIDTH_BYTES; dx++) - { - const uint8_t dummy = 0; - hscan_write(&dummy, 1); - } - } - else - { - block_t *block = &g_blocks[g_blockmap[by][bx] - 1]; - hscan_write(&block->data[dy][0], WIDTH_BYTES); - } - } - hscan_stop(); - - vscan_write(); - } -} - -/** Clear the block map, i.e. deallocate all blocks */ -static void clear_block_map() -{ - unsigned bx, by; - for (by = 0; by < BLOCKS_Y; by++) - { - for (bx = 0; bx < BLOCKS_X; bx++) - { - g_blockmap[by][bx] = 0; - } - } - - g_next_block = 0; -} - -/** Flush all the buffered rows to display. */ -static void flush_buffers() -{ - unsigned by, dy, i; - - for (i = 0; i < EINK_WRITECOUNT; i++) - { - vscan_start(); - - for (by = 0; by < BLOCKS_Y; by++) - { - if (!blocks_on_row(by)) - { - /* Skip the whole row of blocks. */ - for (dy = 0; dy < EINK_BLOCKHEIGHT; dy++) - { - vscan_skip(); - } - } - else - { - /* Write out the blocks. */ - write_block_row(by); - } - } - - vscan_stop(); - } - - clear_block_map(); -} - -/** Initialize a newly allocated block. */ -static void zero_block(block_t *block) -{ - unsigned dx, dy; - for (dy = 0; dy < EINK_BLOCKHEIGHT; dy++) - { - for (dx = 0; dx < WIDTH_BYTES; dx++) - { - block->data[dy][dx] = 0; - } - } -} - -/** Allocate a buffer - * Automatically flushes if all buffers are full. */ -static block_t *alloc_buffer(unsigned bx, unsigned by) -{ - block_t *result; - if (g_blockmap[by][bx] == 0) - { - if (g_next_block >= EINK_NUMBUFFERS) - { - flush_buffers(); - } - - result = &g_blocks[g_next_block]; - g_blockmap[by][bx] = g_next_block + 1; - g_next_block++; - zero_block(result); - return result; - } - else - { - result = &g_blocks[g_blockmap[by][bx] - 1]; - return result; - } -} - -/* =============================== - * Public functions - * =============================== */ - -bool_t gdisp_lld_init(void) -{ - init_board(); - - /* Make sure that all the pins are in "off" state. - * Having any pin high could cause voltage leaking to the - * display, which in turn causes the image to leak slowly away. - */ - power_off(); - - clear_block_map(); - - /* Initialize the global GDISP structure */ - GDISP.Width = GDISP_SCREEN_WIDTH; - GDISP.Height = GDISP_SCREEN_HEIGHT; - GDISP.Orientation = GDISP_ROTATE_0; - GDISP.Powermode = powerOff; - GDISP.Backlight = 0; - GDISP.Contrast = 0; - #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP - GDISP.clipx0 = 0; - GDISP.clipy0 = 0; - GDISP.clipx1 = GDISP.Width; - GDISP.clipy1 = GDISP.Height; - #endif - - return TRUE; -} - -void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) -{ - block_t *block; - uint8_t byte; - unsigned bx, by, dx, dy; - uint8_t bitpos; - - bx = x / EINK_BLOCKWIDTH; - by = y / EINK_BLOCKHEIGHT; - dx = x % EINK_BLOCKWIDTH; - dy = y % EINK_BLOCKHEIGHT; - - if (bx < 0 || bx >= BLOCKS_X || by < 0 || by >= BLOCKS_Y) - return; - - block = alloc_buffer(bx, by); - - bitpos = (6 - 2 * (dx % EINK_PPB)); - byte = block->data[dy][dx / EINK_PPB]; - byte &= ~(PIXELMASK << bitpos); - if (color) - { - byte |= PIXEL_WHITE << bitpos; - } - else - { - byte |= PIXEL_BLACK << bitpos; - } - block->data[dy][dx / EINK_PPB] = byte; -} - -#if !GDISP_NEED_CONTROL -#error You must enable GDISP_NEED_CONTROL for the E-Ink driver. -#endif - -void gdisp_lld_control(unsigned what, void *value) { - gdisp_powermode_t newmode; - - switch(what) - { - case GDISP_CONTROL_POWER: - newmode = (gdisp_powermode_t)value; - - if (GDISP.Powermode == newmode) - return; - - if (newmode == powerOn) - { - power_on(); - } - else - { - flush_buffers(); - power_off(); - } - GDISP.Powermode = newmode; - break; - - case GDISP_CONTROL_FLUSH: - flush_buffers(); - break; - } -} - -/* =============================== - * Accelerated routines - * =============================== */ - -#if GDISP_HARDWARE_CLEARS - -static void subclear(color_t color) -{ - unsigned x, y; - uint8_t byte; - - hscan_start(); - byte = color ? BYTE_WHITE : BYTE_BLACK; - for (x = 0; x < GDISP_SCREEN_WIDTH; x++) - { - hscan_write(&byte, 1); - } - hscan_stop(); - - setpin_oe(TRUE); - vscan_start(); - for (y = 0; y < GDISP_SCREEN_HEIGHT; y++) - { - vscan_bulkwrite(); - } - vscan_stop(); - setpin_oe(FALSE); -} - -void gdisp_lld_clear(color_t color) -{ - unsigned i; - clear_block_map(); - - if (EINK_BLINKCLEAR) - { - subclear(!color); - gfxSleepMilliseconds(50); - } - - for (i = 0; i < EINK_CLEARCOUNT; i++) - { - subclear(color); - gfxSleepMilliseconds(10); - } - -} -#endif - -#endif |