xref: /plan9-contrib/sys/src/cmd/aux/vga/s3clock.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 
4 #include "vga.h"
5 
6 static void
7 setcrt42(Vga *vga, Ctlr *ctlr, uchar index)
8 {
9 	verbose("%s->clock->setcrt42\n", ctlr->name);
10 
11 	vgao(MiscW, vga->misc & ~0x0C);
12 	outportb(Crtx+1, 0x00);
13 
14 	vga->crt[0x42] = (vga->crt[0x42] & 0xF0)|index;
15 	vgao(MiscW, vga->misc);
16 	vgaxo(Crtx, 0x42, vga->crt[0x42]);
17 }
18 
19 static void
20 icd2061aload(Vga *vga, Ctlr *ctlr)
21 {
22 	ulong sdata;
23 	int i;
24 	uchar crt42;
25 
26 	verbose("%s->clock->icd2061aload\n", ctlr->name);
27 	/*
28 	 * The serial word to be loaded into the icd2061a is
29 	 *	(2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d
30 	 * Always select ICD2061A REG2.
31 	 */
32 	sdata = (2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d;
33 
34 	/*
35 	 * The display should be already off to enable  us to clock the
36 	 * serial data word into either MiscW or Crt42.
37 	 *
38 	 * Set the Misc register to make clock-select-out the contents of
39 	 * register Crt42. Must turn the sequencer off when changing bits
40 	 * <3:2> of Misc. Also, must set Crt42 to 0 before setting <3:2>
41 	 * of Misc due to a hardware glitch.
42 	 */
43 	vgao(MiscW, vga->misc & ~0x0C);
44 	crt42 = vgaxi(Crtx, 0x42) & 0xF0;
45 	outportb(Crtx+1, 0x00);
46 
47 	vgaxo(Seqx, 0x00, 0x00);
48 	vgao(MiscW, vga->misc|0x0C);
49 	vgaxo(Seqx, 0x00, 0x03);
50 
51 	/*
52 	 * Unlock the ICD2061A. The unlock sequence is at least 5 low-to-high
53 	 * transitions of CLK with DATA high, followed by a single low-to-high
54 	 * transition of CLK with DATA low.
55 	 * Using Crt42, CLK is bit0, DATA is bit 1. If we used MiscW, they'd
56 	 * be bits 2 and 3 respectively.
57 	 */
58 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
59 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
60 	for(i = 0; i < 5; i++){
61 		outportb(Crtx+1, crt42|0x03);		/* +DATA|+CLK */
62 		outportb(Crtx+1, crt42|0x02);		/* +DATA|-CLK */
63 	}
64 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
65 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
66 
67 	/*
68 	 * Now write the serial data word, framed by a start-bit and a stop-bit.
69 	 * The data is written using a modified Manchester encoding such that a
70 	 * data-bit is sampled on the rising edge of CLK and the complement of
71 	 * the data-bit is sampled on the previous falling edge of CLK.
72 	 */
73 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK (start-bit) */
74 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
75 
76 	for(i = 0; i < 24; i++){			/* serial data word */
77 		if(sdata & 0x01){
78 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK */
79 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK (falling edge) */
80 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK */
81 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK (rising edge) */
82 		}
83 		else {
84 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK */
85 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK (falling edge) */
86 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK */
87 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK (rising edge) */
88 		}
89 		sdata >>= 1;
90 	}
91 
92 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK (stop-bit) */
93 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
94 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK */
95 
96 	/*
97 	 * We always use REG2 in the ICD2061A.
98 	 */
99 	setcrt42(vga, ctlr, 0x02);
100 }
101 
102 static void
103 ch9294load(Vga *vga, Ctlr *ctlr)
104 {
105 	verbose("%s->clock->ch9294load\n", ctlr->name);
106 
107 	setcrt42(vga, ctlr, vga->i);
108 }
109 
110 static struct {
111 	char*	name;
112 	void	(*load)(Vga*, Ctlr*);
113 } clocks[] = {
114 	{ "icd2061a",	icd2061aload, },
115 	{ "ch9294",	ch9294load, },
116 	{ 0 },
117 };
118 
119 static void
120 init(Vga *vga, Ctlr *ctlr)
121 {
122 	char name[NAMELEN+1], *p;
123 	int i;
124 
125 	verbose("%s->init\n", ctlr->name);
126 
127 	if(vga->clock == 0)
128 		return;
129 
130 	/*
131 	 * Check we know about it.
132 	 */
133 	strncpy(name, vga->clock->name, NAMELEN);
134 	name[NAMELEN] = 0;
135 	if(p = strchr(name, '-'))
136 		*p = 0;
137 	for(i = 0; clocks[i].name; i++){
138 		if(strcmp(clocks[i].name, name) == 0)
139 			break;
140 	}
141 	if(clocks[i].name == 0)
142 		error("don't recognise s3clock \"%s\"\n", vga->clock->name);
143 
144 	if(vga->clock->init && (vga->clock->flag & Finit) == 0){
145 		(*vga->clock->init)(vga, vga->clock);
146 		(*vga->clock->init)(vga, vga->clock);
147 		(*vga->clock->init)(vga, vga->clock);
148 	}
149 
150 	/*
151 	 * If we don't already have a desired pclk,
152 	 * take it from the mode.
153 	 */
154 	if(vga->f == 0)
155 		vga->f = vga->mode->frequency;
156 	if(vga->f != VgaFreq0 && vga->f != VgaFreq1)
157 		vga->misc |= 0x0C;
158 
159 	ctlr->flag |= Finit;
160 }
161 
162 static void
163 load(Vga *vga, Ctlr *ctlr)
164 {
165 	char name[NAMELEN+1], *p;
166 	int i;
167 
168 	verbose("%s->load\n", ctlr->name);
169 
170 	if(vga->clock == 0 || (vga->clock->flag & Fload))
171 		return;
172 
173 	strncpy(name, vga->clock->name, NAMELEN);
174 	name[NAMELEN] = 0;
175 	if(p = strchr(name, '-'))
176 		*p = 0;
177 
178 	for(i = 0; clocks[i].name; i++){
179 		if(strcmp(clocks[i].name, name))
180 			continue;
181 		(*clocks[i].load)(vga, ctlr);
182 
183 		ctlr->flag |= Fload;
184 		return;
185 	}
186 }
187 
188 Ctlr s3clock = {
189 	"s3clock",			/* name */
190 	0,				/* snarf */
191 	0,				/* options */
192 	init,				/* init */
193 	load,				/* load */
194 	0,				/* dump */
195 };
196