mq200subr.c revision 1.4 1 /* $NetBSD: mq200subr.c,v 1.4 2003/07/15 02:29:30 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 2001 TAKEMURA Shin
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32 #ifdef _KERNEL
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: mq200subr.c,v 1.4 2003/07/15 02:29:30 lukem Exp $");
35
36 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 #else
41 #include <stdio.h>
42 #endif
43 #include <sys/types.h>
44
45 #include <machine/platid.h>
46 #include <machine/platid_mask.h>
47
48 #include "opt_mq200.h"
49 #include "mq200var.h"
50 #include "mq200reg.h"
51 #include "mq200priv.h"
52
53 #define ABS(a) ((a) < 0 ? -(a) : (a))
54
55 int mq200_depth_table[] = {
56 [MQ200_GCC_1BPP] = 1,
57 [MQ200_GCC_2BPP] = 2,
58 [MQ200_GCC_4BPP] = 4,
59 [MQ200_GCC_8BPP] = 8,
60 [MQ200_GCC_16BPP] = 16,
61 [MQ200_GCC_24BPP] = 32,
62 [MQ200_GCC_ARGB888] = 32,
63 [MQ200_GCC_ABGR888] = 32,
64 [MQ200_GCC_16BPP_DIRECT] = 16,
65 [MQ200_GCC_24BPP_DIRECT] = 32,
66 [MQ200_GCC_ARGB888_DIRECT] = 32,
67 [MQ200_GCC_ABGR888_DIRECT] = 32,
68 };
69
70 struct mq200_crt_param mq200_crt_params[] = {
71 [MQ200_CRT_640x480_60Hz] =
72 { 640, 480, 25175, /* width, height, dot clock */
73 800, /* HD Total */
74 525, /* VD Total */
75 656, 752, /* HS Start, HS End */
76 490, 492, /* VS Start, VS End */
77 (MQ200_GC1CRTC_HSYNC_ACTVLOW |
78 MQ200_GC1CRTC_VSYNC_ACTVLOW |
79 MQ200_GC1CRTC_BLANK_PEDESTAL_EN),
80 },
81 [MQ200_CRT_800x600_60Hz] =
82 { 800, 600, 40000, /* width, height, dot clock */
83 1054, /* HD Total */
84 628, /* VD Total */
85 839, 967, /* HS Start, HS End */
86 601, 605, /* VS Start, VS End */
87 MQ200_GC1CRTC_BLANK_PEDESTAL_EN,
88 },
89 [MQ200_CRT_1024x768_60Hz] =
90 { 1024, 768, 65000, /* width, height, dot clock */
91 1344, /* HD Total */
92 806, /* VD Total */
93 1048, 1184, /* HS Start, HS End */
94 771, 777, /* VS Start, VS End */
95 (MQ200_GC1CRTC_HSYNC_ACTVLOW |
96 MQ200_GC1CRTC_VSYNC_ACTVLOW |
97 MQ200_GC1CRTC_BLANK_PEDESTAL_EN),
98 },
99 };
100
101 int mq200_crt_nparams = sizeof(mq200_crt_params)/sizeof(*mq200_crt_params);
102
103 /*
104 * get PLL setting register value for given frequency
105 */
106 void
107 mq200_pllparam(int reqout, u_int32_t *res)
108 {
109 int n, m, p, out;
110 int ref = 12288;
111 int bn, bm, bp, e;
112
113 e = ref;
114 for (p = 0; p <= 4; p++) {
115 for (n = 0; n < (1<<5); n++) {
116 m = (reqout * ((n + 1) << p)) / ref - 1;
117 out = ref * (m + 1) / ((n + 1) << p);
118 if (0xff < m)
119 break;
120 if (40 <= m &&
121 1000 <= ref/(n + 1) &&
122 170000 <= ref*(m+1)/(n+1) &&
123 ref*(m+1)/(n+1) <= 340000 &&
124 ABS(reqout - out) <= e) {
125 e = ABS(reqout - out);
126 bn = n;
127 bm = m;
128 bp = p;
129 }
130 }
131 }
132
133 #if 0
134 out = ref * (bm + 1) / ((bn + 1) << bp);
135 printf("PLL: %d.%03d x (%d+1) / (%d+1) / %d = %d.%03d\n",
136 ref / 1000, ref % 1000, bm, bn, (1<<bp),
137 out / 1000, out % 1000);
138 #endif
139 *res = ((bm << MQ200_PLL_M_SHIFT) |
140 (bn << MQ200_PLL_N_SHIFT) |
141 (bp << MQ200_PLL_P_SHIFT));
142 }
143
144 void
145 mq200_set_pll(struct mq200_softc *sc, int pll, int clock)
146 {
147 struct mq200_regctx *paramreg, *enreg;
148 u_int32_t param, enbit;
149
150 switch (pll) {
151 case MQ200_CLOCK_PLL1:
152 paramreg = &sc->sc_regctxs[MQ200_I_PLL(1)];
153 enreg = &sc->sc_regctxs[MQ200_I_DCMISC];
154 enbit = MQ200_DCMISC_PLL1_ENABLE;
155 break;
156 case MQ200_CLOCK_PLL2:
157 paramreg = &sc->sc_regctxs[MQ200_I_PLL(2)];
158 enreg = &sc->sc_regctxs[MQ200_I_PMC];
159 enbit = MQ200_PMC_PLL2_ENABLE;
160 break;
161 case MQ200_CLOCK_PLL3:
162 paramreg = &sc->sc_regctxs[MQ200_I_PLL(3)];
163 enreg = &sc->sc_regctxs[MQ200_I_PMC];
164 enbit = MQ200_PMC_PLL3_ENABLE;
165 break;
166 default:
167 printf("mq200: invalid PLL: %d\n", pll);
168 return;
169 }
170 if (clock != 0 && clock != -1) {
171 /* PLL Programming */
172 mq200_pllparam(clock, ¶m);
173 mq200_mod(sc, paramreg, MQ200_PLL_PARAM_MASK, param);
174 /* enable PLL */
175 mq200_on(sc, enreg, enbit);
176 }
177
178 DPRINTF("%s %d.%03dMHz\n",
179 mq200_clknames[pll], clock/1000, clock%1000);
180 }
181
182 void
183 mq200_setup_regctx(struct mq200_softc *sc)
184 {
185 int i;
186 static int offsets[MQ200_I_MAX] = {
187 [MQ200_I_DCMISC] = MQ200_DCMISCR,
188 [MQ200_I_PLL(2)] = MQ200_PLL2R,
189 [MQ200_I_PLL(3)] = MQ200_PLL3R,
190 [MQ200_I_PMC] = MQ200_PMCR,
191 [MQ200_I_MM01] = MQ200_MMR(1),
192 [MQ200_I_GCC(MQ200_GC1)] = MQ200_GCCR(MQ200_GC1),
193 [MQ200_I_GCC(MQ200_GC2)] = MQ200_GCCR(MQ200_GC2),
194 };
195
196 for (i = 0; i < sizeof(offsets)/sizeof(*offsets); i++) {
197 if (offsets[i] == 0)
198 #ifdef MQ200_DEBUG
199 if (i != MQ200_I_PMC)
200 panic("%s(%d): register context %d is empty",
201 __FILE__, __LINE__, i);
202 #endif
203 sc->sc_regctxs[i].offset = offsets[i];
204 }
205 }
206
207 void
208 mq200_setup(struct mq200_softc *sc)
209 {
210 const struct mq200_clock_setting *clock;
211 const struct mq200_crt_param *crt;
212
213 clock = &sc->sc_md->md_clock_settings[sc->sc_flags & MQ200_SC_GC_MASK];
214 crt = sc->sc_crt;
215
216 /* disable GC1 and GC2 */
217 //mq200_write(sc, MQ200_GCCR(MQ200_GC1), 0);
218 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)], 0);
219 mq200_write(sc, MQ200_GC1CRTCR, 0);
220 //mq200_write(sc, MQ200_GCCR(MQ200_GC2), 0);
221 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)], 0);
222
223 while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS)
224 /* busy wait */;
225
226 /*
227 * setup around clock
228 */
229 /* setup eatch PLLs */
230 mq200_set_pll(sc, MQ200_CLOCK_PLL1, clock->pll1);
231 mq200_set_pll(sc, MQ200_CLOCK_PLL2, clock->pll2);
232 mq200_set_pll(sc, MQ200_CLOCK_PLL3, clock->pll3);
233 if (sc->sc_flags & MQ200_SC_GC1_ENABLE)
234 mq200_set_pll(sc, clock->gc[MQ200_GC1], crt->clock);
235
236 /* setup MEMORY clock */
237 if (clock->mem == MQ200_CLOCK_PLL2)
238 mq200_on(sc, &sc->sc_regctxs[MQ200_I_MM01],
239 MQ200_MM01_CLK_PLL2);
240 else
241 mq200_off(sc, &sc->sc_regctxs[MQ200_I_MM01],
242 MQ200_MM01_CLK_PLL2);
243 DPRINTF("MEM: PLL%d\n", (clock->mem == MQ200_CLOCK_PLL2)?2:1);
244
245 /* setup GE clock */
246 mq200_mod(sc, &sc->sc_regctxs[MQ200_I_PMC],
247 MQ200_PMC_GE_CLK_MASK | MQ200_PMC_GE_ENABLE,
248 (clock->ge << MQ200_PMC_GE_CLK_SHIFT) | MQ200_PMC_GE_ENABLE);
249 DPRINTF(" GE: PLL%d\n", clock->ge);
250
251 /*
252 * setup GC1 (CRT contoller)
253 */
254 if (sc->sc_flags & MQ200_SC_GC1_ENABLE) {
255 /* GC03R Horizontal Display Control */
256 mq200_write(sc, MQ200_GCHDCR(MQ200_GC1),
257 (((u_int32_t)crt->hdtotal-2)<<MQ200_GC1HDC_TOTAL_SHIFT) |
258 ((u_int32_t)crt->width << MQ200_GCHDC_END_SHIFT));
259
260 /* GC03R Vertical Display Control */
261 mq200_write(sc, MQ200_GCVDCR(MQ200_GC1),
262 (((u_int32_t)crt->vdtotal-1)<<MQ200_GC1VDC_TOTAL_SHIFT) |
263 (((u_int32_t)crt->height - 1) << MQ200_GCVDC_END_SHIFT));
264
265 /* GC04R Horizontal Sync Control */
266 mq200_write(sc, MQ200_GCHSCR(MQ200_GC1),
267 ((u_int32_t)crt->hsstart << MQ200_GCHSC_START_SHIFT) |
268 ((u_int32_t)crt->hsend << MQ200_GCHSC_END_SHIFT));
269
270 /* GC05R Vertical Sync Control */
271 mq200_write(sc, MQ200_GCVSCR(MQ200_GC1),
272 ((u_int32_t)crt->vsstart << MQ200_GCVSC_START_SHIFT) |
273 ((u_int32_t)crt->vsend << MQ200_GCVSC_END_SHIFT));
274
275 /* GC00R GC1 Control */
276 //mq200_write(sc, MQ200_GCCR(MQ200_GC1),
277 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)],
278 (MQ200_GCC_ENABLE |
279 (clock->gc[MQ200_GC1] << MQ200_GCC_RCLK_SHIFT) |
280 MQ200_GCC_MCLK_FD_1 |
281 (1 << MQ200_GCC_MCLK_SD_SHIFT)));
282
283 /* GC01R CRT Control */
284 mq200_write(sc, MQ200_GC1CRTCR,
285 MQ200_GC1CRTC_DACEN | crt->opt);
286
287 sc->sc_width[MQ200_GC1] = crt->width;
288 sc->sc_height[MQ200_GC1] = crt->height;
289
290 DPRINTF("GC1: %s\n",
291 mq200_clknames[clock->gc[MQ200_GC1]]);
292 }
293
294 while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS)
295 /* busy wait */;
296
297 /*
298 * setup GC2 (FP contoller)
299 */
300 if (sc->sc_flags & MQ200_SC_GC2_ENABLE) {
301 //mq200_write(sc, MQ200_GCCR(MQ200_GC2),
302 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)],
303 MQ200_GCC_ENABLE |
304 (clock->gc[MQ200_GC2] << MQ200_GCC_RCLK_SHIFT) |
305 MQ200_GCC_MCLK_FD_1 | (1 << MQ200_GCC_MCLK_SD_SHIFT));
306 DPRINTF("GC2: %s\n",
307 mq200_clknames[clock->gc[MQ200_GC2]]);
308 }
309
310 while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS)
311 /* busy wait */;
312
313 /*
314 * disable unused PLLs
315 */
316 if (clock->pll1 == 0) {
317 DPRINTF("PLL1 disable\n");
318 mq200_off(sc, &sc->sc_regctxs[MQ200_I_DCMISC],
319 MQ200_DCMISC_PLL1_ENABLE);
320 }
321 if (clock->pll2 == 0) {
322 DPRINTF("PLL2 disable\n");
323 mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC],
324 MQ200_PMC_PLL2_ENABLE);
325 }
326 if (clock->pll3 == 0) {
327 DPRINTF("PLL3 disable\n");
328 mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC],
329 MQ200_PMC_PLL3_ENABLE);
330 }
331 }
332
333 void
334 mq200_win_enable(struct mq200_softc *sc, int gc,
335 u_int32_t depth, u_int32_t start,
336 int width, int height, int stride)
337 {
338
339 DPRINTF("enable window on GC%d: %dx%d(%dx%d)\n",
340 gc + 1, width, height, sc->sc_width[gc], sc->sc_height[gc]);
341
342 if (sc->sc_width[gc] < width) {
343 if (mq200_depth_table[depth])
344 start += (height - sc->sc_height[gc]) *
345 mq200_depth_table[depth] / 8;
346 width = sc->sc_width[gc];
347 }
348
349 if (sc->sc_height[gc] < height) {
350 start += (height - sc->sc_height[gc]) * stride;
351 height = sc->sc_height[gc];
352 }
353
354 /* GC08R Window Horizontal Control */
355 mq200_write(sc, MQ200_GCWHCR(gc),
356 (((u_int32_t)width - 1) << MQ200_GCWHC_WIDTH_SHIFT) |
357 ((sc->sc_width[gc] - width)/2));
358
359 /* GC09R Window Vertical Control */
360 mq200_write(sc, MQ200_GCWVCR(gc),
361 (((u_int32_t)height - 1) << MQ200_GCWVC_HEIGHT_SHIFT) |
362 ((sc->sc_height[gc] - height)/2));
363
364 /* GC00R GC Control */
365 mq200_mod(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)],
366 (MQ200_GCC_WINEN | MQ200_GCC_DEPTH_MASK),
367 (MQ200_GCC_WINEN | (depth << MQ200_GCC_DEPTH_SHIFT)));
368 }
369
370 void
371 mq200_win_disable(struct mq200_softc *sc, int gc)
372 {
373 /* GC00R GC Control */
374 mq200_off(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)], MQ200_GCC_WINEN);
375 }
376