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