1 /*        $NetBSD: mq200subr.c,v 1.7 2019/08/21 04:17:40 msaitoh 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.7 2019/08/21 04:17:40 msaitoh 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 int
mq200_pllparam(int reqout,u_int32_t * res)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           bn = 0; bp = 0; bm = 0;
115           for (p = 0; p <= 4; p++) {
116                     for (n = 0; n < (1<<5); n++) {
117                               m = (reqout * ((n + 1) << p)) / ref - 1;
118                               out = ref * (m + 1) / ((n + 1) << p);
119                               if (0xff < m)
120                                         break;
121                               if (40 <= m &&
122                                   1000 <= ref/(n + 1) &&
123                                   170000 <= ref*(m+1)/(n+1) &&
124                                   ref*(m+1)/(n+1) <= 340000 &&
125                                   ABS(reqout - out) <= e) {
126                                         e = ABS(reqout - out);
127                                         bn = n;
128                                         bm = m;
129                                         bp = p;
130                               }
131                     }
132           }
133           if (ref <= e)
134                     return (-1);
135 
136 #if 0
137           out = ref * (bm + 1) / ((bn + 1) << bp);
138           printf("PLL: %d.%03d x (%d+1) / (%d+1) / %d = %d.%03d\n",
139               ref / 1000, ref % 1000, bm, bn, (1<<bp),
140               out / 1000, out % 1000);
141 #endif
142           *res = ((bm << MQ200_PLL_M_SHIFT) |
143                     (bn << MQ200_PLL_N_SHIFT) |
144                     (bp << MQ200_PLL_P_SHIFT));
145 
146           return (0);
147 }
148 
149 void
mq200_set_pll(struct mq200_softc * sc,int pll,int clock)150 mq200_set_pll(struct mq200_softc *sc, int pll, int clock)
151 {
152           struct mq200_regctx *paramreg, *enreg;
153           u_int32_t param, enbit;
154 
155           switch (pll) {
156           case MQ200_CLOCK_PLL1:
157                     paramreg = &sc->sc_regctxs[MQ200_I_PLL(1)];
158                     enreg = &sc->sc_regctxs[MQ200_I_DCMISC];
159                     enbit = MQ200_DCMISC_PLL1_ENABLE;
160                     break;
161           case MQ200_CLOCK_PLL2:
162                     paramreg = &sc->sc_regctxs[MQ200_I_PLL(2)];
163                     enreg = &sc->sc_regctxs[MQ200_I_PMC];
164                     enbit = MQ200_PMC_PLL2_ENABLE;
165                     break;
166           case MQ200_CLOCK_PLL3:
167                     paramreg = &sc->sc_regctxs[MQ200_I_PLL(3)];
168                     enreg = &sc->sc_regctxs[MQ200_I_PMC];
169                     enbit = MQ200_PMC_PLL3_ENABLE;
170                     break;
171           default:
172                     printf("mq200: invalid PLL: %d\n", pll);
173                     return;
174           }
175           if (clock != 0 && clock != -1) {
176                     /* PLL Programming  */
177                     if (mq200_pllparam(clock, &param) != 0) {
178                               printf("mq200: invalid clock rate: %s %d.%03dMHz\n",
179                                   mq200_clknames[pll], clock/1000, clock%1000);
180                               return;
181                     }
182                     mq200_mod(sc, paramreg, MQ200_PLL_PARAM_MASK, param);
183                     /* enable PLL       */
184                     mq200_on(sc, enreg, enbit);
185           }
186 
187           DPRINTF("%s %d.%03dMHz\n",
188               mq200_clknames[pll], clock/1000, clock%1000);
189 }
190 
191 void
mq200_setup_regctx(struct mq200_softc * sc)192 mq200_setup_regctx(struct mq200_softc *sc)
193 {
194           int i;
195           static int offsets[MQ200_I_MAX] = {
196                     [MQ200_I_DCMISC] =            MQ200_DCMISCR,
197                     [MQ200_I_PLL(2)] =            MQ200_PLL2R,
198                     [MQ200_I_PLL(3)] =            MQ200_PLL3R,
199                     [MQ200_I_PMC] =                         MQ200_PMCR,
200                     [MQ200_I_MM01] =              MQ200_MMR(1),
201                     [MQ200_I_GCC(MQ200_GC1)] =    MQ200_GCCR(MQ200_GC1),
202                     [MQ200_I_GCC(MQ200_GC2)] =    MQ200_GCCR(MQ200_GC2),
203           };
204 
205           for (i = 0; i < sizeof(offsets)/sizeof(*offsets); i++) {
206                     if (offsets[i] == 0)
207 #ifdef MQ200_DEBUG
208                               if (i != MQ200_I_PMC)
209                                         panic("%s(%d): register context %d is empty",
210                                             __FILE__, __LINE__, i);
211 #endif
212                     sc->sc_regctxs[i].offset = offsets[i];
213           }
214 }
215 
216 void
mq200_setup(struct mq200_softc * sc)217 mq200_setup(struct mq200_softc *sc)
218 {
219           const struct mq200_clock_setting *clock;
220           const struct mq200_crt_param *crt;
221 
222           clock = &sc->sc_md->md_clock_settings[sc->sc_flags & MQ200_SC_GC_MASK];
223           crt = sc->sc_crt;
224 
225           /* disable GC1 and GC2        */
226           //mq200_write(sc, MQ200_GCCR(MQ200_GC1), 0);
227           mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)], 0);
228           mq200_write(sc, MQ200_GC1CRTCR, 0);
229           //mq200_write(sc, MQ200_GCCR(MQ200_GC2), 0);
230           mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)], 0);
231 
232           while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS)
233               /* busy wait */;
234 
235           /*
236            * setup around clock
237            */
238           /* setup eatch PLLs */
239           mq200_set_pll(sc, MQ200_CLOCK_PLL1, clock->pll1);
240           mq200_set_pll(sc, MQ200_CLOCK_PLL2, clock->pll2);
241           mq200_set_pll(sc, MQ200_CLOCK_PLL3, clock->pll3);
242           if (sc->sc_flags & MQ200_SC_GC1_ENABLE)
243                     mq200_set_pll(sc, clock->gc[MQ200_GC1], crt->clock);
244 
245           /* setup MEMORY clock */
246           if (clock->mem == MQ200_CLOCK_PLL2)
247                     mq200_on(sc, &sc->sc_regctxs[MQ200_I_MM01],
248                         MQ200_MM01_CLK_PLL2);
249           else
250                     mq200_off(sc, &sc->sc_regctxs[MQ200_I_MM01],
251                         MQ200_MM01_CLK_PLL2);
252           DPRINTF("MEM: PLL%d\n", (clock->mem == MQ200_CLOCK_PLL2)?2:1);
253 
254           /* setup GE clock */
255           mq200_mod(sc, &sc->sc_regctxs[MQ200_I_PMC],
256               MQ200_PMC_GE_CLK_MASK | MQ200_PMC_GE_ENABLE,
257               (clock->ge << MQ200_PMC_GE_CLK_SHIFT) | MQ200_PMC_GE_ENABLE);
258           DPRINTF(" GE: PLL%d\n", clock->ge);
259 
260           /*
261            * setup GC1        (CRT controller)
262            */
263           if (sc->sc_flags & MQ200_SC_GC1_ENABLE) {
264                     /* GC03R  Horizontal Display Control    */
265                     mq200_write(sc, MQ200_GCHDCR(MQ200_GC1),
266                         (((u_int32_t)crt->hdtotal-2)<<MQ200_GC1HDC_TOTAL_SHIFT) |
267                         ((u_int32_t)crt->width << MQ200_GCHDC_END_SHIFT));
268 
269                     /* GC03R  Vertical Display Control      */
270                     mq200_write(sc, MQ200_GCVDCR(MQ200_GC1),
271                         (((u_int32_t)crt->vdtotal-1)<<MQ200_GC1VDC_TOTAL_SHIFT) |
272                         (((u_int32_t)crt->height - 1) << MQ200_GCVDC_END_SHIFT));
273 
274                     /* GC04R  Horizontal Sync Control                 */
275                     mq200_write(sc, MQ200_GCHSCR(MQ200_GC1),
276                         ((u_int32_t)crt->hsstart << MQ200_GCHSC_START_SHIFT) |
277                         ((u_int32_t)crt->hsend << MQ200_GCHSC_END_SHIFT));
278 
279                     /* GC05R  Vertical Sync Control                   */
280                     mq200_write(sc, MQ200_GCVSCR(MQ200_GC1),
281                         ((u_int32_t)crt->vsstart << MQ200_GCVSC_START_SHIFT) |
282                         ((u_int32_t)crt->vsend << MQ200_GCVSC_END_SHIFT));
283 
284                     /* GC00R  GC1 Control                             */
285                     //mq200_write(sc, MQ200_GCCR(MQ200_GC1),
286                     mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC1)],
287                         (MQ200_GCC_ENABLE |
288                               (clock->gc[MQ200_GC1] << MQ200_GCC_RCLK_SHIFT) |
289                               MQ200_GCC_MCLK_FD_1 |
290                               (1 << MQ200_GCC_MCLK_SD_SHIFT)));
291 
292                     /* GC01R  CRT Control                             */
293                     mq200_write(sc, MQ200_GC1CRTCR,
294                         MQ200_GC1CRTC_DACEN | crt->opt);
295 
296                     sc->sc_width[MQ200_GC1] = crt->width;
297                     sc->sc_height[MQ200_GC1] = crt->height;
298 
299                     DPRINTF("GC1: %s\n",
300                         mq200_clknames[clock->gc[MQ200_GC1]]);
301           }
302 
303           while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS)
304               /* busy wait */;
305 
306           /*
307            * setup GC2        (FP controller)
308            */
309           if (sc->sc_flags & MQ200_SC_GC2_ENABLE) {
310                     //mq200_write(sc, MQ200_GCCR(MQ200_GC2),
311                     mq200_write2(sc, &sc->sc_regctxs[MQ200_I_GCC(MQ200_GC2)],
312                         MQ200_GCC_ENABLE |
313                         (clock->gc[MQ200_GC2] << MQ200_GCC_RCLK_SHIFT) |
314                         MQ200_GCC_MCLK_FD_1 | (1 << MQ200_GCC_MCLK_SD_SHIFT));
315                     DPRINTF("GC2: %s\n",
316                         mq200_clknames[clock->gc[MQ200_GC2]]);
317           }
318 
319           while (mq200_read(sc, MQ200_PMCR) & MQ200_PMC_SEQPROGRESS)
320               /* busy wait */;
321 
322           /*
323            * disable unused PLLs
324            */
325           if (clock->pll1 == 0) {
326                     DPRINTF("PLL1 disable\n");
327                     mq200_off(sc, &sc->sc_regctxs[MQ200_I_DCMISC],
328                         MQ200_DCMISC_PLL1_ENABLE);
329           }
330           if (clock->pll2 == 0) {
331                     DPRINTF("PLL2 disable\n");
332                     mq200_off(sc, &sc->sc_regctxs[MQ200_I_PMC],
333                         MQ200_PMC_PLL2_ENABLE);
334           }
335           if (clock->pll3 == 0) {
336                     DPRINTF("PLL3 disable\n");
337                     mq200_off(sc,  &sc->sc_regctxs[MQ200_I_PMC],
338                         MQ200_PMC_PLL3_ENABLE);
339           }
340 }
341 
342 void
mq200_win_enable(struct mq200_softc * sc,int gc,u_int32_t depth,u_int32_t start,int width,int height,int stride)343 mq200_win_enable(struct mq200_softc *sc, int gc,
344     u_int32_t depth, u_int32_t start,
345     int width, int height, int stride)
346 {
347 
348           DPRINTF("enable window on GC%d: %dx%d(%dx%d)\n",
349               gc + 1, width, height,  sc->sc_width[gc], sc->sc_height[gc]);
350 
351           if (sc->sc_width[gc] < width) {
352                     if (mq200_depth_table[depth])
353                               start += (height - sc->sc_height[gc]) *
354                                   mq200_depth_table[depth] / 8;
355                     width = sc->sc_width[gc];
356           }
357 
358           if (sc->sc_height[gc] < height) {
359                     start += (height - sc->sc_height[gc]) * stride;
360                     height = sc->sc_height[gc];
361           }
362 
363           /* GC08R  Window Horizontal Control     */
364           mq200_write(sc, MQ200_GCWHCR(gc),
365               (((u_int32_t)width - 1) << MQ200_GCWHC_WIDTH_SHIFT) |
366               ((sc->sc_width[gc] - width)/2));
367 
368           /* GC09R  Window Vertical Control                 */
369           mq200_write(sc, MQ200_GCWVCR(gc),
370               (((u_int32_t)height - 1) << MQ200_GCWVC_HEIGHT_SHIFT) |
371               ((sc->sc_height[gc] - height)/2));
372 
373           /* GC00R  GC Control          */
374           mq200_mod(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)],
375               (MQ200_GCC_WINEN | MQ200_GCC_DEPTH_MASK),
376               (MQ200_GCC_WINEN | (depth << MQ200_GCC_DEPTH_SHIFT)));
377 }
378 
379 void
mq200_win_disable(struct mq200_softc * sc,int gc)380 mq200_win_disable(struct mq200_softc *sc, int gc)
381 {
382           /* GC00R  GC Control          */
383           mq200_off(sc, &sc->sc_regctxs[MQ200_I_GCC(gc)], MQ200_GCC_WINEN);
384 }
385