From de7a132fbbeb8055645b111a21b680ea35c4cb83 Mon Sep 17 00:00:00 2001 From: mmdolze Date: Fri, 21 Jan 2011 06:59:37 +0000 Subject: [PATCH] Rewrote the t6963 driver: - Limit the maximum size to 40x16 (240x128 pixel) and change the RAM locations to make use of 8 KB only. (If one's display is larger and has more memory this may have to be changed.) - Drive control lines using defines from lpt-port.h without using a function. - Use timing.h for delays (which are now optional). - Remove any use of the graphic area and mode settings, except for optionally clearing of graphic memory on start-up. - Remove any cursor related stuff. - Handle displays correctly which width (in pixels) is not a multiple of the cellwidth (e.g. 128x64). - Remove double buffering (display updates are already really fast). - Update text and font memory using AUTO WRITE function. - Remove the bignum stub. Now at least something is displayed. - Fix character offset for bar graphs. - Make local functions static and inline the most commonly used read / write functions. --- ChangeLog | 1 + LCDd.conf | 12 +- docs/lcdproc-user/drivers/t6963.docbook | 56 ++- server/drivers/t6963.c | 451 ++++++++++-------------- server/drivers/t6963.h | 62 ++-- 5 files changed, 260 insertions(+), 322 deletions(-) diff --git a/ChangeLog b/ChangeLog index c6a3b7f..3840d8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,7 @@ v.0.5dev (ongoing development) * All drivers: Refactor CGmode enum into lcd.h * hd44780: New (possible faster) screen update algorithm + Add an option to start LCDd with screen rotation disabled (M. Zanetti) + * Rewrite of the t6963 driver fixing several problems (see commit note). v0.5.4 * Update driver includes and LDFLAGS (fixed glk and bayrad binding error) diff --git a/LCDd.conf b/LCDd.conf index d04114c..6315c9f 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -1084,11 +1084,15 @@ Size=20x6 # port to use [default: 0x378; legal: 0x200 - 0x400] Port=0x378 -# Is ECP mode on? [default: yes; legal: yes, no] -#ECPlpt=yes +# Use LPT port in bi-directional mode. This should work on most LPT port and +# is required for proper timing! [default: yes; legal: yes, no] +#bidirectional=yes -# Use graphics? [default: no; legal: yes, no] -#graphic=no +# Insert additional delays into reads / writes. [default: no; legal: yes, no] +#delayBus=no + +# Clear graphic memory on start-up. [default: no; legal: yes, no] +#ClearGraphic=no diff --git a/docs/lcdproc-user/drivers/t6963.docbook b/docs/lcdproc-user/drivers/t6963.docbook index 583bfa9..a47cc98 100644 --- a/docs/lcdproc-user/drivers/t6963.docbook +++ b/docs/lcdproc-user/drivers/t6963.docbook @@ -3,18 +3,19 @@ This section talks about using LCDproc with LCD displays that use the -T6963 chipset from Toshiba. Usually, this chipset is used on big character LCD +T6963 chipset from Toshiba. Usually, this chipset is used on big graphical LCD displays that can often act as a screen. The driver uses the text mode of the chipset with a custom font loaded which -resembles the characters from Code Page 437. +resembles the characters from Code Page 437. If your display has a 'Font Select' +pin it must be wired to use the 6x8 font. Only displays in 'Single Scan' configurations are supported. Displays configured -as 'Dual Scan' are currently not supported. Those use onboard memory differently. +as 'Dual Scan' are currently not supported. Those use on-board memory differently. Check the datasheet of your display! @@ -25,7 +26,7 @@ Check the datasheet of your display! Displays using T6963 chipset come in a variety of pin-outs and power configurations. They usually require negative voltage for contrast and some displays have - a negative voltage generator onboard. Therefore the wiring table below does + a negative voltage generator on-board. Therefore the wiring table below does only list signal names, not LCD pins. Be sure to get the correct datasheet for your display and identify the pins to use! @@ -92,6 +93,13 @@ Check the datasheet of your display! /RD * + + + + VDD + FS + * + @@ -122,7 +130,11 @@ can be done by specifying "--enable-drivers=all" or by Size = ¶meters.size; - set display size [default: 20x6] + Set display size in characters [default: 20x6]. Do set + it as close as possible to the pixel size of the display (based on a + 6x8 font). Otherwise characters will repeat on the + right of a line. Example: 21x8 on a 128x64 + display. @@ -142,31 +154,47 @@ can be done by specifying "--enable-drivers=all" or by - ECPlpt = ¶meters.yesdefno; + bidirectional = ¶meters.yesdefno; - Is ECP mode on? [default: yes; legal: yes, no] + Use LPT port in bi-directional mode. [default: yes; + legal: yes, no] - Despite its name, this driver does not use ECP mode of parallel port. This - option turns bi-directional mode of the port on or off. Leave this on - unless you experience problems. + Most LPT ports can be used in bi-directional mode. It is required for + proper timing of the display. Leave this on unless you experience problems. - graphic = ¶meters.yesnodef; + ClearGraphic = ¶meters.yesnodef; - Use graphic? [default: no; legal: yes, no] + Clear graphic memory on start-up [default: no; + legal: yes, no]. The T6963 has a + graphic and a text area which can be combined using several modes. This + driver uses default OR-mode. Usually the graphic area is empty after + power-on but if you see random garbage overlaying the text this option + may be enabled. + + + + + + delayBus = ¶meters.yesnodef; + + - Enables / disables drawing of the content of the graphic RAM. This is - currently a no-op as the driver does not make use of the graphic RAM. + Use additional delay in read / write operations. [default: no; + legal: yes, no]. The display can + execute operations very fast. As the driver implements busy checking no + additional delays are required. But if you experience problems you may + try to slow down communication by enabling this setting. diff --git a/server/drivers/t6963.c b/server/drivers/t6963.c index d9e0d88..3c07858 100644 --- a/server/drivers/t6963.c +++ b/server/drivers/t6963.c @@ -11,23 +11,21 @@ * 14 (Autofeed) -------- /CE * 16 (Init) ------------ C/D * 17 (Slct) ------------ /RD + * + 5V --- FS (6x8 font) *\endverbatim - * - * \todo This driver uses writes to port 0x80 to implement delay. Convert it - * to use \c timing.h. */ /*- - * Base driver module for Toshiba T6963 based LCD displays. ver 2.2 + * Base driver module for Toshiba T6963 based LCD displays. * - * Parts of this file are based on the kernel driver by A - * lexander Frink + * Parts of this file are based on the kernel driver by + * Alexander Frink * + * Copyright (c) 2001 Manuel Stahl + * 2011 Markus Dolze * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. - * - * Copyright (c) 2001 Manuel Stahl */ #include @@ -44,24 +42,30 @@ #include "t6963.h" #include "t6963_font.h" +#include "timing.h" #include "report.h" #include "lcd_lib.h" #include "port.h" +#include "lpt-port.h" +/* Define the wiring for display */ +#define nWR nSTRB +#define nRD nSEL +#define nCE nLF +#define T_CMD INIT +#define T_DATA 0x00 /** private data for the \c t6963 driver */ typedef struct t6963_private_data { u16 port; - u16 display_mode; u8 *display_buffer1; - u8 *display_buffer2; int width; int height; int cellwidth; int cellheight; short bidirectLPT; - short graphicON; + short delayBus; } PrivateData; /* Vars for the server core */ @@ -77,7 +81,7 @@ MODULE_EXPORT int t6963_init(Driver * drvthis) { PrivateData *p; - int w, h, i, ecp_input; + int w, h; char size[200] = DEFAULT_SIZE; debug(RPT_INFO, "T6963: init(%p)", drvthis); @@ -90,7 +94,6 @@ t6963_init(Driver * drvthis) return -1; /* initialize private data */ - p->display_mode = 0; p->cellwidth = DEFAULT_CELL_WIDTH; p->cellheight = DEFAULT_CELL_HEIGHT; @@ -102,8 +105,8 @@ t6963_init(Driver * drvthis) strncpy(size, drvthis->config_get_string(drvthis->name, "Size", 0, DEFAULT_SIZE), sizeof(size)); size[sizeof(size) - 1] = '\0'; if ((sscanf(size, "%dx%d", &w, &h) != 2) - || (w <= 0) || (w > LCD_MAX_WIDTH) - || (h <= 0) || (h > LCD_MAX_HEIGHT)) { + || (w <= 0) || (w > T6963_MAX_WIDTH) + || (h <= 0) || (h > T6963_MAX_HEIGHT)) { report(RPT_WARNING, "%s: cannot read Size: %s, Using default %s", drvthis->name, size, DEFAULT_SIZE); sscanf(DEFAULT_SIZE, "%dx%d", &w, &h); @@ -119,11 +122,10 @@ t6963_init(Driver * drvthis) drvthis->name, DEFAULT_PORT); } - /* Is ECP mode on? Default: yes */ - p->bidirectLPT = drvthis->config_get_bool(drvthis->name, "ECPlpt", 0, 1); - /* Use graphic? Default: no */ - p->graphicON = drvthis->config_get_bool(drvthis->name, "graphic", 0, 0); - + /* Use bi-directional mode of LPT port? Default: yes */ + p->bidirectLPT = drvthis->config_get_bool(drvthis->name, "bidirectional", 0, 1); + /* Additional delay necessary? Default: no */ + p->delayBus = drvthis->config_get_bool(drvthis->name, "delaybus", 0, 0); /* Get permission to parallel port */ debug(RPT_DEBUG, "T6963: Getting permission to parallel port %d...", p->port); @@ -132,98 +134,70 @@ t6963_init(Driver * drvthis) drvthis->name, p->port, strerror(errno)); return -1; } - if (port_access(0x80)) { - report(RPT_ERR, "%s: no permission to port 0x80: (%s)", - drvthis->name, strerror(errno)); + + /* Set up timing */ + if (timing_init() == -1) { + report(RPT_ERR, "%s: timing_init() failed (%s)", drvthis->name, strerror(errno)); return -1; } - debug(RPT_DEBUG, "T6963: cool, got 'em!"); - - /* Allocate memory for double buffering */ - debug(RPT_DEBUG, "T6963: Allocating double buffering..."); + /* Allocate and clear memory for frame buffer */ p->display_buffer1 = malloc(p->width * p->height); - p->display_buffer2 = malloc(p->width * p->height); - if ((p->display_buffer1 == NULL) || (p->display_buffer2 == NULL)) { - report(RPT_ERR, "%s: No memory for double buffering", drvthis->name); + if (p->display_buffer1 == NULL) { + report(RPT_ERR, "%s: No memory for frame buffer", drvthis->name); t6963_close(drvthis); return -1; } - - /* Clear front and back buffer */ memset(p->display_buffer1, ' ', p->width * p->height); - memset(p->display_buffer2, ' ', p->width * p->height); - debug(RPT_DEBUG, "T6963: done!"); /* ------------------- I N I T I A L I Z A T I O N --------------- */ - debug(RPT_DEBUG, "T6963: Sending init to display..."); - - t6963_low_set_control(drvthis, 1, 1, 1, 1); - T6963_DATAOUT(p->port); /* configure port for output */ - - /* Test if bidirectional mode is working */ if (p->bidirectLPT == 1) { - debug(RPT_WARNING, "T6963: Testing bidirectional mode..."); - i = 0; - ecp_input = 0; - T6963_DATAIN(p->port); - /* - * Try to read status from display. STA0 and STA1 must both - * be set 1 (= 0x03). - */ - do { - i++; - t6963_low_set_control(drvthis, 1, 1, 1, 1); - t6963_low_set_control(drvthis, 1, 0, 1, 0); - t6963_low_set_control(drvthis, 1, 0, 1, 0); - t6963_low_set_control(drvthis, 1, 0, 1, 0); - ecp_input = port_in(T6963_DATA_PORT(p->port)); - t6963_low_set_control(drvthis, 1, 1, 1, 1); - } while (i < 100 && (ecp_input & 0x03) != 0x03); - T6963_DATAOUT(p->port); - if (i >= 100) { - debug(RPT_WARNING, "T6963: Bidirectional mode not working!\n -> is now disabled (STA0: %i, STA1: %i)", ecp_input & 1, ecp_input & 2); + debug(RPT_INFO, "T6963: Testing bidirectional mode..."); + if (t6963_low_dsp_ready(drvthis, 0x03) == -1) { + report(RPT_WARNING, "T6963: Bidirectional mode not working (now disabled)"); p->bidirectLPT = 0; } - else - debug(RPT_WARNING, "T6963: working!"); + else { + debug(RPT_INFO, "T6963: working!"); + } } - debug(RPT_DEBUG, "T6963: set graphic/text home adress and area"); + debug(RPT_INFO, "T6963: Sending init to display..."); + /* + * Note: I assume display has a width that is a multiple of 8 (e.g. + * 128, 160, 240, etc). If this is not a multiple of the cellwidth + * the display has excess pixels on the right. In this case make the + * controller think we have one column more which will be filled with + * a space on flush. This avoids incorrect excess pixels on the right. + */ + w = p->width; + if ((p->width * p->cellwidth) % 8) + w++; /* Set text and graphic addresses */ - t6963_low_command_word(drvthis, SET_GRAPHIC_HOME_ADDRESS, ATTRIB_BASE); - t6963_low_command_word(drvthis, SET_GRAPHIC_AREA, p->width); + t6963_low_command_word(drvthis, SET_GRAPHIC_HOME_ADDRESS, GRAPHIC_BASE); + t6963_low_command_word(drvthis, SET_GRAPHIC_AREA, w); t6963_low_command_word(drvthis, SET_TEXT_HOME_ADDRESS, TEXT_BASE); - t6963_low_command_word(drvthis, SET_TEXT_AREA, p->width); + t6963_low_command_word(drvthis, SET_TEXT_AREA, w); /* Enable external character generator */ t6963_low_command(drvthis, SET_MODE | OR_MODE | EXTERNAL_CG); /* Set address of CG RAM address start */ - t6963_low_command_2_bytes(drvthis, SET_OFFSET_REGISTER, CHARGEN_BASE >> 11, 0); - - /* Set cursor to 8 lines and place it at position (0,0) */ - t6963_low_command(drvthis, SET_CURSOR_PATTERN | 7); - t6963_low_command_2_bytes(drvthis, SET_CURSOR_POINTER, 0, 0); + t6963_low_command_word(drvthis, SET_OFFSET_REGISTER, CHARGEN_BASE >> 11); /* Load font data */ t6963_set_nchar(drvthis, 0, fontdata_6x8, 256); - /* Turn on text and optionally graphics, turn off cursor */ - t6963_low_enable_mode(drvthis, TEXT_ON); - if (p->graphicON == 0) - t6963_low_disable_mode(drvthis, GRAPHIC_ON); - else - t6963_low_enable_mode(drvthis, GRAPHIC_ON); - t6963_low_disable_mode(drvthis, CURSOR_ON); - t6963_low_disable_mode(drvthis, BLINK_ON); - /* Clear display */ t6963_clear(drvthis); - t6963_graphic_clear(drvthis, 0, 0, p->width, p->cellheight * p->height); + if (drvthis->config_get_bool(drvthis->name, "ClearGraphic", 0, 0) == 1) + t6963_graphic_clear(drvthis); t6963_flush(drvthis); - report(RPT_DEBUG, "%s: init() done", drvthis->name); + /* Turn on display, text on, graphics off, cursor off */ + t6963_low_command(drvthis, SET_DISPLAY_MODE | TEXT_ON); + + debug(RPT_INFO, "%s: init() done", drvthis->name); return 0; /* return success */ } @@ -239,16 +213,11 @@ t6963_close(Driver * drvthis) debug(RPT_INFO, "Shutting down!"); if (p != NULL) { - t6963_low_disable_mode(drvthis, BLINK_ON); - port_deny_multiple(p->port, 3); if (p->display_buffer1 != NULL) free(p->display_buffer1); - if (p->display_buffer2 != NULL) - free(p->display_buffer2); - free(p); } drvthis->store_private_ptr(drvthis, NULL); @@ -292,23 +261,29 @@ t6963_clear(Driver * drvthis) /** * Clears the specified area of graphic RAM. * \param drvthis Pointer to driver structure. - * \param x1 Start column (character!) - * \param y1 Start line (pixel row) - * \param x2 End column (character!) - * \param y2 End line (pixel row) */ -void -t6963_graphic_clear(Driver * drvthis, int x1, int y1, int x2, int y2) +static void +t6963_graphic_clear(Driver * drvthis) { PrivateData *p = drvthis->private_data; - int x; + int i, num; - debug(RPT_DEBUG, "Clearing Graphic %d bytes", (x2 - x1) * (y2 - y1)); - for (; y1 < y2; y1++) { - t6963_low_command_word(drvthis, SET_ADDRESS_POINTER, ATTRIB_BASE + y1 * p->width + x1); - for (x = x1; x < x2; x++) - t6963_low_command_byte(drvthis, DATA_WRITE_INC, 0); - } + /* + * If width * cellwidth is not a multiple of 8 we have an additional + * column on the right. + */ + if ((p->width * p->cellwidth) % 8) + num = (p->width + 1) * p->height * p->cellheight; + else + num = p->width * p->height * p->cellheight; + + debug(RPT_DEBUG, "Clearing Graphic %d bytes", num); + + t6963_low_command_word(drvthis, SET_ADDRESS_POINTER, GRAPHIC_BASE); + t6963_low_command(drvthis, AUTO_WRITE); + for (i = 0; i < num; i++) + t6963_low_auto_write(drvthis, 0); + t6963_low_command(drvthis, AUTO_RESET); } /** @@ -318,21 +293,33 @@ MODULE_EXPORT void t6963_flush(Driver * drvthis) { PrivateData *p = drvthis->private_data; - int i; + int r, c, line_address; debug(RPT_DEBUG, "Flushing %d x %d", p->width, p->height); - for (i = 0; i < (p->width * p->height); i++) { - //debug(RPT_DEBUG, "%i%|i", p->display_buffer1[i], p->display_buffer2[i]); - if (p->display_buffer1[i] != p->display_buffer2[i]) { - t6963_low_command_word(drvthis, SET_ADDRESS_POINTER, TEXT_BASE + i); - t6963_low_command_byte(drvthis, DATA_WRITE, p->display_buffer1[i]); + t6963_low_command_word(drvthis, SET_ADDRESS_POINTER, TEXT_BASE); + t6963_low_command(drvthis, AUTO_WRITE); + + /* + * Note: There used to be a double buffering algorithm here that + * transferred only changes to the display. However, even complete + * screen updates are so fast (typically 5 ms on my 128x64) that I + * rewrote it not to use partial updates but use fast 'auto write' + * commands instead. + */ + for (r = 0; r < p->height; r++) { + line_address = r * p->width; + for (c = 0; c < p->width; c++) { + t6963_low_auto_write(drvthis, p->display_buffer1[line_address + c]); } + /* + * If width * cellwidth is not a multiple of 8 write a space + * into an additional column on the right. + */ + if ((p->width * p->cellwidth) % 8) + t6963_low_auto_write(drvthis, ' '); } - debug(RPT_DEBUG, "Done"); - /* Reverse the two buffers and clear buffer1 */ - t6963_swap_buffers(drvthis); - t6963_clear(drvthis); + t6963_low_command(drvthis, AUTO_RESET); } /** @@ -370,15 +357,6 @@ t6963_chr(Driver * drvthis, int x, int y, char c) p->display_buffer1[(y * p->width) + x] = c; } -/** - * API: Draws a big (4-row) number. (NOT IMPLEMENTED) - */ -MODULE_EXPORT void -t6963_num(Driver * drvthis, int x, int num) -{ - //printf("BigNum(%i, %i)\n", x, num); -} - /** * Changes the font data of character n. * \param drvthis Pointer to driver structure. @@ -386,7 +364,7 @@ t6963_num(Driver * drvthis, int x, int num) * \param dat Data (must consist of num*cellwidth*cellheight bytes). * \param num Number of characters stored in data. */ -void +static void t6963_set_nchar(Driver * drvthis, int n, unsigned char *dat, int num) { PrivateData *p = drvthis->private_data; @@ -399,6 +377,7 @@ t6963_set_nchar(Driver * drvthis, int n, unsigned char *dat, int num) return; t6963_low_command_word(drvthis, SET_ADDRESS_POINTER, CHARGEN_BASE + n * 8); + t6963_low_command(drvthis, AUTO_WRITE); for (row = 0; row < p->cellheight * num; row++) { letter = 0; /* Accumulate data for one pixel row */ @@ -407,8 +386,9 @@ t6963_set_nchar(Driver * drvthis, int n, unsigned char *dat, int num) letter |= (dat[(row * p->cellwidth) + col] > 0); } - t6963_low_command_byte(drvthis, DATA_WRITE_INC, letter); + t6963_low_auto_write(drvthis, letter); } + t6963_low_command(drvthis, AUTO_RESET); } /** @@ -428,7 +408,7 @@ t6963_vbar(Driver * drvthis, int x, int y, int len, int promille, int options) { PrivateData *p = drvthis->private_data; - lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 212); + lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 211); } /** @@ -439,7 +419,7 @@ t6963_hbar(Driver * drvthis, int x, int y, int len, int promille, int options) { PrivateData *p = drvthis->private_data; - lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 220); + lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 219); } /** @@ -466,79 +446,77 @@ t6963_icon(Driver * drvthis, int x, int y, int icon) } -/*---------------------- internal functions -------------------------------*/ - -/** - * Set control lines for the display. State values are \c 0 or \c 1 and - * represent the real level as it arrives at the display. This function - * takes into account the hardware inversion of control lines. - * - * \param drvthis Pointer to driver structure. - * \param wr State of the /RW pin (0 or 1). - * \param ce State of the /CE pin (0 or 1). - * \param cd State of the C/D pin (0 or 1). - * \param rd State of the /RD pin (0 or 1). - */ -void -t6963_low_set_control(Driver * drvthis, char wr, char ce, char cd, char rd) -{ - PrivateData *p = drvthis->private_data; - - unsigned char status = port_in(T6963_CONTROL_PORT(p->port)); - if (wr == 1) /* WR = HI */ - status &= 0xfe; - else if (wr == 0) - status |= 0x01; - if (ce == 1) /* CE = HI */ - status &= 0xfd; - else if (ce == 0) - status |= 0x02; - if (cd == 0) /* CD = HI */ - status &= 0xfb; - else if (cd == 1) - status |= 0x04; - if (rd == 1) /* RD = HI */ - status &= 0xf7; - else if (rd == 0) - status |= 0x08; - port_out(T6963_CONTROL_PORT(p->port), status); -} - /** * Check display status. * \param drvthis Pointer to driver structure. + * \param sta Bitmap of expected STA flags + * \return 0 on success, -1 if ready could not be read */ -void -t6963_low_dsp_ready(Driver * drvthis) +static inline int +t6963_low_dsp_ready(Driver * drvthis, u8 sta) { PrivateData *p = drvthis->private_data; - int i = 0; - int input; + int portcontrol = 0; - T6963_DATAIN(p->port); if (p->bidirectLPT == 1) { - do { - i++; - t6963_low_set_control(drvthis, 1, 1, 1, 1); - /* - * Output control lines 3 times (hold value for tacc - * max 150ns - */ - t6963_low_set_control(drvthis, 1, 0, 1, 0); - t6963_low_set_control(drvthis, 1, 0, 1, 0); - t6963_low_set_control(drvthis, 1, 0, 1, 0); - input = port_in(T6963_DATA_PORT(p->port)); - t6963_low_set_control(drvthis, 1, 1, 1, 1); + int val; + int loop = 0; - } while (i < 100 && (input & 3) != 3); + do { + portcontrol = T_CMD | nWR | nRD | nCE; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + /* lower nRD, nCE, set bi-directional mode */ + portcontrol = T_CMD | nWR | ENBI; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + /* possible wait required here: tACC = 150 ns max */ + if (p->delayBus) + timing_uPause(1); + val = port_in(T6963_DATA_PORT(p->port)); + portcontrol = T_CMD | nWR | nRD | nCE; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + loop++; + if (loop == 100) { + report(RPT_WARNING, "Ready check failed, STA=0x%02x", val); + return -1; + } + } while ((val & sta) != sta); } else { - t6963_low_set_control(drvthis, 1, 1, 1, 1); - t6963_low_set_control(drvthis, 1, 0, 1, 0); - t6963_low_set_control(drvthis, 1, 1, 1, 1); - port_out(0x80, 0x00); /* wait 1ms */ + portcontrol = T_CMD | nWR | nRD | nCE; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + portcontrol = T_CMD | nWR; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + timing_uPause(150); + portcontrol = T_CMD | nWR | nRD | nCE; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); } - T6963_DATAOUT(p->port); + + return 0; +} + + +/** + * Write a single command / or byte to the parallel port. + * \param drvthis Pointer to driver structure. + * \param type Command or Data + * \param byte Data byte. + */ +static inline void +t6963_low_send(Driver * drvthis, u8 type, u8 byte) +{ + PrivateData *p = drvthis->private_data; + int portcontrol = 0; + + portcontrol = type | nWR | nRD | nCE; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + port_out(T6963_DATA_PORT(p->port), byte); + portcontrol = type | nRD; /* lower nWR, nCE */ + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); + /* possible wait required here: tWR */ + if (p->delayBus) + timing_uPause(1); + portcontrol = type | nWR | nRD | nCE; + port_out(T6963_CONTROL_PORT(p->port), portcontrol ^ OUTMASK); } @@ -547,17 +525,11 @@ t6963_low_dsp_ready(Driver * drvthis) * \param drvthis Pointer to driver structure. * \param byte Data byte. */ -void +static void t6963_low_data(Driver * drvthis, u8 byte) { - PrivateData *p = drvthis->private_data; - - t6963_low_dsp_ready(drvthis); /* Check if display ready */ - t6963_low_set_control(drvthis, 1, 1, 0, 1); /* CD down (data) */ - t6963_low_set_control(drvthis, 0, 0, 0, 1); /* CE & WR down */ - port_out(T6963_DATA_PORT(p->port), byte); /* present data */ - port_out(0x80, 0x00); /* short wait */ - t6963_low_set_control(drvthis, 1, 1, 1, 1); /* all up again */ + t6963_low_dsp_ready(drvthis, STA0|STA1); + t6963_low_send(drvthis, T_DATA, byte); } /** @@ -565,17 +537,24 @@ t6963_low_data(Driver * drvthis, u8 byte) * \param drvthis Pointer to driver structure. * \param byte Command byte. */ -void +static void t6963_low_command(Driver * drvthis, u8 byte) { - PrivateData *p = drvthis->private_data; + t6963_low_dsp_ready(drvthis, STA0|STA1); + t6963_low_send(drvthis, T_CMD, byte); +} - t6963_low_dsp_ready(drvthis); /* Check if display ready */ - t6963_low_set_control(drvthis, 1, 1, 1, 1); /* CD up (command) */ - t6963_low_set_control(drvthis, 0, 0, 1, 1); /* CE & WR down */ - port_out(T6963_DATA_PORT(p->port), byte); /* present data */ - port_out(0x80, 0x00); /* short wait */ - t6963_low_set_control(drvthis, 1, 1, 0, 1); /* CE & WR up, CD down */ +/** + * Write one byte of data to display in AUTO mode (needs a different ready + * check). + * \param drvthis Pointer to driver structure. + * \param byte Data byte. + */ +static void +t6963_low_auto_write(Driver *drvthis, u8 byte) +{ + t6963_low_dsp_ready(drvthis, STA3); + t6963_low_send(drvthis, T_DATA, byte); } /** @@ -584,28 +563,13 @@ t6963_low_command(Driver * drvthis, u8 byte) * \param cmd Command byte. * \param byte Data value. */ -void +static void t6963_low_command_byte(Driver * drvthis, u8 cmd, u8 byte) { t6963_low_data(drvthis, byte); t6963_low_command(drvthis, cmd); } -/** - * Send two bytes of data followed by one command byte to the display. - * \param drvthis Pointer to driver structure. - * \param cmd Command byte. - * \param byte1 Data value (byte 1) - * \param byte2 Data value (byte 2) - */ -void -t6963_low_command_2_bytes(Driver * drvthis, u8 cmd, u8 byte1, u8 byte2) -{ - t6963_low_data(drvthis, byte1); - t6963_low_data(drvthis, byte2); - t6963_low_command(drvthis, cmd); -} - /** * Send one word (two bytes) of data followed by one command byte to the * display. Low byte is output first. @@ -613,57 +577,12 @@ t6963_low_command_2_bytes(Driver * drvthis, u8 cmd, u8 byte1, u8 byte2) * \param cmd Command byte. * \param word Data value (2 bytes) */ -void +static void t6963_low_command_word(Driver * drvthis, u8 cmd, u16 word) { - t6963_low_data(drvthis, word % 256); - t6963_low_data(drvthis, word >> 8); + t6963_low_data(drvthis, word & 0xFF); + t6963_low_data(drvthis, (word >> 8) & 0xFF); t6963_low_command(drvthis, cmd); } -/** - * Send 'SET DISPLAY MODE' command to LCD setting the desired mode and store - * it in private data. - * \param drvthis Pointer to driver structure. - * \param mode Display mode to set. - */ -void -t6963_low_enable_mode(Driver * drvthis, u8 mode) -{ - PrivateData *p = drvthis->private_data; - - p->display_mode |= mode; - t6963_low_command(drvthis, SET_DISPLAY_MODE | p->display_mode); -} - -/** - * Send 'SET DISPLAY MODE' command to LCD with the desired mode cleared and - * store the new value in private data. - * \param drvthis Pointer to driver structure. - * \param mode Display mode to clear. - */ -void -t6963_low_disable_mode(Driver * drvthis, u8 mode) -{ - PrivateData *p = drvthis->private_data; - - p->display_mode &= ~mode; - t6963_low_command(drvthis, SET_DISPLAY_MODE | p->display_mode); -} - -/** - * Swap the two display buffers. This function does not swap the actual - * data but the pointers to the buffers. - * \param drvthis Pointer to driver structure. - */ -void -t6963_swap_buffers(Driver * drvthis) -{ - PrivateData *p = drvthis->private_data; - - u8 *tmp_buffer; - tmp_buffer = p->display_buffer1; - p->display_buffer1 = p->display_buffer2; - p->display_buffer2 = tmp_buffer; - tmp_buffer = NULL; -} +/* EOF */ diff --git a/server/drivers/t6963.h b/server/drivers/t6963.h index 1de1f8d..df7ebbf 100644 --- a/server/drivers/t6963.h +++ b/server/drivers/t6963.h @@ -1,14 +1,14 @@ /*- - * Base driver module for Toshiba T6963 based LCD displays ver 2.2 - * ( ver 2.* is not at all compatible to 1.* versions!) + * Base driver module for Toshiba T6963 based LCD displays. * * Parts of this file are based on the kernel driver by * Alexander Frink * + * Copyright (c) 2001 Manuel Stahl + * 2011 Markus Dolze + * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. - * - * Copyright (c) 2001 Manuel Stahl */ #ifndef T6963_H @@ -19,20 +19,13 @@ #define DEFAULT_SIZE "20x6" #define DEFAULT_PORT 0x378 -/** Configure LPT port for bidirectional input */ -#define T6963_DATAIN(p) port_out(T6963_CONTROL_PORT(p), port_in(T6963_CONTROL_PORT(p)) | 0x20) -/** Configure LPT port for bidirectional output */ -#define T6963_DATAOUT(p) port_out(T6963_CONTROL_PORT(p), port_in(T6963_CONTROL_PORT(p)) & 0xdf) +#define T6963_MAX_WIDTH 40 /* These value are for 240x128 */ +#define T6963_MAX_HEIGHT 16 -/* RAM layout base addresses (for 64 KB RAM) */ -/* - * FIXME: Those addresses are fine for 64 KB RAM. If the display has less, - * these values need to be adjusted by the user. It should do no harm setting - * these for 8 KB RAM. - */ -#define TEXT_BASE 0x0000 -#define ATTRIB_BASE 0x7000 -#define CHARGEN_BASE 0xF000 +/* RAM layout base addresses (for 8 KB RAM) */ +#define TEXT_BASE 0x0000 /* 1K, fine for 40x16 */ +#define GRAPHIC_BASE 0x0400 /* 5K, fine for 240x128 */ +#define CHARGEN_BASE 0x1800 /* 2K, required for 256 CC */ /* 'Registers setting' commands */ #define SET_CURSOR_POINTER 0x21 @@ -75,20 +68,22 @@ /* 'Data auto read/write' commands */ #define AUTO_WRITE 0xb0 #define AUTO_READ 0xb1 -#define AUTO_RESET 0xb +#define AUTO_RESET 0xb2 +/* 'Status read' anwer codes */ +#define STA0 0x01 +#define STA1 0x02 +#define STA3 0x08 /* Macros to make port usage more readable */ #define T6963_DATA_PORT(p) (p) #define T6963_STATUS_PORT(p) ((p)+1) #define T6963_CONTROL_PORT(p) ((p)+2) - /* Data types */ typedef unsigned short u16; typedef unsigned char u8; - /* API functions */ MODULE_EXPORT int t6963_init (Driver *drvthis); MODULE_EXPORT void t6963_close (Driver *drvthis); @@ -104,24 +99,15 @@ MODULE_EXPORT void t6963_num (Driver *drvthis, int x, int num); MODULE_EXPORT int t6963_icon (Driver *drvthis, int x, int y, int icon); MODULE_EXPORT void t6963_set_char (Driver *drvthis, int n, char *dat); - /* Internal functions */ -void t6963_graphic_clear (Driver *drvthis, int x1, int y1, int x2, int y2); -void t6963_set_nchar (Driver *drvthis, int n, unsigned char *dat, int num); - -void t6963_low_set_control(Driver *drvthis, char wr, char ce, char cd, char rd); -void t6963_low_dsp_ready(Driver *drvthis); - -void t6963_low_data(Driver *drvthis, u8 byte); -void t6963_low_command (Driver *drvthis, u8 byte); - -void t6963_low_command_byte(Driver *drvthis, u8 cmd, u8 byte); -void t6963_low_command_2_bytes(Driver *drvthis, u8 cmd, u8 byte1, u8 byte2); -void t6963_low_command_word(Driver *drvthis, u8 cmd, u16 word); - -void t6963_low_enable_mode (Driver *drvthis, u8 mode); -void t6963_low_disable_mode (Driver *drvthis, u8 mode); - -void t6963_swap_buffers(Driver *drvthis); +static void t6963_graphic_clear(Driver *drvthis); +static void t6963_set_nchar(Driver *drvthis, int n, unsigned char *dat, int num); +static inline int t6963_low_dsp_ready(Driver *drvthis, u8 sta); +static void t6963_low_data(Driver *drvthis, u8 byte); +static void t6963_low_auto_write(Driver *drvthis, u8 byte); +static void t6963_low_command(Driver *drvthis, u8 byte); +static void t6963_low_command_byte(Driver *drvthis, u8 cmd, u8 byte); +static void t6963_low_command_word(Driver *drvthis, u8 cmd, u16 word); +static inline void t6963_low_send(Driver * drvthis, u8 type, u8 byte); #endif