root/trunk/drv_T6963.c

Revision 840, 13.2 kB (checked in by michael, 15 months ago)

email address changed

  • Property svn:keywords set to Id URL Rev
Line 
1/* $Id$
2 * $URL$
3 *
4 * new style driver for T6963-based displays
5 *
6 * Copyright (C) 2003 Michael Reinelt <michael@reinelt.co.at>
7 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
8 *
9 * This file is part of LCD4Linux.
10 *
11 * LCD4Linux is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
14 * any later version.
15 *
16 * LCD4Linux is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28 *
29 * exported fuctions:
30 *
31 * struct DRIVER drv_T6963
32 *
33 */
34
35#include "config.h"
36
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40#include <errno.h>
41#include <unistd.h>
42#include <termios.h>
43#include <fcntl.h>
44#include <sys/time.h>
45
46#include "debug.h"
47#include "cfg.h"
48#include "qprintf.h"
49#include "udelay.h"
50#include "plugin.h"
51#include "drv.h"
52#include "drv_generic_graphic.h"
53#include "drv_generic_parport.h"
54
55#ifdef WITH_DMALLOC
56#include <dmalloc.h>
57#endif
58
59static char Name[] = "T6963";
60static int Model;
61
62typedef struct {
63    int type;
64    char *name;
65} MODEL;
66
67static MODEL Models[] = {
68    {0x01, "generic"},
69    {0xff, "Unknown"}
70};
71
72
73/* font width of display */
74static int CELL;
75
76/* text rows/columns */
77static int TROWS, TCOLS;
78
79/* SingleScan or DualScan */
80static int DualScan = 0;
81
82/* Timings */
83static int T_ACC, T_OH, T_PW, T_DH, T_CDS;
84
85/* soft-wiring */
86static unsigned char SIGNAL_CE;
87static unsigned char SIGNAL_CD;
88static unsigned char SIGNAL_RD;
89static unsigned char SIGNAL_WR;
90
91unsigned char *Buffer1, *Buffer2;
92
93static int bug = 0;
94
95
96/****************************************/
97/***  hardware dependant functions    ***/
98/****************************************/
99
100/* perform normal status check */
101static void drv_T6_status1(void)
102{
103    int n;
104
105    /* turn off data line drivers */
106    drv_generic_parport_direction(1);
107
108    /* lower CE and RD */
109    drv_generic_parport_control(SIGNAL_CE | SIGNAL_RD, 0);
110
111    /* Access Time */
112    ndelay(T_ACC);
113
114    /* wait for STA0=1 and STA1=1 */
115    n = 0;
116    do {
117  rep_nop();
118  if (++n > 1000) {
119      debug("hang in status1");
120      bug = 1;
121      break;
122  }
123    } while ((drv_generic_parport_read() & 0x03) != 0x03);
124
125    /* rise RD and CE */
126    drv_generic_parport_control(SIGNAL_RD | SIGNAL_CE, SIGNAL_RD | SIGNAL_CE);
127
128    /* Output Hold Time */
129    ndelay(T_OH);
130
131    /* turn on data line drivers */
132    drv_generic_parport_direction(0);
133}
134
135
136/* perform status check in "auto mode" */
137static void drv_T6_status2(void)
138{
139    int n;
140
141    /* turn off data line drivers */
142    drv_generic_parport_direction(1);
143
144    /* lower RD and CE */
145    drv_generic_parport_control(SIGNAL_RD | SIGNAL_CE, 0);
146
147    /* Access Time */
148    ndelay(T_ACC);
149
150    /* wait for STA3=1 */
151    n = 0;
152    do {
153  rep_nop();
154  if (++n > 1000) {
155      debug("hang in status2");
156      bug = 1;
157      break;
158  }
159    } while ((drv_generic_parport_read() & 0x08) != 0x08);
160
161    /* rise RD and CE */
162    drv_generic_parport_control(SIGNAL_RD | SIGNAL_CE, SIGNAL_RD | SIGNAL_CE);
163
164    /* Output Hold Time */
165    ndelay(T_OH);
166
167    /* turn on data line drivers */
168    drv_generic_parport_direction(0);
169}
170
171
172static void drv_T6_write_cmd(const unsigned char cmd)
173{
174    /* wait until the T6963 is idle */
175    drv_T6_status1();
176
177    /* put data on DB1..DB8 */
178    drv_generic_parport_data(cmd);
179
180    /* lower WR and CE */
181    drv_generic_parport_control(SIGNAL_WR | SIGNAL_CE, 0);
182
183    /* Pulse width */
184    ndelay(T_PW);
185
186    /* rise WR and CE */
187    drv_generic_parport_control(SIGNAL_WR | SIGNAL_CE, SIGNAL_WR | SIGNAL_CE);
188
189    /* Data Hold Time */
190    ndelay(T_DH);
191}
192
193
194static void drv_T6_write_data(const unsigned char data)
195{
196    /* wait until the T6963 is idle */
197    drv_T6_status1();
198
199    /* put data on DB1..DB8 */
200    drv_generic_parport_data(data);
201
202    /* lower C/D */
203    drv_generic_parport_control(SIGNAL_CD, 0);
204
205    /* C/D Setup Time */
206    ndelay(T_CDS);
207
208    /* lower WR and CE */
209    drv_generic_parport_control(SIGNAL_WR | SIGNAL_CE, 0);
210
211    /* Pulse Width */
212    ndelay(T_PW);
213
214    /* rise WR and CE */
215    drv_generic_parport_control(SIGNAL_WR | SIGNAL_CE, SIGNAL_WR | SIGNAL_CE);
216
217    /* Data Hold Time */
218    ndelay(T_DH);
219
220    /* rise CD */
221    drv_generic_parport_control(SIGNAL_CD, SIGNAL_CD);
222}
223
224
225static void drv_T6_write_auto(const unsigned char data)
226{
227    /* wait until the T6963 is idle */
228    drv_T6_status2();
229
230    /* put data on DB1..DB8 */
231    drv_generic_parport_data(data);
232
233    /* lower C/D */
234    drv_generic_parport_control(SIGNAL_CD, 0);
235
236    /* C/D Setup Time */
237    ndelay(T_CDS);
238
239    /* lower WR and CE */
240    drv_generic_parport_control(SIGNAL_WR | SIGNAL_CE, 0);
241
242    /* Pulse Width */
243    ndelay(T_PW);
244
245    /* rise WR and CE */
246    drv_generic_parport_control(SIGNAL_WR | SIGNAL_CE, SIGNAL_WR | SIGNAL_CE);
247
248    /* Data Hold Time */
249    ndelay(T_DH);
250
251    /* rise CD */
252    drv_generic_parport_control(SIGNAL_CD, SIGNAL_CD);
253}
254
255
256#if 0       /* not used */
257static void drv_T6_send_byte(const unsigned char cmd, const unsigned char data)
258{
259    drv_T6_write_data(data);
260    drv_T6_write_cmd(cmd);
261}
262#endif
263
264static void drv_T6_send_word(const unsigned char cmd, const unsigned short data)
265{
266    drv_T6_write_data(data & 0xff);
267    drv_T6_write_data(data >> 8);
268    drv_T6_write_cmd(cmd);
269}
270
271
272static void drv_T6_clear(const unsigned short addr, const int len)
273{
274    int i;
275
276    drv_T6_send_word(0x24, addr); /* Set Adress Pointer */
277    drv_T6_write_cmd(0xb0); /* Set Data Auto Write */
278    for (i = 0; i < len; i++) {
279  drv_T6_write_auto(0x0);
280  if (bug) {
281      bug = 0;
282      debug("bug occured at byte %d of %d", i, len);
283  }
284    }
285    drv_T6_status2();
286    drv_T6_write_cmd(0xb2); /* Auto Reset */
287}
288
289
290static void drv_T6_copy(const unsigned short addr, const unsigned char *data, const int len)
291{
292    int i;
293
294    drv_T6_send_word(0x24, addr); /* Set Adress Pointer */
295    drv_T6_write_cmd(0xb0); /* Set Data Auto Write */
296    for (i = 0; i < len; i++) {
297  drv_T6_write_auto(*(data++));
298  if (bug) {
299      bug = 0;
300      debug("bug occured at byte %d of %d, addr=%d", i, len, addr);
301  }
302    }
303    drv_T6_status2();
304    drv_T6_write_cmd(0xb2); /* Auto Reset */
305}
306
307
308static void drv_T6_blit(const int row, const int col, const int height, const int width)
309{
310    int r, c, a, b;
311    int i, j, e, n;
312    int base;
313
314    for (r = row; r < row + height; r++) {
315  for (c = col; c < col + width; c++) {
316      unsigned char mask = 1 << (CELL - 1 - c % CELL);
317      if (drv_generic_graphic_black(r, c)) {
318    /* set bit */
319    Buffer1[(r * DCOLS + c) / CELL] |= mask;
320      } else {
321    /* clear bit */
322    Buffer1[(r * DCOLS + c) / CELL] &= ~mask;
323      }
324  }
325  a = (r * DCOLS + col) / CELL;
326  b = (r * DCOLS + col + width + CELL - 1) / CELL;
327  for (i = a; i <= b; i++) {
328      if (Buffer1[i] == Buffer2[i])
329    continue;
330      for (j = i, e = 0; i <= b; i++) {
331    if (Buffer1[i] == Buffer2[i]) {
332        if (++e > 4)
333      break;
334    } else {
335        e = 0;
336    }
337      }
338      if (DualScan && r >= DROWS / 2) {
339    base = 0x8200 - DCOLS * DROWS / 2 / CELL;
340      } else {
341    base = 0x0200;
342      }
343      n = i - j - e + 1;
344      memcpy(Buffer2 + j, Buffer1 + j, n);
345      drv_T6_copy(base + j, Buffer1 + j, n);
346  }
347    }
348}
349
350
351static int drv_T6_start(const char *section)
352{
353    char *model, *s;
354
355    model = cfg_get(section, "Model", "generic");
356    if (model != NULL && *model != '\0') {
357  int i;
358  for (i = 0; Models[i].type != 0xff; i++) {
359      if (strcasecmp(Models[i].name, model) == 0)
360    break;
361  }
362  if (Models[i].type == 0xff) {
363      error("%s: %s.Model '%s' is unknown from %s", Name, section, model, cfg_source());
364      return -1;
365  }
366  Model = i;
367    } else {
368  error("%s: empty '%s.Model' entry from %s", Name, section, cfg_source());
369  return -1;
370    }
371
372    /* read display size from config */
373    s = cfg_get(section, "Size", NULL);
374    if (s == NULL || *s == '\0') {
375  error("%s: no '%s.Size' entry from %s", Name, section, cfg_source());
376  return -1;
377    }
378
379    DROWS = -1;
380    DCOLS = -1;
381    if (sscanf(s, "%dx%d", &DCOLS, &DROWS) != 2 || DCOLS < 1 || DROWS < 1) {
382  error("%s: bad Size '%s' from %s", Name, s, cfg_source());
383  return -1;
384    }
385
386    if (sscanf(s = cfg_get(section, "font", "6x8"), "%dx%d", &XRES, &YRES) != 2 || XRES < 1 || YRES < 1) {
387  error("%s: bad %s.Font '%s' from %s", Name, section, s, cfg_source());
388  free(s);
389  return -1;
390    }
391    free(s);
392
393
394    /* get font width of display */
395    cfg_number(section, "Cell", 6, 5, 8, &CELL);
396
397    TROWS = DROWS / 8;    /* text rows */
398    TCOLS = DCOLS / CELL; /* text columns */
399
400    /* get DualScan mode */
401    cfg_number(section, "DualScan", 0, 0, 1, &DualScan);
402
403    info("%s: %dx%d %sScan %d bits/cell", Name, DCOLS, DROWS, DualScan ? "Dual" : "Single", CELL);
404
405    Buffer1 = malloc(TCOLS * DROWS);
406    if (Buffer1 == NULL) {
407  error("%s: framebuffer #1 could not be allocated: malloc() failed", Name);
408  return -1;
409    }
410
411    Buffer2 = malloc(TCOLS * DROWS);
412    if (Buffer2 == NULL) {
413  error("%s: framebuffer #2 could not be allocated: malloc() failed", Name);
414  return -1;
415    }
416
417    memset(Buffer1, 0, TCOLS * DROWS * sizeof(*Buffer1));
418    memset(Buffer2, 0, TCOLS * DROWS * sizeof(*Buffer2));
419
420    if (drv_generic_parport_open(section, Name) != 0) {
421  error("%s: could not initialize parallel port!", Name);
422  return -1;
423    }
424
425    /* soft-wiring */
426    if ((SIGNAL_CE = drv_generic_parport_wire_ctrl("CE", "STROBE")) == 0xff)
427  return -1;
428    if ((SIGNAL_CD = drv_generic_parport_wire_ctrl("CD", "SLCTIN")) == 0xff)
429  return -1;
430    if ((SIGNAL_RD = drv_generic_parport_wire_ctrl("RD", "AUTOFD")) == 0xff)
431  return -1;
432    if ((SIGNAL_WR = drv_generic_parport_wire_ctrl("WR", "INIT")) == 0xff)
433  return -1;
434
435    /* timings */
436    T_ACC = timing(Name, section, "ACC", 150, "ns");  /* Access Time */
437    T_OH = timing(Name, section, "OH", 50, "ns"); /* Output Hold Time */
438    T_PW = timing(Name, section, "PW", 80, "ns"); /* CE, RD, WR Pulse Width */
439    T_DH = timing(Name, section, "DH", 40, "ns"); /* Data Hold Time */
440    T_CDS = timing(Name, section, "CDS", 100, "ns");  /* C/D Setup Time */
441
442
443    /* rise CE, CD, RD and WR */
444    drv_generic_parport_control(SIGNAL_CE | SIGNAL_CD | SIGNAL_RD | SIGNAL_WR,
445        SIGNAL_CE | SIGNAL_CD | SIGNAL_RD | SIGNAL_WR);
446    /* set direction: write */
447    drv_generic_parport_direction(0);
448
449
450    /* initialize display */
451
452    drv_T6_send_word(0x40, 0x0000); /* Set Text Home Address */
453    drv_T6_send_word(0x41, TCOLS);  /* Set Text Area */
454
455    drv_T6_send_word(0x42, 0x0200); /* Set Graphic Home Address */
456    drv_T6_send_word(0x43, TCOLS);  /* Set Graphic Area */
457
458    drv_T6_write_cmd(0x80); /* Mode Set: OR mode, Internal CG RAM mode */
459    drv_T6_send_word(0x22, 0x0002); /* Set Offset Register */
460    drv_T6_write_cmd(0x98); /* Set Display Mode: Curser off, Text off, Graphics on */
461    drv_T6_write_cmd(0xa0); /* Set Cursor Pattern: 1 line cursor */
462    drv_T6_send_word(0x21, 0x0000); /* Set Cursor Pointer to (0,0) */
463
464
465    /* clear display */
466
467    if (DualScan) {
468  /* upper half */
469  drv_T6_clear(0x0000, TCOLS * TROWS / 2);  /* clear text area  */
470  drv_T6_clear(0x0200, TCOLS * TROWS * 8 / 2);  /* clear graphic area */
471  /* lower half */
472  drv_T6_clear(0x8000, TCOLS * TROWS / 2);  /* clear text area  */
473  drv_T6_clear(0x8200, TCOLS * TROWS * 8 / 2);  /* clear graphic area */
474    } else {
475  drv_T6_clear(0x0000, TCOLS * TROWS);  /* clear text area  */
476  drv_T6_clear(0x0200, TCOLS * TROWS * 8);  /* clear graphic area */
477    }
478
479    return 0;
480}
481
482
483/****************************************/
484/***            plugins               ***/
485/****************************************/
486
487/* none at the moment... */
488
489
490/****************************************/
491/***        exported functions        ***/
492/****************************************/
493
494
495/* list models */
496int drv_T6_list(void)
497{
498    int i;
499
500    for (i = 0; Models[i].type != 0xff; i++) {
501  printf("%s ", Models[i].name);
502    }
503    return 0;
504}
505
506
507/* initialize driver & display */
508int drv_T6_init(const char *section, const int quiet)
509{
510    int ret;
511
512    info("%s: %s", Name, "$Rev$");
513
514    /* real worker functions */
515    drv_generic_graphic_real_blit = drv_T6_blit;
516
517    /* start display */
518    if ((ret = drv_T6_start(section)) != 0)
519  return ret;
520
521    /* initialize generic graphic driver */
522    if ((ret = drv_generic_graphic_init(section, Name)) != 0)
523  return ret;
524
525    if (!quiet) {
526  char buffer[40];
527  qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS);
528  if (drv_generic_graphic_greet(buffer, NULL)) {
529      sleep(3);
530      drv_generic_graphic_clear();
531  }
532    }
533
534    /* register plugins */
535    /* none at the moment... */
536
537
538    return 0;
539}
540
541
542/* close driver & display */
543int drv_T6_quit(const int quiet)
544{
545
546    info("%s: shutting down.", Name);
547
548    drv_generic_graphic_clear();
549
550    if (!quiet) {
551  drv_generic_graphic_greet("goodbye!", NULL);
552    }
553
554    drv_generic_graphic_quit();
555    drv_generic_parport_close();
556
557    if (Buffer1) {
558  free(Buffer1);
559  Buffer1 = NULL;
560    }
561
562    if (Buffer2) {
563  free(Buffer2);
564  Buffer2 = NULL;
565    }
566
567    return (0);
568}
569
570
571DRIVER drv_T6963 = {
572    .name = Name,
573    .list = drv_T6_list,
574    .init = drv_T6_init,
575    .quit = drv_T6_quit,
576};
Note: See TracBrowser for help on using the browser.