xref: /netbsd-src/sys/arch/hpcmips/dev/mq200.c (revision 5aefcfdc06931dd97e76246d2fe0302f7b3fe094)
1 /*	$NetBSD: mq200.c,v 1.5 2000/12/21 03:27:15 sato Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000 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 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/device.h>
35 #include <sys/systm.h>
36 #include <sys/reboot.h>
37 
38 #include <uvm/uvm_extern.h>
39 
40 #include <dev/wscons/wsconsio.h>
41 
42 #include <machine/bootinfo.h>
43 #include <machine/bus.h>
44 #include <machine/autoconf.h>
45 #include <machine/config_hook.h>
46 #include <machine/platid.h>
47 #include <machine/platid_mask.h>
48 
49 #include <hpcmips/dev/mq200reg.h>
50 #include <hpcmips/dev/mq200var.h>
51 #include "bivideo.h"
52 #if NBIVIDEO > 0
53 #include <hpcmips/dev/bivideovar.h>
54 #endif
55 
56 #define MQ200DEBUG
57 #ifdef MQ200DEBUG
58 #ifndef MQ200DEBUG_CONF
59 #define MQ200DEBUG_CONF 0
60 #endif
61 int	mq200_debug = MQ200DEBUG_CONF;
62 #define	DPRINTF(arg)     do { if (mq200_debug) printf arg; } while(0);
63 #define	DPRINTFN(n, arg) do { if (mq200_debug > (n)) printf arg; } while (0);
64 #define	VPRINTF(arg)     do { if (bootverbose || mq200_debug) printf arg; } while(0);
65 #define	VPRINTFN(n, arg) do { if (bootverbose || mq200_debug > (n)) printf arg; } while (0);
66 #else
67 #define	DPRINTF(arg)     do { } while (0);
68 #define DPRINTFN(n, arg) do { } while (0);
69 #define	VPRINTF(arg)     do { if (bootverbose) printf arg; } while(0);
70 #define	VPRINTFN(n, arg) do { if (bootverbose) printf arg; } while (0);
71 #endif
72 
73 /*
74  * function prototypes
75  */
76 static void	mq200_power __P((int, void *));
77 static int	mq200_hardpower __P((void *, int, long, void *));
78 static int	mq200_fbinit __P((struct hpcfb_fbconf *));
79 static int	mq200_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
80 static paddr_t	mq200_mmap __P((void *, off_t offset, int));
81 
82 /*
83  * static variables
84  */
85 struct hpcfb_accessops mq200_ha = {
86 	mq200_ioctl, mq200_mmap
87 };
88 
89 int
90 mq200_probe(iot, ioh)
91 	bus_space_tag_t iot;
92 	bus_space_handle_t ioh;
93 {
94 	unsigned long regval;
95 
96 #if NBIVIDEO > 0
97 	if (bivideo_dont_attach) /* some video driver already attached */
98 		return (0);
99 #endif /* NBIVIDEO > 0 */
100 
101 	regval = bus_space_read_4(iot, ioh, MQ200_PC00R);
102 	VPRINTF(("mq200 probe: vendor id=%04lx product id=%04lx\n",
103 		 regval & 0xffff, (regval >> 16) & 0xffff));
104 	if (regval != ((MQ200_PRODUCT_ID << 16) | MQ200_VENDOR_ID))
105 		return (0);
106 
107 	return (1);
108 }
109 
110 void
111 mq200_attach(sc)
112 	struct mq200_softc *sc;
113 {
114 	unsigned long regval;
115 	struct hpcfb_attach_args ha;
116 	int console = (bootinfo->bi_cnuse & BI_CNUSE_SERIAL) ? 0 : 1;
117 
118 	regval = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MQ200_PC08R);
119 	printf(": MQ200 Rev.%02lx video controller\n", regval & 0xff);
120 
121 	/* Add a power hook to power saving */
122 	sc->sc_powerstate = MQ200_POWERSTATE_D0;
123 	sc->sc_powerhook = powerhook_establish(mq200_power, sc);
124 	if (sc->sc_powerhook == NULL)
125 		printf("%s: WARNING: unable to establish power hook\n",
126 			sc->sc_dev.dv_xname);
127 
128 	/* Add a hard power hook to power saving */
129 	sc->sc_hardpowerhook = config_hook(CONFIG_HOOK_PMEVENT,
130 					   CONFIG_HOOK_PMEVENT_HARDPOWER,
131 					   CONFIG_HOOK_SHARE,
132 					   mq200_hardpower, sc);
133 	if (sc->sc_hardpowerhook == NULL)
134 		printf("%s: WARNING: unable to establish hard power hook\n",
135 			sc->sc_dev.dv_xname);
136 
137 	mq200_fbinit(&sc->sc_fbconf);
138 	sc->sc_fbconf.hf_baseaddr = (u_long)bootinfo->fb_addr;
139 	sc->sc_fbconf.hf_offset	= (u_long)sc->sc_fbconf.hf_baseaddr -
140 	    MIPS_PHYS_TO_KSEG1(mips_ptob(mips_btop(sc->sc_baseaddr)));
141 	DPRINTF(("hf_baseaddr=%lx\n", sc->sc_fbconf.hf_baseaddr));
142 	DPRINTF(("hf_offset=%lx\n", sc->sc_fbconf.hf_offset));
143 
144 	if (console && hpcfb_cnattach(&sc->sc_fbconf) != 0) {
145 		panic("mq200_attach: can't init fb console");
146 	}
147 
148 	ha.ha_console = console;
149 	ha.ha_accessops = &mq200_ha;
150 	ha.ha_accessctx = sc;
151 	ha.ha_curfbconf = 0;
152 	ha.ha_nfbconf = 1;
153 	ha.ha_fbconflist = &sc->sc_fbconf;
154 	ha.ha_curdspconf = 0;
155 	ha.ha_ndspconf = 1;
156 	ha.ha_dspconflist = &sc->sc_dspconf;
157 
158 	config_found(&sc->sc_dev, &ha, hpcfbprint);
159 
160 #if NBIVIDEO > 0
161 	/*
162 	 * bivideo is no longer need
163 	 */
164 	bivideo_dont_attach = 1;
165 #endif /* NBIVIDEO > 0 */
166 }
167 
168 static void
169 mq200_power(why, arg)
170 	int why;
171 	void *arg;
172 {
173 #if 0
174 	struct mq200_softc *sc = arg;
175 
176 	switch (why) {
177 	case PWR_SUSPEND:
178 		sc->sc_powerstate = MQ200_POWERSTATE_D2;
179 		break;
180 	case PWR_STANDBY:
181 		sc->sc_powerstate = MQ200_POWERSTATE_D3;
182 		break;
183 	case PWR_RESUME:
184 		sc->sc_powerstate = MQ200_POWERSTATE_D0;
185 		break;
186 	}
187 
188 	printf("MQ200_PMCSR=%08x\n", sc->sc_powerstate);
189 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
190 			  MQ200_PMCSR, sc->sc_powerstate);
191 #endif
192 }
193 
194 static int
195 mq200_hardpower(ctx, type, id, msg)
196 	void *ctx;
197 	int type;
198 	long id;
199 	void *msg;
200 {
201 	struct mq200_softc *sc = ctx;
202 	int why = (int)msg;
203 
204 	switch (why) {
205 	case PWR_SUSPEND:
206 		sc->sc_powerstate = MQ200_POWERSTATE_D2;
207 		break;
208 	case PWR_STANDBY:
209 		sc->sc_powerstate = MQ200_POWERSTATE_D3;
210 		break;
211 	case PWR_RESUME:
212 		sc->sc_powerstate = MQ200_POWERSTATE_D0;
213 		break;
214 	}
215 
216 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
217 			  MQ200_PMCSR, sc->sc_powerstate);
218 
219 	/*
220 	 * you should wait until the
221 	 * power state transit sequence will end.
222 	 */
223 	{
224 		unsigned long tmp;
225 		do {
226 			tmp = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
227 					       MQ200_PMCSR);
228 		} while ((tmp & 0x3) != (sc->sc_powerstate & 0x3));
229 		delay(100000); /* XXX */
230 	}
231 
232 	return (0);
233 }
234 
235 
236 static int
237 mq200_fbinit(fb)
238 	struct hpcfb_fbconf *fb;
239 {
240 
241 	/*
242 	 * get fb settings from bootinfo
243 	 */
244 	if (bootinfo == NULL ||
245 	    bootinfo->fb_addr == 0 ||
246 	    bootinfo->fb_line_bytes == 0 ||
247 	    bootinfo->fb_width == 0 ||
248 	    bootinfo->fb_height == 0) {
249 		printf("no frame buffer infomation.\n");
250 		return (-1);
251 	}
252 
253 	/* zero fill */
254 	bzero(fb, sizeof(*fb));
255 
256 	fb->hf_conf_index	= 0;	/* configuration index		*/
257 	fb->hf_nconfs		= 1;   	/* how many configurations	*/
258 	strcpy(fb->hf_name, "built-in video");
259 					/* frame buffer name		*/
260 	strcpy(fb->hf_conf_name, "default");
261 					/* configuration name		*/
262 	fb->hf_height		= bootinfo->fb_height;
263 	fb->hf_width		= bootinfo->fb_width;
264 	fb->hf_baseaddr		= mips_ptob(mips_btop(bootinfo->fb_addr));
265 	fb->hf_offset		= (u_long)bootinfo->fb_addr - fb->hf_baseaddr;
266 					/* frame buffer start offset   	*/
267 	fb->hf_bytes_per_line	= bootinfo->fb_line_bytes;
268 	fb->hf_nplanes		= 1;
269 	fb->hf_bytes_per_plane	= bootinfo->fb_height *
270 					bootinfo->fb_line_bytes;
271 
272 	fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
273 	fb->hf_access_flags |= HPCFB_ACCESS_WORD;
274 	fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
275 
276 	switch (bootinfo->fb_type) {
277 		/*
278 		 * gray scale
279 		 */
280 	case BIFB_D2_M2L_3:
281 	case BIFB_D2_M2L_3x2:
282 		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
283 		/* fall through */
284 	case BIFB_D2_M2L_0:
285 	case BIFB_D2_M2L_0x2:
286 		fb->hf_class = HPCFB_CLASS_GRAYSCALE;
287 		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
288 		fb->hf_pack_width = 8;
289 		fb->hf_pixels_per_pack = 4;
290 		fb->hf_pixel_width = 2;
291 		fb->hf_class_data_length = sizeof(struct hf_gray_tag);
292 		fb->hf_u.hf_gray.hf_flags = 0;	/* reserved for future use */
293 		break;
294 
295 		/*
296 		 * indexed color
297 		 */
298 	case BIFB_D8_FF:
299 		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
300 		/* fall through */
301 	case BIFB_D8_00:
302 		fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
303 		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
304 		fb->hf_pack_width = 8;
305 		fb->hf_pixels_per_pack = 1;
306 		fb->hf_pixel_width = 8;
307 		fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
308 		fb->hf_u.hf_indexed.hf_flags = 0; /* reserved for future use */
309 		break;
310 
311 		/*
312 		 * RGB color
313 		 */
314 	case BIFB_D16_FFFF:
315 		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
316 		/* fall through */
317 	case BIFB_D16_0000:
318 		fb->hf_class = HPCFB_CLASS_RGBCOLOR;
319 		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
320 #if BYTE_ORDER == LITTLE_ENDIAN
321 		fb->hf_swap_flags = HPCFB_SWAP_BYTE;
322 #endif
323 		fb->hf_pack_width = 16;
324 		fb->hf_pixels_per_pack = 1;
325 		fb->hf_pixel_width = 16;
326 
327 		fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
328 		fb->hf_u.hf_rgb.hf_flags = 0;	/* reserved for future use */
329 
330 		fb->hf_u.hf_rgb.hf_red_width = 5;
331 		fb->hf_u.hf_rgb.hf_red_shift = 11;
332 		fb->hf_u.hf_rgb.hf_green_width = 6;
333 		fb->hf_u.hf_rgb.hf_green_shift = 5;
334 		fb->hf_u.hf_rgb.hf_blue_width = 5;
335 		fb->hf_u.hf_rgb.hf_blue_shift = 0;
336 		fb->hf_u.hf_rgb.hf_alpha_width = 0;
337 		fb->hf_u.hf_rgb.hf_alpha_shift = 0;
338 		break;
339 
340 	default:
341 		printf("unknown type (=%d).\n", bootinfo->fb_type);
342 		return (-1);
343 		break;
344 	}
345 
346 	return (0); /* no error */
347 }
348 
349 int
350 mq200_ioctl(v, cmd, data, flag, p)
351 	void *v;
352 	u_long cmd;
353 	caddr_t data;
354 	int flag;
355 	struct proc *p;
356 {
357 	struct mq200_softc *sc = (struct mq200_softc *)v;
358 	struct hpcfb_fbconf *fbconf;
359 	struct hpcfb_dspconf *dspconf;
360 	struct wsdisplay_cmap *cmap;
361 
362 	switch (cmd) {
363 	case WSDISPLAYIO_GETCMAP:
364 		cmap = (struct wsdisplay_cmap*)data;
365 
366 		if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR ||
367 		    sc->sc_fbconf.hf_pack_width != 8 ||
368 		    256 <= cmap->index ||
369 		    256 < (cmap->index + cmap->count))
370 			return (EINVAL);
371 
372 #if 0
373 		if (!uvm_useracc(cmap->red, cmap->count, B_WRITE) ||
374 		    !uvm_useracc(cmap->green, cmap->count, B_WRITE) ||
375 		    !uvm_useracc(cmap->blue, cmap->count, B_WRITE))
376 			return (EFAULT);
377 
378 		copyout(&bivideo_cmap_r[cmap->index], cmap->red, cmap->count);
379 		copyout(&bivideo_cmap_g[cmap->index], cmap->green,cmap->count);
380 		copyout(&bivideo_cmap_b[cmap->index], cmap->blue, cmap->count);
381 #endif
382 
383 		return (0);
384 
385 	case WSDISPLAYIO_PUTCMAP:
386 		/*
387 		 * This driver can't set color map.
388 		 */
389 		return (EINVAL);
390 
391 	case HPCFBIO_GCONF:
392 		fbconf = (struct hpcfb_fbconf *)data;
393 		if (fbconf->hf_conf_index != 0 &&
394 		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
395 			return (EINVAL);
396 		}
397 		*fbconf = sc->sc_fbconf;	/* structure assignment */
398 		return (0);
399 	case HPCFBIO_SCONF:
400 		fbconf = (struct hpcfb_fbconf *)data;
401 		if (fbconf->hf_conf_index != 0 &&
402 		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
403 			return (EINVAL);
404 		}
405 		/*
406 		 * nothing to do because we have only one configration
407 		 */
408 		return (0);
409 	case HPCFBIO_GDSPCONF:
410 		dspconf = (struct hpcfb_dspconf *)data;
411 		if ((dspconf->hd_unit_index != 0 &&
412 		     dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
413 		    (dspconf->hd_conf_index != 0 &&
414 		     dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
415 			return (EINVAL);
416 		}
417 		*dspconf = sc->sc_dspconf;	/* structure assignment */
418 		return (0);
419 	case HPCFBIO_SDSPCONF:
420 		dspconf = (struct hpcfb_dspconf *)data;
421 		if ((dspconf->hd_unit_index != 0 &&
422 		     dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
423 		    (dspconf->hd_conf_index != 0 &&
424 		     dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
425 			return (EINVAL);
426 		}
427 		/*
428 		 * nothing to do
429 		 * because we have only one unit and one configration
430 		 */
431 		return (0);
432 	case HPCFBIO_GOP:
433 	case HPCFBIO_SOP:
434 		/*
435 		 * curently not implemented...
436 		 */
437 		return (EINVAL);
438 	}
439 
440 	return (ENOTTY);
441 }
442 
443 paddr_t
444 mq200_mmap(ctx, offset, prot)
445 	void *ctx;
446 	off_t offset;
447 	int prot;
448 {
449 	struct mq200_softc *sc = (struct mq200_softc *)ctx;
450 
451 	if (offset < 0 || MQ200_MAPSIZE <= offset)
452 		return -1;
453 
454 	return mips_btop(sc->sc_baseaddr + offset);
455 }
456