1 /* $NetBSD: mq200subr.c,v 1.2 2002/05/11 14:10:06 takemura 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/param.h> 34 #include <sys/kernel.h> 35 #include <sys/systm.h> 36 #include <sys/device.h> 37 #else 38 #include <stdio.h> 39 #endif 40 #include <sys/types.h> 41 42 #include <machine/platid.h> 43 #include <machine/platid_mask.h> 44 45 #include "opt_mq200.h" 46 #include "mq200var.h" 47 #include "mq200reg.h" 48 #include "mq200priv.h" 49 50 #define ABS(a) ((a) < 0 ? -(a) : (a)) 51 52 int mq200_depth_table[] = { 53 [MQ200_GCC_1BPP] = 1, 54 [MQ200_GCC_2BPP] = 2, 55 [MQ200_GCC_4BPP] = 4, 56 [MQ200_GCC_8BPP] = 8, 57 [MQ200_GCC_16BPP] = 16, 58 [MQ200_GCC_24BPP] = 32, 59 [MQ200_GCC_ARGB888] = 32, 60 [MQ200_GCC_ABGR888] = 32, 61 [MQ200_GCC_16BPP_DIRECT] = 16, 62 [MQ200_GCC_24BPP_DIRECT] = 32, 63 [MQ200_GCC_ARGB888_DIRECT] = 32, 64 [MQ200_GCC_ABGR888_DIRECT] = 32, 65 }; 66 67 struct mq200_crt_param mq200_crt_params[] = { 68 [MQ200_CRT_640x480_60Hz] = 69 { 640, 480, 25175, /* width, height, dot clock */ 70 800, /* HD Total */ 71 525, /* VD Total */ 72 656, 752, /* HS Start, HS End */ 73 490, 492, /* VS Start, VS End */ 74 (MQ200_GC1CRTC_HSYNC_ACTVLOW | 75 MQ200_GC1CRTC_VSYNC_ACTVLOW | 76 MQ200_GC1CRTC_BLANK_PEDESTAL_EN), 77 }, 78 [MQ200_CRT_800x600_60Hz] = 79 { 800, 600, 40000, /* width, height, dot clock */ 80 1054, /* HD Total */ 81 628, /* VD Total */ 82 839, 967, /* HS Start, HS End */ 83 601, 605, /* VS Start, VS End */ 84 MQ200_GC1CRTC_BLANK_PEDESTAL_EN, 85 }, 86 [MQ200_CRT_1024x768_60Hz] = 87 { 1024, 768, 65000, /* width, height, dot clock */ 88 1344, /* HD Total */ 89 806, /* VD Total */ 90 1048, 1184, /* HS Start, HS End */ 91 771, 777, /* VS Start, VS End */ 92 (MQ200_GC1CRTC_HSYNC_ACTVLOW | 93 MQ200_GC1CRTC_VSYNC_ACTVLOW | 94 MQ200_GC1CRTC_BLANK_PEDESTAL_EN), 95 }, 96 }; 97 98 int mq200_crt_nparams = sizeof(mq200_crt_params)/sizeof(*mq200_crt_params); 99 100 /* 101 * get PLL setting register value for given frequency 102 */ 103 void 104 mq200_pllparam(int reqout, u_int32_t *res) 105 { 106 int n, m, p, out; 107 int ref = 12288; 108 int bn, bm, bp, e; 109 110 e = ref; 111 for (p = 0; p <= 4; p++) { 112 for (n = 0; n < (1<<5); n++) { 113 m = (reqout * ((n + 1) << p)) / ref - 1; 114 out = ref * (m + 1) / ((n + 1) << p); 115 if (0xff < m) 116 break; 117 if (40 <= m && 118 1000 <= ref/(n + 1) && 119 170000 <= ref*(m+1)/(n+1) && 120 ref*(m+1)/(n+1) <= 340000 && 121 ABS(reqout - out) <= e) { 122 e = ABS(reqout - out); 123 bn = n; 124 bm = m; 125 bp = p; 126 } 127 } 128 } 129 130 #if 0 131 out = ref * (bm + 1) / ((bn + 1) << bp); 132 printf("PLL: %d.%03d x (%d+1) / (%d+1) / %d = %d.%03d\n", 133 ref / 1000, ref % 1000, bm, bn, (1<<bp), 134 out / 1000, out % 1000); 135 #endif 136 *res = ((bm << MQ200_PLL_M_SHIFT) | 137 (bn << MQ200_PLL_N_SHIFT) | 138 (bp << MQ200_PLL_P_SHIFT)); 139 } 140 141 void 142 mq200_set_pll(struct mq200_softc *sc, int pll, int clock) 143 { 144 struct mq200_regctx *paramreg, *enreg; 145 u_int32_t param, enbit; 146 147 switch (pll) { 148 case MQ200_CLOCK_PLL1: 149 paramreg = &sc->sc_regctxs[MQ200_I_PLL(1)]; 150 enreg = &sc->sc_regctxs[MQ200_I_DCMISC]; 151 enbit = MQ200_DCMISC_PLL1_ENABLE; 152 break; 153 case MQ200_CLOCK_PLL2: 154 paramreg = &sc->sc_regctxs[MQ200_I_PLL(2)]; 155 enreg = &sc->sc_regctxs[MQ200_I_PMC]; 156 enbit = MQ200_PMC_PLL2_ENABLE; 157 break; 158 case MQ200_CLOCK_PLL3: 159 paramreg = &sc->sc_regctxs[MQ200_I_PLL(3)]; 160 enreg = &sc->sc_regctxs[MQ200_I_PMC]; 161 enbit = MQ200_PMC_PLL3_ENABLE; 162 break; 163 default: 164 printf("mq200: invalid PLL: %d\n", pll); 165 return; 166 } 167 if (clock != 0 && clock != -1) { 168 /* PLL Programming */ 169 mq200_pllparam(clock, ¶m); 170 mq200_mod(sc, paramreg, MQ200_PLL_PARAM_MASK, param); 171 /* enable PLL */ 172 mq200_on(sc, enreg, enbit); 173 } 174 175 DPRINTF("%s %d.%03dMHz\n", 176 mq200_clknames[pll], clock/1000, clock%1000); 177 } 178 179 void 180 mq200_setup_regctx(struct mq200_softc *sc) 181 { 182 int i; 183 static int offsets[MQ200_I_MAX] = { 184 [MQ200_I_DCMISC] = MQ200_DCMISCR, 185 [MQ200_I_PLL(2)] = MQ200_PLL2R, 186 [MQ200_I_PLL(3)] = MQ200_PLL3R, 187 [MQ200_I_PMC] = MQ200_PMCR, 188 [MQ200_I_MM01] = MQ200_MMR(1), 189 [MQ200_I_GCC(MQ200_GC1)] = MQ200_GCCR(MQ200_GC1), 190 [MQ200_I_GCC(MQ200_GC2)] = MQ200_GCCR(MQ200_GC2), 191 }; 192 193 for (i = 0; i < sizeof(offsets)/sizeof(*offsets); i++) { 194 if (offsets[i] == 0) 195 #ifdef MQ200_DEBUG 196 if (i != MQ200_I_PMC) 197 panic("%s(%d): register context %d is empty\n", 198 __FILE__, __LINE__, i); 199 #endif 200 sc->sc_regctxs[i].offset = offsets[i]; 201 } 202 } 203 204 void 205 mq200_setup(struct mq200_softc *sc) 206 { 207 const struct mq200_clock_setting *clock; 208 const struct mq200_crt_param *crt; 209 210 clock = &sc->sc_md->md_clock_settings[sc->sc_flags & MQ200_SC_GC_MASK]; 211 crt = sc->sc_crt; 212 213 /* disable GC1 and GC2 */ 214 //mq200_write(sc, MQ200_GCCR(MQ200_GC1), 0); 215 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)], 0); 216 mq200_write(sc, MQ200_GC1CRTCR, 0); 217 //mq200_write(sc, MQ200_GCCR(MQ200_GC2), 0); 218 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)], 0); 219 220 while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS) 221 /* busy wait */; 222 223 /* 224 * setup around clock 225 */ 226 /* setup eatch PLLs */ 227 mq200_set_pll(sc, MQ200_CLOCK_PLL1, clock->pll1); 228 mq200_set_pll(sc, MQ200_CLOCK_PLL2, clock->pll2); 229 mq200_set_pll(sc, MQ200_CLOCK_PLL3, clock->pll3); 230 if (sc->sc_flags & MQ200_SC_GC1_ENABLE) 231 mq200_set_pll(sc, clock->gc[MQ200_GC1], crt->clock); 232 233 /* setup MEMORY clock */ 234 if (clock->mem == MQ200_CLOCK_PLL2) 235 mq200_on(sc, &sc->sc_regctxs[MQ200_I_MM01], 236 MQ200_MM01_CLK_PLL2); 237 else 238 mq200_off(sc, &sc->sc_regctxs[MQ200_I_MM01], 239 MQ200_MM01_CLK_PLL2); 240 DPRINTF("MEM: PLL%d\n", (clock->mem == MQ200_CLOCK_PLL2)?2:1); 241 242 /* setup GE clock */ 243 mq200_mod(sc, &sc->sc_regctxs[MQ200_I_PMC], 244 MQ200_PMC_GE_CLK_MASK | MQ200_PMC_GE_ENABLE, 245 (clock->ge << MQ200_PMC_GE_CLK_SHIFT) | MQ200_PMC_GE_ENABLE); 246 DPRINTF(" GE: PLL%d\n", clock->ge); 247 248 /* 249 * setup GC1 (CRT contoller) 250 */ 251 if (sc->sc_flags & MQ200_SC_GC1_ENABLE) { 252 /* GC03R Horizontal Display Control */ 253 mq200_write(sc, MQ200_GCHDCR(MQ200_GC1), 254 (((u_int32_t)crt->hdtotal-2)<<MQ200_GC1HDC_TOTAL_SHIFT) | 255 ((u_int32_t)crt->width << MQ200_GCHDC_END_SHIFT)); 256 257 /* GC03R Vertical Display Control */ 258 mq200_write(sc, MQ200_GCVDCR(MQ200_GC1), 259 (((u_int32_t)crt->vdtotal-1)<<MQ200_GC1VDC_TOTAL_SHIFT) | 260 (((u_int32_t)crt->height - 1) << MQ200_GCVDC_END_SHIFT)); 261 262 /* GC04R Horizontal Sync Control */ 263 mq200_write(sc, MQ200_GCHSCR(MQ200_GC1), 264 ((u_int32_t)crt->hsstart << MQ200_GCHSC_START_SHIFT) | 265 ((u_int32_t)crt->hsend << MQ200_GCHSC_END_SHIFT)); 266 267 /* GC05R Vertical Sync Control */ 268 mq200_write(sc, MQ200_GCVSCR(MQ200_GC1), 269 ((u_int32_t)crt->vsstart << MQ200_GCVSC_START_SHIFT) | 270 ((u_int32_t)crt->vsend << MQ200_GCVSC_END_SHIFT)); 271 272 /* GC00R GC1 Control */ 273 //mq200_write(sc, MQ200_GCCR(MQ200_GC1), 274 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)], 275 (MQ200_GCC_ENABLE | 276 (clock->gc[MQ200_GC1] << MQ200_GCC_RCLK_SHIFT) | 277 MQ200_GCC_MCLK_FD_1 | 278 (1 << MQ200_GCC_MCLK_SD_SHIFT))); 279 280 /* GC01R CRT Control */ 281 mq200_write(sc, MQ200_GC1CRTCR, 282 MQ200_GC1CRTC_DACEN | crt->opt); 283 284 sc->sc_width[MQ200_GC1] = crt->width; 285 sc->sc_height[MQ200_GC1] = crt->height; 286 287 DPRINTF("GC1: %s\n", 288 mq200_clknames[clock->gc[MQ200_GC1]]); 289 } 290 291 while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS) 292 /* busy wait */; 293 294 /* 295 * setup GC2 (FP contoller) 296 */ 297 if (sc->sc_flags & MQ200_SC_GC2_ENABLE) { 298 //mq200_write(sc, MQ200_GCCR(MQ200_GC2), 299 mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)], 300 MQ200_GCC_ENABLE | 301 (clock->gc[MQ200_GC2] << MQ200_GCC_RCLK_SHIFT) | 302 MQ200_GCC_MCLK_FD_1 | (1 << MQ200_GCC_MCLK_SD_SHIFT)); 303 DPRINTF("GC2: %s\n", 304 mq200_clknames[clock->gc[MQ200_GC2]]); 305 } 306 307 while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS) 308 /* busy wait */; 309 310 /* 311 * disable unused PLLs 312 */ 313 if (clock->pll1 == 0) { 314 DPRINTF("PLL1 disable\n"); 315 mq200_off(sc, &sc->sc_regctxs[MQ200_I_DCMISC], 316 MQ200_DCMISC_PLL1_ENABLE); 317 } 318 if (clock->pll2 == 0) { 319 DPRINTF("PLL2 disable\n"); 320 mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC], 321 MQ200_PMC_PLL2_ENABLE); 322 } 323 if (clock->pll3 == 0) { 324 DPRINTF("PLL3 disable\n"); 325 mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC], 326 MQ200_PMC_PLL3_ENABLE); 327 } 328 } 329 330 void 331 mq200_win_enable(struct mq200_softc *sc, int gc, 332 u_int32_t depth, u_int32_t start, 333 int width, int height, int stride) 334 { 335 336 DPRINTF("enable window on GC%d: %dx%d(%dx%d)\n", 337 gc + 1, width, height, sc->sc_width[gc], sc->sc_height[gc]); 338 339 if (sc->sc_width[gc] < width) { 340 if (mq200_depth_table[depth]) 341 start += (height - sc->sc_height[gc]) * 342 mq200_depth_table[depth] / 8; 343 width = sc->sc_width[gc]; 344 } 345 346 if (sc->sc_height[gc] < height) { 347 start += (height - sc->sc_height[gc]) * stride; 348 height = sc->sc_height[gc]; 349 } 350 351 /* GC08R Window Horizontal Control */ 352 mq200_write(sc, MQ200_GCWHCR(gc), 353 (((u_int32_t)width - 1) << MQ200_GCWHC_WIDTH_SHIFT) | 354 ((sc->sc_width[gc] - width)/2)); 355 356 /* GC09R Window Vertical Control */ 357 mq200_write(sc, MQ200_GCWVCR(gc), 358 (((u_int32_t)height - 1) << MQ200_GCWVC_HEIGHT_SHIFT) | 359 ((sc->sc_height[gc] - height)/2)); 360 361 /* GC00R GC Control */ 362 mq200_mod(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)], 363 (MQ200_GCC_WINEN | MQ200_GCC_DEPTH_MASK), 364 (MQ200_GCC_WINEN | (depth << MQ200_GCC_DEPTH_SHIFT))); 365 } 366 367 void 368 mq200_win_disable(struct mq200_softc *sc, int gc) 369 { 370 /* GC00R GC Control */ 371 mq200_off(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)], MQ200_GCC_WINEN); 372 } 373