/* $NetBSD: mq200subr.c,v 1.6 2005/12/11 12:17:33 christos Exp $ */ /*- * Copyright (c) 2001 TAKEMURA Shin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef _KERNEL #include __KERNEL_RCSID(0, "$NetBSD: mq200subr.c,v 1.6 2005/12/11 12:17:33 christos Exp $"); #include #include #include #include #else #include #endif #include #include #include #include "opt_mq200.h" #include "mq200var.h" #include "mq200reg.h" #include "mq200priv.h" #define ABS(a) ((a) < 0 ? -(a) : (a)) int mq200_depth_table[] = { [MQ200_GCC_1BPP] = 1, [MQ200_GCC_2BPP] = 2, [MQ200_GCC_4BPP] = 4, [MQ200_GCC_8BPP] = 8, [MQ200_GCC_16BPP] = 16, [MQ200_GCC_24BPP] = 32, [MQ200_GCC_ARGB888] = 32, [MQ200_GCC_ABGR888] = 32, [MQ200_GCC_16BPP_DIRECT] = 16, [MQ200_GCC_24BPP_DIRECT] = 32, [MQ200_GCC_ARGB888_DIRECT] = 32, [MQ200_GCC_ABGR888_DIRECT] = 32, }; struct mq200_crt_param mq200_crt_params[] = { [MQ200_CRT_640x480_60Hz] = { 640, 480, 25175, /* width, height, dot clock */ 800, /* HD Total */ 525, /* VD Total */ 656, 752, /* HS Start, HS End */ 490, 492, /* VS Start, VS End */ (MQ200_GC1CRTC_HSYNC_ACTVLOW | MQ200_GC1CRTC_VSYNC_ACTVLOW | MQ200_GC1CRTC_BLANK_PEDESTAL_EN), }, [MQ200_CRT_800x600_60Hz] = { 800, 600, 40000, /* width, height, dot clock */ 1054, /* HD Total */ 628, /* VD Total */ 839, 967, /* HS Start, HS End */ 601, 605, /* VS Start, VS End */ MQ200_GC1CRTC_BLANK_PEDESTAL_EN, }, [MQ200_CRT_1024x768_60Hz] = { 1024, 768, 65000, /* width, height, dot clock */ 1344, /* HD Total */ 806, /* VD Total */ 1048, 1184, /* HS Start, HS End */ 771, 777, /* VS Start, VS End */ (MQ200_GC1CRTC_HSYNC_ACTVLOW | MQ200_GC1CRTC_VSYNC_ACTVLOW | MQ200_GC1CRTC_BLANK_PEDESTAL_EN), }, }; int mq200_crt_nparams = sizeof(mq200_crt_params)/sizeof(*mq200_crt_params); /* * get PLL setting register value for given frequency */ int mq200_pllparam(int reqout, u_int32_t *res) { int n, m, p, out; int ref = 12288; int bn, bm, bp, e; e = ref; bn = 0; bp = 0; bm = 0; for (p = 0; p <= 4; p++) { for (n = 0; n < (1<<5); n++) { m = (reqout * ((n + 1) << p)) / ref - 1; out = ref * (m + 1) / ((n + 1) << p); if (0xff < m) break; if (40 <= m && 1000 <= ref/(n + 1) && 170000 <= ref*(m+1)/(n+1) && ref*(m+1)/(n+1) <= 340000 && ABS(reqout - out) <= e) { e = ABS(reqout - out); bn = n; bm = m; bp = p; } } } if (ref <= e) return (-1); #if 0 out = ref * (bm + 1) / ((bn + 1) << bp); printf("PLL: %d.%03d x (%d+1) / (%d+1) / %d = %d.%03d\n", ref / 1000, ref % 1000, bm, bn, (1<sc_regctxs[MQ200_I_PLL(1)]; enreg = &sc->sc_regctxs[MQ200_I_DCMISC]; enbit = MQ200_DCMISC_PLL1_ENABLE; break; case MQ200_CLOCK_PLL2: paramreg = &sc->sc_regctxs[MQ200_I_PLL(2)]; enreg = &sc->sc_regctxs[MQ200_I_PMC]; enbit = MQ200_PMC_PLL2_ENABLE; break; case MQ200_CLOCK_PLL3: paramreg = &sc->sc_regctxs[MQ200_I_PLL(3)]; enreg = &sc->sc_regctxs[MQ200_I_PMC]; enbit = MQ200_PMC_PLL3_ENABLE; break; default: printf("mq200: invalid PLL: %d\n", pll); return; } if (clock != 0 && clock != -1) { /* PLL Programming */ if (mq200_pllparam(clock, ¶m) != 0) { printf("mq200: invalid clock rate: %s %d.%03dMHz\n", mq200_clknames[pll], clock/1000, clock%1000); return; } mq200_mod(sc, paramreg, MQ200_PLL_PARAM_MASK, param); /* enable PLL */ mq200_on(sc, enreg, enbit); } DPRINTF("%s %d.%03dMHz\n", mq200_clknames[pll], clock/1000, clock%1000); } void mq200_setup_regctx(struct mq200_softc *sc) { int i; static int offsets[MQ200_I_MAX] = { [MQ200_I_DCMISC] = MQ200_DCMISCR, [MQ200_I_PLL(2)] = MQ200_PLL2R, [MQ200_I_PLL(3)] = MQ200_PLL3R, [MQ200_I_PMC] = MQ200_PMCR, [MQ200_I_MM01] = MQ200_MMR(1), [MQ200_I_GCC(MQ200_GC1)] = MQ200_GCCR(MQ200_GC1), [MQ200_I_GCC(MQ200_GC2)] = MQ200_GCCR(MQ200_GC2), }; for (i = 0; i < sizeof(offsets)/sizeof(*offsets); i++) { if (offsets[i] == 0) #ifdef MQ200_DEBUG if (i != MQ200_I_PMC) panic("%s(%d): register context %d is empty", __FILE__, __LINE__, i); #endif sc->sc_regctxs[i].offset = offsets[i]; } } void mq200_setup(struct mq200_softc *sc) { const struct mq200_clock_setting *clock; const struct mq200_crt_param *crt; clock = &sc->sc_md->md_clock_settings[sc->sc_flags & MQ200_SC_GC_MASK]; crt = sc->sc_crt; /* disable GC1 and GC2 */ //mq200_write(sc, MQ200_GCCR(MQ200_GC1), 0); mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)], 0); mq200_write(sc, MQ200_GC1CRTCR, 0); //mq200_write(sc, MQ200_GCCR(MQ200_GC2), 0); mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)], 0); while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS) /* busy wait */; /* * setup around clock */ /* setup eatch PLLs */ mq200_set_pll(sc, MQ200_CLOCK_PLL1, clock->pll1); mq200_set_pll(sc, MQ200_CLOCK_PLL2, clock->pll2); mq200_set_pll(sc, MQ200_CLOCK_PLL3, clock->pll3); if (sc->sc_flags & MQ200_SC_GC1_ENABLE) mq200_set_pll(sc, clock->gc[MQ200_GC1], crt->clock); /* setup MEMORY clock */ if (clock->mem == MQ200_CLOCK_PLL2) mq200_on(sc, &sc->sc_regctxs[MQ200_I_MM01], MQ200_MM01_CLK_PLL2); else mq200_off(sc, &sc->sc_regctxs[MQ200_I_MM01], MQ200_MM01_CLK_PLL2); DPRINTF("MEM: PLL%d\n", (clock->mem == MQ200_CLOCK_PLL2)?2:1); /* setup GE clock */ mq200_mod(sc, &sc->sc_regctxs[MQ200_I_PMC], MQ200_PMC_GE_CLK_MASK | MQ200_PMC_GE_ENABLE, (clock->ge << MQ200_PMC_GE_CLK_SHIFT) | MQ200_PMC_GE_ENABLE); DPRINTF(" GE: PLL%d\n", clock->ge); /* * setup GC1 (CRT contoller) */ if (sc->sc_flags & MQ200_SC_GC1_ENABLE) { /* GC03R Horizontal Display Control */ mq200_write(sc, MQ200_GCHDCR(MQ200_GC1), (((u_int32_t)crt->hdtotal-2)<width << MQ200_GCHDC_END_SHIFT)); /* GC03R Vertical Display Control */ mq200_write(sc, MQ200_GCVDCR(MQ200_GC1), (((u_int32_t)crt->vdtotal-1)<height - 1) << MQ200_GCVDC_END_SHIFT)); /* GC04R Horizontal Sync Control */ mq200_write(sc, MQ200_GCHSCR(MQ200_GC1), ((u_int32_t)crt->hsstart << MQ200_GCHSC_START_SHIFT) | ((u_int32_t)crt->hsend << MQ200_GCHSC_END_SHIFT)); /* GC05R Vertical Sync Control */ mq200_write(sc, MQ200_GCVSCR(MQ200_GC1), ((u_int32_t)crt->vsstart << MQ200_GCVSC_START_SHIFT) | ((u_int32_t)crt->vsend << MQ200_GCVSC_END_SHIFT)); /* GC00R GC1 Control */ //mq200_write(sc, MQ200_GCCR(MQ200_GC1), mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)], (MQ200_GCC_ENABLE | (clock->gc[MQ200_GC1] << MQ200_GCC_RCLK_SHIFT) | MQ200_GCC_MCLK_FD_1 | (1 << MQ200_GCC_MCLK_SD_SHIFT))); /* GC01R CRT Control */ mq200_write(sc, MQ200_GC1CRTCR, MQ200_GC1CRTC_DACEN | crt->opt); sc->sc_width[MQ200_GC1] = crt->width; sc->sc_height[MQ200_GC1] = crt->height; DPRINTF("GC1: %s\n", mq200_clknames[clock->gc[MQ200_GC1]]); } while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS) /* busy wait */; /* * setup GC2 (FP contoller) */ if (sc->sc_flags & MQ200_SC_GC2_ENABLE) { //mq200_write(sc, MQ200_GCCR(MQ200_GC2), mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)], MQ200_GCC_ENABLE | (clock->gc[MQ200_GC2] << MQ200_GCC_RCLK_SHIFT) | MQ200_GCC_MCLK_FD_1 | (1 << MQ200_GCC_MCLK_SD_SHIFT)); DPRINTF("GC2: %s\n", mq200_clknames[clock->gc[MQ200_GC2]]); } while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS) /* busy wait */; /* * disable unused PLLs */ if (clock->pll1 == 0) { DPRINTF("PLL1 disable\n"); mq200_off(sc, &sc->sc_regctxs[MQ200_I_DCMISC], MQ200_DCMISC_PLL1_ENABLE); } if (clock->pll2 == 0) { DPRINTF("PLL2 disable\n"); mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC], MQ200_PMC_PLL2_ENABLE); } if (clock->pll3 == 0) { DPRINTF("PLL3 disable\n"); mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC], MQ200_PMC_PLL3_ENABLE); } } void mq200_win_enable(struct mq200_softc *sc, int gc, u_int32_t depth, u_int32_t start, int width, int height, int stride) { DPRINTF("enable window on GC%d: %dx%d(%dx%d)\n", gc + 1, width, height, sc->sc_width[gc], sc->sc_height[gc]); if (sc->sc_width[gc] < width) { if (mq200_depth_table[depth]) start += (height - sc->sc_height[gc]) * mq200_depth_table[depth] / 8; width = sc->sc_width[gc]; } if (sc->sc_height[gc] < height) { start += (height - sc->sc_height[gc]) * stride; height = sc->sc_height[gc]; } /* GC08R Window Horizontal Control */ mq200_write(sc, MQ200_GCWHCR(gc), (((u_int32_t)width - 1) << MQ200_GCWHC_WIDTH_SHIFT) | ((sc->sc_width[gc] - width)/2)); /* GC09R Window Vertical Control */ mq200_write(sc, MQ200_GCWVCR(gc), (((u_int32_t)height - 1) << MQ200_GCWVC_HEIGHT_SHIFT) | ((sc->sc_height[gc] - height)/2)); /* GC00R GC Control */ mq200_mod(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)], (MQ200_GCC_WINEN | MQ200_GCC_DEPTH_MASK), (MQ200_GCC_WINEN | (depth << MQ200_GCC_DEPTH_SHIFT))); } void mq200_win_disable(struct mq200_softc *sc, int gc) { /* GC00R GC Control */ mq200_off(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)], MQ200_GCC_WINEN); }