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.
This commit is contained in:
mmdolze
2011-01-21 06:59:37 +00:00
parent c64646b4de
commit de7a132fbb
5 changed files with 260 additions and 322 deletions
+1
View File
@@ -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)
+8 -4
View File
@@ -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
+42 -14
View File
@@ -3,18 +3,19 @@
<para>
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.
</para>
<para>
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.
</para>
<para>
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!
</para>
@@ -25,7 +26,7 @@ Check the datasheet of your display!
<para>
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!
</para>
@@ -92,6 +93,13 @@ Check the datasheet of your display!
<entry>/RD</entry>
<entry>*</entry>
</row>
<row>
<entry></entry>
<entry></entry>
<entry>VDD</entry>
<entry>FS</entry>
<entry>*</entry>
</row>
</tbody>
</tgroup>
</table>
@@ -122,7 +130,11 @@ can be done by specifying "--enable-drivers=all" or by
<property>Size</property> = &parameters.size;
</term>
<listitem><para>
set display size [default: <literal>20x6</literal>]
Set display size in characters [default: <literal>20x6</literal>]. Do set
it as close as possible to the pixel size of the display (based on a
<literal>6x8</literal> font). Otherwise characters will repeat on the
right of a line. Example: <literal>21x8</literal> on a <literal>128x64</literal>
display.
</para></listitem>
</varlistentry>
@@ -142,31 +154,47 @@ can be done by specifying "--enable-drivers=all" or by
<varlistentry>
<term>
<property>ECPlpt</property> = &parameters.yesdefno;
<property>bidirectional</property> = &parameters.yesdefno;
</term>
<listitem>
<para>
Is ECP mode on? [default: <literal>yes</literal>; legal: <literal>yes</literal>, <literal>no</literal>]
Use LPT port in bi-directional mode. [default: <literal>yes</literal>;
legal: <literal>yes</literal>, <literal>no</literal>]
</para>
<para>
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.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<property>graphic</property> = &parameters.yesnodef;
<property>ClearGraphic</property> = &parameters.yesnodef;
</term>
<listitem>
<para>
Use graphic? [default: <literal>no</literal>; legal: <literal>yes</literal>, <literal>no</literal>]
Clear graphic memory on start-up [default: <literal>no</literal>;
legal: <literal>yes</literal>, <literal>no</literal>]. 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.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<property>delayBus</property> = &parameters.yesnodef;
</term>
<listitem>
<para>
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: <literal>no</literal>;
legal: <literal>yes</literal>, <literal>no</literal>]. 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.
</para>
</listitem>
</varlistentry>
+185 -266
View File
@@ -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 <Alexander.Frink@Uni-Mainz.DE>
* Parts of this file are based on the kernel driver by
* Alexander Frink <Alexander.Frink@Uni-Mainz.DE>
*
* Copyright (c) 2001 Manuel Stahl <mythos@xmythos.de>
* 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 <mythos@xmythos.de>
*/
#include <stdlib.h>
@@ -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 */
+24 -38
View File
@@ -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 <Alexander.Frink@Uni-Mainz.DE>
*
* Copyright (c) 2001 Manuel Stahl <mythos@xmythos.de>
* 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 <mythos@xmythos.de>
*/
#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