1 //////////////////////////////////////////////////////////////////////////
2 // This is a driver for the STV5730A on-screen display chip in con- //
3 // junction with a parallel port interface. Check //
4 // http://www.usblcd.de/lcdproc/ for where to buy iand how to build //
6 // The STV3730 displays 11 rows with 28 characters. The characters are //
7 // fixed and can not be reprogrammed. Luckily the chraracter set con- //
8 // tains a heartbeat icon and some characters that can be used as //
11 // Moved the delay timing code by Charles Steinkuehler to timing.h. //
12 // Guillaume Filion <gfk@logidac.com>, December 2001 //
14 // (C) 2001 Robin Adams ( robin@adams-online.de ) //
16 // This driver is released under the GPL. See file COPYING in this //
17 // package for further details. //
18 //////////////////////////////////////////////////////////////////////////
26 #include <sys/errno.h>
36 #include "shared/str.h"
46 #define STV5730_TEST_O 0x01
47 #define STV5730_BAR 0x02
48 #define STV5730_CLK 0x04
49 #define STV5730_CSN 0x08
50 #define STV5730_DATA 0x10
52 #define STV5730_TEST_I 0x40
53 #define STV5730_MUTE 0x80
55 // If it doesn't work try increasing this value
59 // Change that to NTSC if you are from the US...
63 #define STV5730_HGT 11
64 #define STV5730_WID 28
65 #define STV5730_ATTRIB 0x800
67 #define STV5730_REG_ZOOM 0xCC
68 #define STV5730_REG_COLOR 0xCD
69 #define STV5730_REG_CONTROL 0xCE
70 #define STV5730_REG_POSITION 0xCF
71 #define STV5730_REG_MODE 0xD0
73 // Choose Colors: FLINE: First line text color, TEXT: Text color, CBACK: Character Background Color
74 // CBORD: Character Border Color, SBACK: Screen Background color
75 // 0:Black, 1: Blue, 2:Green, 3: Cyan, 4: Red, 5: Magenta, 6: Yellow, 7: White
76 #define STV5730_COL_FLINE 4
77 #define STV5730_COL_TEXT 1
78 #define STV5730_COL_CBACK 3
79 #define STV5730_COL_CBORD 0
80 #define STV5730_COL_SBACK 2
83 typedef struct driver_private_data
{
85 unsigned int charattrib
;
91 // Vars for the server core
92 MODULE_EXPORT
char *api_version
= API_VERSION
;
93 MODULE_EXPORT
int stay_in_foreground
= 0;
94 MODULE_EXPORT
int supports_multiple
= 0;
95 MODULE_EXPORT
char *symbol_prefix
= "stv5730_";
98 // Translation map ascii->stv5730 charset
99 unsigned char stv5730_to_ascii
[256] =
100 { 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
101 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
102 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
103 0x0B, 0x0B, 0x0B, 0x0B,
104 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
105 0x27, 0x0B, 0x27, 0x28,
106 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x26, 0x26,
107 0x62, 0x78, 0x61, 0x70,
108 0x6c, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
109 0x17, 0x18, 0x19, 0x1A,
110 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x0B,
111 0x0B, 0x0B, 0x0B, 0x72,
112 0x0B, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34,
113 0x35, 0x36, 0x37, 0x38,
114 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x6E,
115 0x6C, 0x71, 0x79, 0x7F,
116 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
117 0x0B, 0x0B, 0x0B, 0x0B,
118 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
119 0x0B, 0x0B, 0x0B, 0x0B,
120 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
121 0x0B, 0x0B, 0x0B, 0x0B,
122 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
123 0x0B, 0x0B, 0x0B, 0x0B,
124 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
125 0x0B, 0x0B, 0x0B, 0x0B,
126 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
127 0x0B, 0x0B, 0x0B, 0x0B,
128 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
129 0x0B, 0x0B, 0x0B, 0x0B,
130 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
131 0x0B, 0x0B, 0x0B, 0x77
136 //static void stv5730_upause(int delayCalls);
137 #define stv5730_upause timing_uPause
139 /////////////////////////////////////////////////////////////////
140 // This function returns true if a powered and working STV5730
141 // hardware is present at p->port
144 stv5730_detect (unsigned int port
)
148 for (i
= 0; i
< 10; i
++) {
149 port_out(port
, STV5730_TEST_O
);
150 stv5730_upause(IODELAY
);
151 if ((port_in(port
+ 1) & STV5730_TEST_I
) == 0)
154 stv5730_upause(IODELAY
);
155 if ((port_in(port
+ 1) & STV5730_TEST_I
) != 0)
161 /////////////////////////////////////////////////////////////////
162 // returns 0 if a valid video signal is connected to the video
165 stv5730_is_mute (unsigned int port
)
167 stv5730_upause(IODELAY
);
168 return ((port_in(port
+ 1) & STV5730_MUTE
) ? 0 : 1);
171 /////////////////////////////////////////////////////////////////
172 // stv5730_write16bit, stv5730_write8bit, stv5730_write0bit
173 // this family of functions write commands or data to the stv5730
174 // 8 bit writes repeat the high byte, 0 byte writes repeat the last
178 stv5730_write16bit (unsigned int port
, unsigned int flags
, unsigned int value
)
182 stv5730_upause(IODELAY
);
183 port_out(port
, STV5730_CSN
+ flags
);
184 stv5730_upause(IODELAY
);
185 port_out(port
, STV5730_CSN
+ STV5730_CLK
+ flags
);
186 stv5730_upause(IODELAY
);
187 port_out(port
, STV5730_CLK
+ flags
);
189 for (i
= 15; i
>= 0; i
--) {
190 char databit
= ((value
& (1 << i
)) != 0) ? STV5730_DATA
: 0;
192 port_out(port
, databit
+ STV5730_CLK
+ flags
);
193 stv5730_upause(IODELAY
);
194 port_out(port
, databit
+ flags
);
195 stv5730_upause(IODELAY
);
196 port_out(port
, databit
+ STV5730_CLK
+ flags
);
197 stv5730_upause(IODELAY
);
200 stv5730_upause(IODELAY
);
201 port_out(port
, STV5730_CSN
+ STV5730_CLK
+ flags
);
202 stv5730_upause(IODELAY
);
203 port_out(port
, STV5730_CSN
+ flags
);
204 stv5730_upause(IODELAY
);
208 stv5730_write8bit (unsigned int port
, unsigned int flags
, unsigned int value
)
212 stv5730_upause(IODELAY
);
213 port_out(port
, STV5730_CSN
+ flags
);
214 stv5730_upause(IODELAY
);
215 port_out(port
, STV5730_CSN
+ STV5730_CLK
+ flags
);
216 stv5730_upause(IODELAY
);
217 port_out(port
, STV5730_CLK
+ flags
);
219 for (i
= 7; i
>= 0; i
--) {
220 char databit
= ((value
& (1 << i
)) != 0) ? STV5730_DATA
: 0;
222 port_out(port
, databit
+ STV5730_CLK
+ flags
);
223 stv5730_upause(IODELAY
);
224 port_out(port
, databit
+ flags
);
225 stv5730_upause(IODELAY
);
226 port_out(port
, databit
+ STV5730_CLK
+ flags
);
227 stv5730_upause(IODELAY
);
230 stv5730_upause(IODELAY
);
231 port_out(port
, STV5730_CSN
+ STV5730_CLK
+ flags
);
232 stv5730_upause(IODELAY
);
233 port_out(port
, STV5730_CSN
+ flags
);
237 stv5730_write0bit (unsigned int port
, unsigned int flags
)
239 stv5730_upause(IODELAY
);
240 port_out(port
, STV5730_CSN
+ flags
);
241 stv5730_upause(IODELAY
);
242 port_out(port
, STV5730_CSN
+ STV5730_CLK
+ flags
);
243 stv5730_upause(IODELAY
);
244 port_out(port
, STV5730_CLK
+ flags
);
246 stv5730_upause(IODELAY
);
247 port_out(port
, STV5730_CSN
+ STV5730_CLK
+ flags
);
248 stv5730_upause(IODELAY
);
249 port_out(port
, STV5730_CSN
+ flags
);
253 /////////////////////////////////////////////////////////////////
254 // sets the memory pointer inside the stv5730 to the position
257 stv5730_locate (unsigned int port
, unsigned int flags
, int row
, int col
)
259 if (row
< 0 || row
>= STV5730_HGT
|| col
< 0 || col
>= STV5730_WID
)
262 stv5730_write16bit(port
, flags
, (row
<< 8) + col
);
265 /////////////////////////////////////////////////////////////////
266 // draws char z from fontmap to the framebuffer at position
267 // x,y. These are zero-based textmode positions.
268 // We need a conversion map to translate from ascii to the
269 // non-standard STV5730 charset.
272 stv5730_drawchar2fb (Driver
*drvthis
, int x
, int y
, unsigned char z
)
274 PrivateData
*p
= drvthis
->private_data
;
276 if ((x
>= 0) && (x
< STV5730_WID
) && (y
>= 0) && (y
< STV5730_HGT
))
277 p
->framebuf
[(y
* STV5730_WID
) + x
] = stv5730_to_ascii
[(unsigned int) z
];
280 /////////////////////////////////////////////////////////////////
281 // This initialises the stuff. We support supplying port as
282 // a command line argument.
285 stv5730_init (Driver
*drvthis
)
290 /* Allocate and store private data */
291 p
= (PrivateData
*) calloc(1, sizeof(PrivateData
));
294 if (drvthis
->store_private_ptr(drvthis
, p
))
297 /* initialize private data */
299 p
->charattrib
= STV5730_ATTRIB
;
303 /* Read config file */
305 /* What port to use */
306 p
->port
= drvthis
->config_get_int(drvthis
->name
, "Port", 0, LPTPORT
);
308 /* End of config file parsing */
310 if (timing_init() == -1) {
311 report(RPT_ERR
, "%s: timing_init() failed (%s)", drvthis
->name
, strerror(errno
));
315 // Initialize the Port and the stv5730
316 if (port_access(p
->port
) || port_access(p
->port
+ 1)) {
318 "%s: cannot get IO-permission for 0x%03X! Are we running as root?",
319 drvthis
->name
, p
->port
);
323 if (stv5730_detect(p
->port
)) {
324 report(RPT_ERR
, "%s: no STV5730 hardware found at 0x%03X ",
325 drvthis
->name
, p
->port
);
329 port_out(p
->port
, 0);
332 stv5730_write16bit(p
->port
, p
->flags
, 0x3000);
333 stv5730_write16bit(p
->port
, p
->flags
, 0x3000);
334 stv5730_write16bit(p
->port
, p
->flags
, 0x00db);
335 stv5730_write16bit(p
->port
, p
->flags
, 0x1000);
337 // Setup Mode + Control Register for video detection
338 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_MODE
);
339 stv5730_write16bit(p
->port
, p
->flags
, 0x1576);
341 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_CONTROL
);
342 stv5730_write16bit(p
->port
, p
->flags
, 0x1FF4);
344 report(RPT_INFO
, "%s: detecting video signal: ", drvthis
->name
);
347 if (stv5730_is_mute(p
->port
)) {
348 report(RPT_INFO
, "%s: no video signal found; using full page mode", drvthis
->name
);
349 // Setup Mode + Control for full page mode
350 p
->charattrib
= STV5730_ATTRIB
;
351 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_MODE
);
352 stv5730_write16bit(p
->port
, p
->flags
, 0x15A6);
354 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_CONTROL
);
356 stv5730_write16bit(p
->port
, p
->flags
, 0x1FD5);
359 stv5730_write16bit(p
->port
, p
->flags
, 0x1ED4);
364 report(RPT_INFO
, "%s: video signal found, using mixed mode (B&W)", drvthis
->name
);
365 // Setup Mode + Control for mixed mode, disable color
367 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_MODE
);
368 stv5730_write16bit(p
->port
, p
->flags
, 0x1576);
370 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_CONTROL
);
372 stv5730_write16bit(p
->port
, p
->flags
, 0x1DD4);
375 stv5730_write16bit(p
->port
, p
->flags
, 0x1CF4);
380 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_POSITION
);
381 stv5730_write16bit(p
->port
, p
->flags
, 0x1000 + 64 * 30 + 30);
384 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_COLOR
);
385 stv5730_write16bit(p
->port
, p
->flags
, 0x1000 + (STV5730_COL_SBACK
<< 9) +
386 (STV5730_COL_CBORD
<< 6) + STV5730_COL_CBACK
);
388 // Zoom Register: Zoom first line
389 stv5730_write16bit(p
->port
, p
->flags
, STV5730_REG_ZOOM
);
390 stv5730_write16bit(p
->port
, p
->flags
, 0x1000 + 4);
392 // Set the Row Attributes
393 for (i
= 0; i
<= 10; i
++) {
394 stv5730_write16bit(p
->port
, p
->flags
, 0x00C0 + i
);
395 stv5730_write16bit(p
->port
, p
->flags
, 0x10C0);
398 // Allocate our own framebuffer
399 p
->framebuf
= malloc(STV5730_WID
* STV5730_HGT
);
400 if (p
->framebuf
== NULL
) {
401 report(RPT_ERR
, "%s: unable to allocate framebuffer", drvthis
->name
);
402 stv5730_close(drvthis
);
407 memset(p
->framebuf
, 0, STV5730_WID
* STV5730_HGT
);
409 report(RPT_DEBUG
, "%s: init() done", drvthis
->name
);
414 /////////////////////////////////////////////////////////////////
415 // Frees the framebuffer and exits the driver.
418 stv5730_close (Driver
*drvthis
)
420 PrivateData
*p
= drvthis
->private_data
;
423 if (p
->framebuf
!= NULL
)
428 drvthis
->store_private_ptr(drvthis
, NULL
);
431 /////////////////////////////////////////////////////////////////
432 // Returns the display width
435 stv5730_width (Driver
*drvthis
)
440 /////////////////////////////////////////////////////////////////
441 // Returns the display height
444 stv5730_height (Driver
*drvthis
)
449 /////////////////////////////////////////////////////////////////
450 // Returns the number of pixels a character is wide
453 stv5730_cellwidth (Driver
*drvthis
)
458 /////////////////////////////////////////////////////////////////
459 // Returns the number of pixels a character is high
462 stv5730_cellheight (Driver
*drvthis
)
466 // cellwidth and cellheight are only needed for old_vbar.
467 // Therefor these values are now hardcoded into these functions.
468 // When old_vbar is not used anymore, these two functions can be removed.
471 /////////////////////////////////////////////////////////////////
475 stv5730_clear (Driver
*drvthis
)
477 PrivateData
*p
= drvthis
->private_data
;
479 memset(p
->framebuf
, 0x0B, STV5730_WID
* STV5730_HGT
);
482 /////////////////////////////////////////////////////////////////
484 // Flushes all output to the lcd...
487 stv5730_flush (Driver
*drvthis
)
489 PrivateData
*p
= drvthis
->private_data
;
492 stv5730_locate(p
->port
, p
->flags
, 0, 0);
494 for (i
= 0; i
< STV5730_HGT
; i
++) {
496 atr
= (STV5730_COL_FLINE
<< 8);
498 atr
= (STV5730_COL_TEXT
<< 8);
499 stv5730_write16bit(p
->port
, p
->flags
, 0x1000 + atr
+ p
->framebuf
[i
* STV5730_WID
] +
501 for (j
= 1; j
< STV5730_WID
; j
++) {
502 if (p
->framebuf
[j
+ (i
* STV5730_WID
) - 1] !=
503 p
->framebuf
[j
+ (i
* STV5730_WID
)])
504 stv5730_write8bit(p
->port
, p
->flags
, p
->framebuf
[j
+ (i
* STV5730_WID
)]);
506 stv5730_write0bit(p
->port
, p
->flags
);
511 /////////////////////////////////////////////////////////////////
512 // Prints a string on the screen, at position (x,y). The
513 // upper-left is (1,1), and the lower right should be (28,11).
516 stv5730_string (Driver
*drvthis
, int x
, int y
, const char string
[])
518 //PrivateData *p = drvthis->private_data;
521 x
--; // Convert 1-based coords to 0-based...
524 for (i
= 0; string
[i
] != '\0'; i
++)
525 stv5730_drawchar2fb(drvthis
, x
+ i
, y
, string
[i
]);
528 /////////////////////////////////////////////////////////////////
529 // Writes char c at position x,y into the framebuffer.
530 // x and y are 1-based textmode coordinates.
533 stv5730_chr (Driver
*drvthis
, int x
, int y
, char c
)
535 //PrivateData *p = drvthis->private_data;
539 stv5730_drawchar2fb(drvthis
, x
, y
, c
);
542 /////////////////////////////////////////////////////////////////
543 // This function draws ugly big numbers. We could use the zoom
544 // feature of the stv5730 if we'd know when big numbers start
547 stv5730_num (Driver
*drvthis
, int x
, int num
)
549 //PrivateData *p = drvthis->private_data;
554 if ((x
>= STV5730_WID
) || (num
< 0) || (num
> 10))
557 for (j
= 1; j
< 10; j
++) {
559 for (i
= 0; i
< 3; i
++)
560 stv5730_drawchar2fb(drvthis
, x
+ i
, j
, '0' + num
);
563 stv5730_drawchar2fb(drvthis
, x
, j
, ':');
568 /////////////////////////////////////////////////////////////////
569 // Draws a vertical bar from the bottom up to the last 7 rows of the
570 // framebuffer at 1-based position x. len is given in pixels.
573 stv5730_old_vbar (Driver
*drvthis
, int x
, int len
)
575 PrivateData
*p
= drvthis
->private_data
;
580 if (x
< 0 || len
< 0 || (len
/ 6) >= STV5730_WID
)
583 for (i
= 0; i
<= len
; i
+= 6) {
584 if (len
>= (i
+ 6)) /* 6 = cellheight */
585 p
->framebuf
[((10 - (i
/ 6)) * STV5730_WID
) + x
] = 0x77;
587 p
->framebuf
[((10 - (i
/ 6)) * STV5730_WID
) + x
] = 0x72 + (len
% 6);
592 /////////////////////////////////////////////////////////////////
593 // Draws a horizontal bar from left to right at 1-based position
594 // x,y into the framebuffer. len is given in pixels.
595 // It uses the STV5730 'channel-tuning' chars(0x64-0x68) to do
598 stv5730_old_hbar (Driver
*drvthis
, int x
, int y
, int len
)
600 PrivateData
*p
= drvthis
->private_data
;
606 if (y
< 0 || y
>= STV5730_HGT
|| x
< 0 || len
< 0
607 || (x
+ (len
/ 5)) >= STV5730_WID
)
610 for (i
= 0; i
<= len
; i
+= 5) {
611 if (len
>= (i
+ 4)) /* 4 = cellwidth */
612 p
->framebuf
[(y
* STV5730_WID
) + x
+ (i
/ 5)] = 0x64;
614 p
->framebuf
[(y
* STV5730_WID
) + x
+ (i
/ 5)] = 0x65 + (len
% 5);
618 /////////////////////////////////////////////////////////////////
619 // Reprogrammes character dest to contain an icon given by
621 // The STV5730 has no programmable chars. The charset is very
622 // limited, it doesn't even contain a '%' char. But wait...
623 // It contains a heartbeat char ! :-)
625 stv5730_old_icon (Driver
*drvthis
, int which
, char dest
)
627 //PrivateData *p = drvthis->private_data;
630 case 0: // 0:empty Heart
631 stv5730_to_ascii
[(int) dest
] = 0x71;
633 case 1: // 1:Filled Heart
634 stv5730_to_ascii
[(int) dest
] = 0x0B;
636 case 2: // 2:Ellipsis
637 stv5730_to_ascii
[(int) dest
] = 0x5F;
640 stv5730_to_ascii
[(int) dest
] = 0x0B;