1 /* $NetBSD: bt485.c,v 1.18 2018/01/24 05:35:58 riastradh Exp $ */
2 
3 /*
4  * Copyright (c) 1995, 1996 Carnegie-Mellon University.
5  * All rights reserved.
6  *
7  * Author: Chris G. Demetriou
8  *
9  * Permission to use, copy, modify and distribute this software and
10  * its documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie the
27  * rights to redistribute these changes.
28  */
29 
30  /* This code was derived from and originally located in sys/dev/pci/
31   *        NetBSD: tga_bt485.c,v 1.4 1999/03/24 05:51:21 mrg Exp
32   */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: bt485.c,v 1.18 2018/01/24 05:35:58 riastradh Exp $");
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #include <sys/buf.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 
44 #include <dev/pci/pcivar.h>
45 #include <dev/ic/bt485reg.h>
46 #include <dev/ic/bt485var.h>
47 #include <dev/ic/ramdac.h>
48 
49 #include <dev/wscons/wsconsio.h>
50 
51 /*
52  * Functions exported via the RAMDAC configuration table.
53  */
54 void      bt485_init(struct ramdac_cookie *);
55 int       bt485_set_cmap(struct ramdac_cookie *, struct wsdisplay_cmap *);
56 int       bt485_get_cmap(struct ramdac_cookie *, struct wsdisplay_cmap *);
57 int       bt485_set_cursor(struct ramdac_cookie *, struct wsdisplay_cursor *);
58 int       bt485_get_cursor(struct ramdac_cookie *, struct wsdisplay_cursor *);
59 int       bt485_set_curpos(struct ramdac_cookie *, struct wsdisplay_curpos *);
60 int       bt485_get_curpos(struct ramdac_cookie *, struct wsdisplay_curpos *);
61 int       bt485_get_curmax(struct ramdac_cookie *, struct wsdisplay_curpos *);
62 
63 /* XXX const */
64 struct ramdac_funcs bt485_funcsstruct = {
65           "Bt485",
66           bt485_register,
67           bt485_init,
68           bt485_set_cmap,
69           bt485_get_cmap,
70           bt485_set_cursor,
71           bt485_get_cursor,
72           bt485_set_curpos,
73           bt485_get_curpos,
74           bt485_get_curmax,
75           NULL,                         /* check_curcmap; not needed */
76           NULL,                         /* set_curcmap; not needed */
77           NULL,                         /* get_curcmap; not needed */
78           NULL,                         /* no dot clock to set */
79 };
80 
81 /*
82  * Private data.
83  */
84 struct bt485data {
85           void            *cookie;        /* This is what is passed
86                                                    * around, and is probably
87                                                    * struct tga_devconfig *
88                                                    */
89 
90           int             (*ramdac_sched_update)(void *, void (*)(void *));
91           void            (*ramdac_wr)(void *, u_int, u_int8_t);
92           u_int8_t        (*ramdac_rd)(void *, u_int);
93 
94           int       changed;                      /* what changed; see below */
95           int       curenb;                                 /* cursor enabled */
96           struct wsdisplay_curpos curpos;                   /* current cursor position */
97           struct wsdisplay_curpos curhot;                   /* cursor hotspot */
98           char curcmap_r[2];                      /* cursor colormap */
99           char curcmap_g[2];
100           char curcmap_b[2];
101           struct wsdisplay_curpos cursize;        /* current cursor size */
102           char curimage[512];                     /* cursor image data */
103           char curmask[512];                      /* cursor mask data */
104           char cmap_r[256];                                 /* colormap */
105           char cmap_g[256];
106           char cmap_b[256];
107 };
108 
109 #define   DATA_ENB_CHANGED    0x01      /* cursor enable changed */
110 #define   DATA_CURCMAP_CHANGED          0x02      /* cursor colormap changed */
111 #define   DATA_CURSHAPE_CHANGED         0x04      /* cursor size, image, mask changed */
112 #define   DATA_CMAP_CHANGED   0x08      /* colormap changed */
113 #define   DATA_ALL_CHANGED    0x0f
114 
115 #define   CURSOR_MAX_SIZE               64
116 
117 /*
118  * Internal functions.
119  */
120 
121 void      bt485_update(void *);
122 void      bt485_update_curpos(struct bt485data *);
123 
124 static inline void
bt485_wr_i(struct bt485data * data,u_int8_t ireg,u_int8_t val)125 bt485_wr_i(struct bt485data *data, u_int8_t ireg, u_int8_t val)
126 {
127           data->ramdac_wr(data->cookie, BT485_REG_PCRAM_WRADDR, ireg);
128           data->ramdac_wr(data->cookie, BT485_REG_EXTENDED, val);
129 }
130 
131 static inline u_int8_t
bt485_rd_i(struct bt485data * data,u_int8_t ireg)132 bt485_rd_i(struct bt485data *data, u_int8_t ireg)
133 {
134           data->ramdac_wr(data->cookie, BT485_REG_PCRAM_WRADDR, ireg);
135           return (data->ramdac_rd(data->cookie, BT485_REG_EXTENDED));
136 }
137 
138 /*****************************************************************************/
139 
140 /*
141  * Functions exported via the RAMDAC configuration table.
142  */
143 
144 struct ramdac_funcs *
bt485_funcs(void)145 bt485_funcs(void)
146 {
147           return &bt485_funcsstruct;
148 }
149 
150 struct ramdac_cookie *
bt485_register(void * v,int (* sched_update)(void *,void (*)(void *)),void (* wr)(void *,u_int,u_int8_t),u_int8_t (* rd)(void *,u_int))151 bt485_register(
152           void *v,
153           int (*sched_update)(void *, void (*)(void *)),
154           void (*wr)(void *, u_int, u_int8_t),
155           u_int8_t (*rd)(void *, u_int))
156 {
157           struct bt485data *data;
158           /*
159            * XXX -- comment out of date.  rcd.
160            * If we should allocate a new private info struct, do so.
161            * Otherwise, use the one we have (if it's there), or
162            * use the temporary one on the stack.
163            */
164           data = malloc(sizeof *data, M_DEVBUF, M_WAITOK);
165           /* XXX -- if !data */
166           data->cookie = v;
167           data->ramdac_sched_update = sched_update;
168           data->ramdac_wr = wr;
169           data->ramdac_rd = rd;
170           return (struct ramdac_cookie *)data;
171 }
172 
173 /*
174  * This function exists solely to provide a means to init
175  * the RAMDAC without first registering.  It is useful for
176  * initializing the console early on.
177  */
178 void
bt485_cninit(void * v,int (* sched_update)(void *,void (*)(void *)),void (* wr)(void *,u_int,u_int8_t),u_int8_t (* rd)(void *,u_int))179 bt485_cninit(
180           void *v,
181           int (*sched_update)(void *, void (*)(void *)),
182           void (*wr)(void *, u_int, u_int8_t),
183           u_int8_t (*rd)(void *, u_int))
184 {
185           struct bt485data tmp, *data = &tmp;
186           data->cookie = v;
187           data->ramdac_sched_update = sched_update;
188           data->ramdac_wr = wr;
189           data->ramdac_rd = rd;
190           bt485_init((struct ramdac_cookie *)data);
191 }
192 
193 void
bt485_init(struct ramdac_cookie * rc)194 bt485_init(struct ramdac_cookie *rc)
195 {
196           u_int8_t regval;
197           struct bt485data *data = (struct bt485data *)rc;
198           int i;
199 
200           /*
201            * Init the BT485 for normal operation.
202            */
203 
204           /*
205            * Allow indirect register access.  (Actually, this is
206            * already enabled.  In fact, if it is _disabled_, for
207            * some reason the monitor appears to lose sync!!! (?!?!)
208            */
209           regval = data->ramdac_rd(data->cookie, BT485_REG_COMMAND_0);
210           regval |= 0x80;
211           /*
212            * Set the RAMDAC to 8 bit resolution, rather than 6 bit
213            * resolution.
214            */
215           regval |= 0x02;
216           data->ramdac_wr(data->cookie, BT485_REG_COMMAND_0, regval);
217 
218           /* Set the RAMDAC to 8BPP (no interestion options). */
219           data->ramdac_wr(data->cookie, BT485_REG_COMMAND_1, 0x40);
220 
221           /* Disable the cursor (for now) */
222           regval = data->ramdac_rd(data->cookie, BT485_REG_COMMAND_2);
223           regval &= ~0x03;
224           regval |= 0x24;
225           data->ramdac_wr(data->cookie, BT485_REG_COMMAND_2, regval);
226 
227           /* Use a 64x64x2 cursor */
228           regval = bt485_rd_i(data, BT485_IREG_COMMAND_3);
229           regval |= 0x04;
230           regval |= 0x08;
231           bt485_wr_i(data, BT485_IREG_COMMAND_3, regval);
232 
233           /* Set the Pixel Mask to something useful */
234           data->ramdac_wr(data->cookie, BT485_REG_PIXMASK, 0xff);
235 
236           /*
237            * Initialize the RAMDAC info struct to hold all of our
238            * data, and fill it in.
239            */
240           data->changed = DATA_ALL_CHANGED;
241 
242           data->curenb = 0;                                 /* cursor disabled */
243           data->curpos.x = data->curpos.y = 0;              /* right now at 0,0 */
244           data->curhot.x = data->curhot.y = 0;              /* hot spot at 0,0 */
245 
246           /* initial cursor colormap: 0 is black, 1 is white */
247           data->curcmap_r[0] = data->curcmap_g[0] = data->curcmap_b[0] = 0;
248           data->curcmap_r[1] = data->curcmap_g[1] = data->curcmap_b[1] = 0xff;
249 
250           /* initial cursor data: 64x64 block of white. */
251           data->cursize.x = data->cursize.y = 64;
252           for (i = 0; i < 512; i++)
253                     data->curimage[i] = data->curmask[i] = 0xff;
254 
255           /* Initial colormap: 0 is black, everything else is white */
256           data->cmap_r[0] = data->cmap_g[0] = data->cmap_b[0] = 0;
257           for (i = 1; i < 256; i++)
258                     data->cmap_r[i] = data->cmap_g[i] = data->cmap_b[i] = 255;
259 
260           bt485_update((void *)data);
261 }
262 
263 int
bt485_set_cmap(struct ramdac_cookie * rc,struct wsdisplay_cmap * cmapp)264 bt485_set_cmap(struct ramdac_cookie *rc, struct wsdisplay_cmap *cmapp)
265 {
266           struct bt485data *data = (struct bt485data *)rc;
267           u_int count, index;
268           uint8_t r[256], g[256], b[256];
269           int s, error;
270 
271           if (cmapp->index >= 256 || cmapp->count > 256 - cmapp->index)
272                     return (EINVAL);
273 
274           index = cmapp->index;
275           count = cmapp->count;
276           error = copyin(cmapp->red, &r[index], count);
277           if (error)
278                     return error;
279           error = copyin(cmapp->green, &g[index], count);
280           if (error)
281                     return error;
282           error = copyin(cmapp->blue, &b[index], count);
283           if (error)
284                     return error;
285           s = spltty();
286           memcpy(&data->cmap_r[index], &r[index], count);
287           memcpy(&data->cmap_g[index], &g[index], count);
288           memcpy(&data->cmap_b[index], &b[index], count);
289           data->changed |= DATA_CMAP_CHANGED;
290           data->ramdac_sched_update(data->cookie, bt485_update);
291           splx(s);
292           return (0);
293 }
294 
295 int
bt485_get_cmap(struct ramdac_cookie * rc,struct wsdisplay_cmap * cmapp)296 bt485_get_cmap(struct ramdac_cookie *rc, struct wsdisplay_cmap *cmapp)
297 {
298           struct bt485data *data = (struct bt485data *)rc;
299           u_int count, index;
300           int error;
301 
302           if (cmapp->index >= 256 || cmapp->count > 256 - cmapp->index )
303                     return (EINVAL);
304 
305           count = cmapp->count;
306           index = cmapp->index;
307           error = copyout(&data->cmap_r[index], cmapp->red, count);
308           if (error)
309                     return (error);
310           error = copyout(&data->cmap_g[index], cmapp->green, count);
311           if (error)
312                     return (error);
313           error = copyout(&data->cmap_b[index], cmapp->blue, count);
314           return (error);
315 }
316 
317 int
bt485_set_cursor(struct ramdac_cookie * rc,struct wsdisplay_cursor * cursorp)318 bt485_set_cursor(struct ramdac_cookie *rc, struct wsdisplay_cursor *cursorp)
319 {
320           struct bt485data *data = (struct bt485data *)rc;
321           u_int count = 0, icount = 0, index = 0, v;
322           char r[2], g[2], b[2], image[512], mask[512];
323           int s, error;
324 
325           v = cursorp->which;
326 
327           /*
328            * For DOCMAP and DOSHAPE, copy in the new data
329            * before we do anything that we can't recover from.
330            */
331           if (v & WSDISPLAY_CURSOR_DOCMAP) {
332                     if (cursorp->cmap.index > 2 ||
333                         cursorp->cmap.count > 2 - cursorp->cmap.index)
334                               return (EINVAL);
335                     count = cursorp->cmap.count;
336                     index = cursorp->cmap.index;
337                     error = copyin(cursorp->cmap.red, &r[index], count);
338                     if (error)
339                               return error;
340                     error = copyin(cursorp->cmap.green, &g[index], count);
341                     if (error)
342                               return error;
343                     error = copyin(cursorp->cmap.blue, &b[index], count);
344                     if (error)
345                               return error;
346           }
347           if (v & WSDISPLAY_CURSOR_DOSHAPE) {
348                     if (cursorp->size.x > CURSOR_MAX_SIZE ||
349                         cursorp->size.y > CURSOR_MAX_SIZE)
350                               return (EINVAL);
351                     icount = (CURSOR_MAX_SIZE / NBBY) * data->cursize.y;
352                     error = copyin(cursorp->image, image, icount);
353                     if (error)
354                               return error;
355                     error = copyin(cursorp->mask, mask, icount);
356                     if (error)
357                               return error;
358           }
359 
360           if (v & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOCUR)) {
361                     if (v & WSDISPLAY_CURSOR_DOPOS)
362                               data->curpos = cursorp->pos;
363                     if (v & WSDISPLAY_CURSOR_DOCUR)
364                               data->curhot = cursorp->hot;
365                     bt485_update_curpos(data);
366           }
367 
368           s = spltty();
369 
370           /* Data is all available; perform the requested operations. */
371           if (v & WSDISPLAY_CURSOR_DOCUR) {
372                     data->curenb = cursorp->enable;
373                     data->changed |= DATA_ENB_CHANGED;
374           }
375           if (v & WSDISPLAY_CURSOR_DOCMAP) {
376                     memcpy(&data->curcmap_r[index], &r[index], count);
377                     memcpy(&data->curcmap_g[index], &g[index], count);
378                     memcpy(&data->curcmap_b[index], &b[index], count);
379                     data->changed |= DATA_CURCMAP_CHANGED;
380           }
381           if (v & WSDISPLAY_CURSOR_DOSHAPE) {
382                     data->cursize = cursorp->size;
383                     count = (CURSOR_MAX_SIZE / NBBY) * data->cursize.y;
384                     memset(data->curimage, 0, sizeof data->curimage);
385                     memset(data->curmask, 0, sizeof data->curmask);
386                     memcpy(data->curimage, image, icount);
387                     memcpy(data->curmask, mask, icount);
388                     data->changed |= DATA_CURSHAPE_CHANGED;
389           }
390 
391           if (data->changed)
392                     data->ramdac_sched_update(data->cookie, bt485_update);
393           splx(s);
394 
395           return (0);
396 }
397 
398 int
bt485_get_cursor(struct ramdac_cookie * rc,struct wsdisplay_cursor * cursorp)399 bt485_get_cursor(struct ramdac_cookie *rc, struct wsdisplay_cursor *cursorp)
400 {
401           struct bt485data *data = (struct bt485data *)rc;
402           int error, count;
403 
404           /* we return everything they want */
405           cursorp->which = WSDISPLAY_CURSOR_DOALL;
406 
407           cursorp->enable = data->curenb;         /* DOCUR */
408           cursorp->pos = data->curpos;  /* DOPOS */
409           cursorp->hot = data->curhot;  /* DOHOT */
410 
411           cursorp->cmap.index = 0;      /* DOCMAP */
412           cursorp->cmap.count = 2;
413           if (cursorp->cmap.red != NULL) {
414                     error = copyout(data->curcmap_r, cursorp->cmap.red, 2);
415                     if (error)
416                               return (error);
417           }
418           if (cursorp->cmap.green != NULL) {
419                     error = copyout(data->curcmap_g, cursorp->cmap.green, 2);
420                     if (error)
421                               return (error);
422           }
423           if (cursorp->cmap.blue != NULL) {
424                     error = copyout(data->curcmap_b, cursorp->cmap.blue, 2);
425                     if (error)
426                               return (error);
427           }
428 
429           cursorp->size = data->cursize;          /* DOSHAPE */
430           if (cursorp->image != NULL) {
431                     count = (CURSOR_MAX_SIZE / NBBY) * data->cursize.y;
432                     error = copyout(data->curimage, cursorp->image, count);
433                     if (error)
434                               return (error);
435                     error = copyout(data->curmask, cursorp->mask, count);
436                     if (error)
437                               return (error);
438           }
439 
440           return (0);
441 }
442 
443 int
bt485_set_curpos(struct ramdac_cookie * rc,struct wsdisplay_curpos * curposp)444 bt485_set_curpos(struct ramdac_cookie *rc, struct wsdisplay_curpos *curposp)
445 {
446           struct bt485data *data = (struct bt485data *)rc;
447 
448           data->curpos = *curposp;
449           bt485_update_curpos(data);
450 
451           return (0);
452 }
453 
454 int
bt485_get_curpos(struct ramdac_cookie * rc,struct wsdisplay_curpos * curposp)455 bt485_get_curpos(struct ramdac_cookie *rc, struct wsdisplay_curpos *curposp)
456 {
457           struct bt485data *data = (struct bt485data *)rc;
458 
459           *curposp = data->curpos;
460           return (0);
461 }
462 
463 int
bt485_get_curmax(struct ramdac_cookie * rc,struct wsdisplay_curpos * curposp)464 bt485_get_curmax(struct ramdac_cookie *rc, struct wsdisplay_curpos *curposp)
465 {
466 
467           curposp->x = curposp->y = CURSOR_MAX_SIZE;
468           return (0);
469 }
470 
471 /*****************************************************************************/
472 
473 /*
474  * Internal functions.
475  */
476 
477 void
bt485_update(void * vp)478 bt485_update(void *vp)
479 {
480           struct bt485data *data = vp;
481           u_int8_t regval;
482           int count, i, v;
483 
484           v = data->changed;
485           data->changed = 0;
486 
487           if (v & DATA_ENB_CHANGED) {
488                     regval = data->ramdac_rd(data->cookie, BT485_REG_COMMAND_2);
489                     if (data->curenb)
490                               regval |= 0x01;
491                     else
492                               regval &= ~0x03;
493                 data->ramdac_wr(data->cookie, BT485_REG_COMMAND_2, regval);
494           }
495 
496           if (v & DATA_CURCMAP_CHANGED) {
497                     /* addr[9:0] assumed to be 0 */
498                     /* set addr[7:0] to 1 */
499                 data->ramdac_wr(data->cookie, BT485_REG_COC_WRADDR, 0x01);
500 
501                     /* spit out the cursor data */
502                     for (i = 0; i < 2; i++) {
503                     data->ramdac_wr(data->cookie, BT485_REG_COCDATA,
504                                   data->curcmap_r[i]);
505                     data->ramdac_wr(data->cookie, BT485_REG_COCDATA,
506                                   data->curcmap_g[i]);
507                     data->ramdac_wr(data->cookie, BT485_REG_COCDATA,
508                                   data->curcmap_b[i]);
509                     }
510           }
511 
512           if (v & DATA_CURSHAPE_CHANGED) {
513                     count = (CURSOR_MAX_SIZE / NBBY) * data->cursize.y;
514 
515                     /*
516                      * Write the cursor image data:
517                      *        set addr[9:8] to 0,
518                      *        set addr[7:0] to 0,
519                      *        spit it all out.
520                      */
521                     regval = bt485_rd_i(data, BT485_IREG_COMMAND_3);
522                     regval &= ~0x03;
523                     bt485_wr_i(data, BT485_IREG_COMMAND_3, regval);
524                 data->ramdac_wr(data->cookie, BT485_REG_PCRAM_WRADDR, 0);
525                     for (i = 0; i < count; i++)
526                               data->ramdac_wr(data->cookie, BT485_REG_CURSOR_RAM,
527                                   data->curimage[i]);
528 
529                     /*
530                      * Write the cursor mask data:
531                      *        set addr[9:8] to 2,
532                      *        set addr[7:0] to 0,
533                      *        spit it all out.
534                      */
535                     regval = bt485_rd_i(data, BT485_IREG_COMMAND_3);
536                     regval &= ~0x03; regval |= 0x02;
537                     bt485_wr_i(data, BT485_IREG_COMMAND_3, regval);
538                 data->ramdac_wr(data->cookie, BT485_REG_PCRAM_WRADDR, 0);
539                     for (i = 0; i < count; i++)
540                               data->ramdac_wr(data->cookie, BT485_REG_CURSOR_RAM,
541                                   data->curmask[i]);
542 
543                     /* set addr[9:0] back to 0 */
544                     regval = bt485_rd_i(data, BT485_IREG_COMMAND_3);
545                     regval &= ~0x03;
546                     bt485_wr_i(data, BT485_IREG_COMMAND_3, regval);
547           }
548 
549           if (v & DATA_CMAP_CHANGED) {
550                     /* addr[9:0] assumed to be 0 */
551                     /* set addr[7:0] to 0 */
552                 data->ramdac_wr(data->cookie, BT485_REG_PCRAM_WRADDR, 0x00);
553 
554                     /* spit out the cursor data */
555                     for (i = 0; i < 256; i++) {
556                     data->ramdac_wr(data->cookie, BT485_REG_PALETTE,
557                                   data->cmap_r[i]);
558                     data->ramdac_wr(data->cookie, BT485_REG_PALETTE,
559                                   data->cmap_g[i]);
560                     data->ramdac_wr(data->cookie, BT485_REG_PALETTE,
561                                   data->cmap_b[i]);
562                     }
563           }
564 }
565 
566 void
bt485_update_curpos(struct bt485data * data)567 bt485_update_curpos(struct bt485data *data)
568 {
569           void *cookie = data->cookie;
570           int s, x, y;
571 
572           s = spltty();
573 
574           x = data->curpos.x + CURSOR_MAX_SIZE - data->curhot.x;
575           y = data->curpos.y + CURSOR_MAX_SIZE - data->curhot.y;
576           data->ramdac_wr(cookie, BT485_REG_CURSOR_X_LOW, x & 0xff);
577           data->ramdac_wr(cookie, BT485_REG_CURSOR_X_HIGH, (x >> 8) & 0x0f);
578           data->ramdac_wr(cookie, BT485_REG_CURSOR_Y_LOW, y & 0xff);
579           data->ramdac_wr(cookie, BT485_REG_CURSOR_Y_HIGH, (y >> 8) & 0x0f);
580 
581           splx(s);
582 }
583