xref: /freebsd/sys/dev/fb/s3_pci.c (revision 3642298923e528d795e3a30ec165d2b469e28b40)
1 /*-
2  * Copyright (c) 2000 Alcove - Nicolas Souchu <nsouch@freebsd.org>
3  * All rights reserved.
4  *
5  * Code based on Peter Horton <pdh@colonel-panic.com> patch.
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 /* Enable LFB on S3 cards that has only VESA 1.2 BIOS */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <machine/bus.h>
38 
39 #include <vm/vm.h>
40 #include <vm/vm_extern.h>
41 #include <vm/vm_kern.h>
42 #include <vm/pmap.h>
43 
44 #include <sys/uio.h>
45 #include <sys/module.h>
46 #include <sys/bus.h>
47 #include <sys/rman.h>
48 #include <machine/resource.h>
49 
50 #include <sys/malloc.h>
51 #include <sys/fbio.h>
52 
53 #include <dev/pci/pcireg.h>
54 #include <dev/pci/pcivar.h>
55 
56 #include <machine/md_var.h>
57 #include <machine/vm86.h>
58 #include <machine/pc/bios.h>
59 #include <machine/pc/vesa.h>
60 
61 #include <dev/fb/fbreg.h>
62 #include <dev/fb/vgareg.h>
63 
64 #define S3PCI_DEBUG 1
65 
66 #define PCI_S3_VENDOR_ID	0x5333
67 
68 #define S3_CONFIG_IO		0x3c0	/* VGA standard config io ports */
69 #define S3_CONFIG_IO_SIZE	0x20
70 
71 #define S3_ENHANCED_IO		0x4ae8	/* Extended config register */
72 #define S3_ENHANCED_IO_SIZE	1
73 
74 #define S3_CRTC_ADDR		0x14
75 #define S3_CRTC_VALUE		0x15
76 
77 #define PCI_BASE_MEMORY		0x10
78 
79 #define outb_p(value, offset) bus_space_write_1(sc->st, sc->sh, offset, value)
80 #define inb_p(offset) (bus_space_read_1(sc->st, sc->sh, offset))
81 #define outb_enh(value, offset) bus_space_write_1(sc->enh_st, sc->enh_sh, \
82 								offset, value)
83 #define inb_enh(offset) (bus_space_read_1(sc->enh_st, sc->enh_sh, offset))
84 
85 struct s3pci_softc {
86 	bus_space_tag_t st;
87 	bus_space_handle_t sh;
88 	bus_space_tag_t enh_st;
89 	bus_space_handle_t enh_sh;
90 	struct resource *port_res;
91 	struct resource *enh_res;
92 	struct resource *mem_res;
93 	u_long mem_base;
94 	u_long mem_size;
95 };
96 
97 static int			s3lfb_error(void);
98 static vi_probe_t		s3lfb_probe;
99 static vi_init_t		s3lfb_init;
100 static vi_get_info_t		s3lfb_get_info;
101 static vi_query_mode_t		s3lfb_query_mode;
102 static vi_set_mode_t		s3lfb_set_mode;
103 static vi_save_font_t		s3lfb_save_font;
104 static vi_load_font_t		s3lfb_load_font;
105 static vi_show_font_t		s3lfb_show_font;
106 static vi_save_palette_t	s3lfb_save_palette;
107 static vi_load_palette_t	s3lfb_load_palette;
108 static vi_set_border_t		s3lfb_set_border;
109 static vi_save_state_t		s3lfb_save_state;
110 static vi_load_state_t		s3lfb_load_state;
111 static vi_set_win_org_t		s3lfb_set_origin;
112 static vi_read_hw_cursor_t	s3lfb_read_hw_cursor;
113 static vi_set_hw_cursor_t	s3lfb_set_hw_cursor;
114 static vi_set_hw_cursor_shape_t	s3lfb_set_hw_cursor_shape;
115 static vi_blank_display_t	s3lfb_blank_display;
116 static vi_mmap_t		s3lfb_mmap;
117 static vi_ioctl_t		s3lfb_ioctl;
118 static vi_clear_t		s3lfb_clear;
119 static vi_fill_rect_t		s3lfb_fill_rect;
120 static vi_bitblt_t		s3lfb_bitblt;
121 static vi_diag_t		s3lfb_diag;
122 
123 static video_switch_t s3lfbvidsw = {
124 	s3lfb_probe,
125 	s3lfb_init,
126 	s3lfb_get_info,
127 	s3lfb_query_mode,
128 	s3lfb_set_mode,
129 	s3lfb_save_font,
130 	s3lfb_load_font,
131 	s3lfb_show_font,
132 	s3lfb_save_palette,
133 	s3lfb_load_palette,
134 	s3lfb_set_border,
135 	s3lfb_save_state,
136 	s3lfb_load_state,
137 	s3lfb_set_origin,
138 	s3lfb_read_hw_cursor,
139 	s3lfb_set_hw_cursor,
140 	s3lfb_set_hw_cursor_shape,
141 	s3lfb_blank_display,
142 	s3lfb_mmap,
143 	s3lfb_ioctl,
144 	s3lfb_clear,
145 	s3lfb_fill_rect,
146 	s3lfb_bitblt,
147 	s3lfb_error,
148 	s3lfb_error,
149 	s3lfb_diag,
150 };
151 
152 static video_switch_t *prevvidsw;
153 static device_t s3pci_dev = NULL;
154 
155 static int
156 s3lfb_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
157 {
158 	return (*prevvidsw->probe)(unit, adpp, arg, flags);
159 }
160 
161 static int
162 s3lfb_init(int unit, video_adapter_t *adp, int flags)
163 {
164 	return (*prevvidsw->init)(unit, adp, flags);
165 }
166 
167 static int
168 s3lfb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
169 {
170 #if 0
171 	device_t dev = s3pci_dev;			/* XXX */
172 	struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev);
173 #endif
174 	int error;
175 
176 	if ((error = (*prevvidsw->get_info)(adp, mode, info)))
177 		return error;
178 
179 #if 0
180 	/* Don't use linear addressing with text modes
181 	 */
182 	if ((mode > M_VESA_BASE) &&
183 		(info->vi_flags & V_INFO_GRAPHICS) &&
184 		!(info->vi_flags & V_INFO_LINEAR)) {
185 
186 		info->vi_flags |= V_INFO_LINEAR;
187 		info->vi_buffer = sc->mem_base;
188 
189 	} else {
190 		info->vi_buffer = 0;
191 	}
192 #endif
193 
194 	return 0;
195 }
196 
197 static int
198 s3lfb_query_mode(video_adapter_t *adp, video_info_t *info)
199 {
200 	return (*prevvidsw->query_mode)(adp, info);
201 }
202 
203 static vm_offset_t
204 s3lfb_map_buffer(u_int paddr, size_t size)
205 {
206 	vm_offset_t vaddr;
207 	u_int off;
208 
209 	off = paddr - trunc_page(paddr);
210 	vaddr = (vm_offset_t)pmap_mapdev(paddr - off, size + off);
211 
212 	return (vaddr + off);
213 }
214 
215 static int
216 s3lfb_set_mode(video_adapter_t *adp, int mode)
217 {
218 	device_t dev = s3pci_dev;			/* XXX */
219 	struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev);
220 #if 0
221 	unsigned char tmp;
222 #endif
223 	int error;
224 
225 	/* First, set the mode as if it was a classic VESA card
226 	 */
227 	if ((error = (*prevvidsw->set_mode)(adp, mode)))
228 		return error;
229 
230 	/* If not in a linear mode (according to s3lfb_get_info() called
231 	 * by vesa_set_mode in the (*vidsw[adp->va_index]->get_info)...
232 	 * sequence, return with no error
233 	 */
234 #if 0
235 	if (!(adp->va_info.vi_flags & V_INFO_LINEAR))
236 		return 0;
237 #endif
238 
239 	if ((mode <= M_VESA_BASE) ||
240 		!(adp->va_info.vi_flags & V_INFO_GRAPHICS) ||
241 		(adp->va_info.vi_flags & V_INFO_LINEAR))
242 		return 0;
243 
244 	/* Ok, now apply the configuration to the card */
245 
246 	outb_p(0x38, S3_CRTC_ADDR); outb_p(0x48, S3_CRTC_VALUE);
247 	outb_p(0x39, S3_CRTC_ADDR); outb_p(0xa5, S3_CRTC_VALUE);
248 
249        /* check that CR47 is read/write */
250 
251 #if 0
252 	outb_p(0x47, S3_CRTC_ADDR); outb_p(0xff, S3_CRTC_VALUE);
253 	tmp = inb_p(S3_CRTC_VALUE);
254 	outb_p(0x00, S3_CRTC_VALUE);
255 	if ((tmp != 0xff) || (inb_p(S3_CRTC_VALUE)))
256 	{
257 		/* lock S3 registers */
258 
259 		outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE);
260 		outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE);
261 
262 		return ENXIO;
263 	}
264 #endif
265 
266 	/* enable enhanced register access */
267 
268 	outb_p(0x40, S3_CRTC_ADDR);
269 	outb_p(inb_p(S3_CRTC_VALUE) | 1, S3_CRTC_VALUE);
270 
271 	/* enable enhanced functions */
272 
273 	outb_enh(inb_enh(0) | 1, 0x0);
274 
275 	/* enable enhanced mode memory mapping */
276 
277 	outb_p(0x31, S3_CRTC_ADDR);
278 	outb_p(inb_p(S3_CRTC_VALUE) | 8, S3_CRTC_VALUE);
279 
280 	/* enable linear frame buffer and set address window to max */
281 
282 	outb_p(0x58, S3_CRTC_ADDR);
283 	outb_p(inb_p(S3_CRTC_VALUE) | 0x13, S3_CRTC_VALUE);
284 
285 	/* disabled enhanced register access */
286 
287 	outb_p(0x40, S3_CRTC_ADDR);
288 	outb_p(inb_p(S3_CRTC_VALUE) & ~1, S3_CRTC_VALUE);
289 
290 	/* lock S3 registers */
291 
292 	outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE);
293 	outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE);
294 
295 	adp->va_info.vi_flags |= V_INFO_LINEAR;
296 	adp->va_info.vi_buffer = sc->mem_base;
297 	adp->va_buffer = s3lfb_map_buffer(adp->va_info.vi_buffer,
298 				adp->va_info.vi_buffer_size);
299 	adp->va_buffer_size = adp->va_info.vi_buffer_size;
300 	adp->va_window = adp->va_buffer;
301 	adp->va_window_size = adp->va_info.vi_buffer_size/adp->va_info.vi_planes;
302 	adp->va_window_gran = adp->va_info.vi_buffer_size/adp->va_info.vi_planes;
303 
304 	return 0;
305 }
306 
307 static int
308 s3lfb_save_font(video_adapter_t *adp, int page, int fontsize, u_char *data,
309 	       int ch, int count)
310 {
311 	return (*prevvidsw->save_font)(adp, page, fontsize, data, ch, count);
312 }
313 
314 static int
315 s3lfb_load_font(video_adapter_t *adp, int page, int fontsize, u_char *data,
316 	       int ch, int count)
317 {
318 	return (*prevvidsw->load_font)(adp, page, fontsize, data, ch, count);
319 }
320 
321 static int
322 s3lfb_show_font(video_adapter_t *adp, int page)
323 {
324 	return (*prevvidsw->show_font)(adp, page);
325 }
326 
327 static int
328 s3lfb_save_palette(video_adapter_t *adp, u_char *palette)
329 {
330 	return (*prevvidsw->save_palette)(adp, palette);
331 }
332 
333 static int
334 s3lfb_load_palette(video_adapter_t *adp, u_char *palette)
335 {
336 	return (*prevvidsw->load_palette)(adp, palette);
337 }
338 
339 static int
340 s3lfb_set_border(video_adapter_t *adp, int color)
341 {
342 	return (*prevvidsw->set_border)(adp, color);
343 }
344 
345 static int
346 s3lfb_save_state(video_adapter_t *adp, void *p, size_t size)
347 {
348 	return (*prevvidsw->save_state)(adp, p, size);
349 }
350 
351 static int
352 s3lfb_load_state(video_adapter_t *adp, void *p)
353 {
354 	return (*prevvidsw->load_state)(adp, p);
355 }
356 
357 static int
358 s3lfb_set_origin(video_adapter_t *adp, off_t offset)
359 {
360 	return (*prevvidsw->set_win_org)(adp, offset);
361 }
362 
363 static int
364 s3lfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
365 {
366 	return (*prevvidsw->read_hw_cursor)(adp, col, row);
367 }
368 
369 static int
370 s3lfb_set_hw_cursor(video_adapter_t *adp, int col, int row)
371 {
372 	return (*prevvidsw->set_hw_cursor)(adp, col, row);
373 }
374 
375 static int
376 s3lfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
377 			 int celsize, int blink)
378 {
379 	return (*prevvidsw->set_hw_cursor_shape)(adp, base, height,
380 			celsize, blink);
381 }
382 
383 static int
384 s3lfb_blank_display(video_adapter_t *adp, int mode)
385 {
386 	return (*prevvidsw->blank_display)(adp, mode);
387 }
388 
389 static int
390 s3lfb_mmap(video_adapter_t *adp, vm_offset_t offset, vm_offset_t *paddr,
391 	  int prot)
392 {
393 	return (*prevvidsw->mmap)(adp, offset, paddr, prot);
394 }
395 
396 static int
397 s3lfb_clear(video_adapter_t *adp)
398 {
399 	return (*prevvidsw->clear)(adp);
400 }
401 
402 static int
403 s3lfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
404 {
405 	return (*prevvidsw->fill_rect)(adp, val, x, y, cx, cy);
406 }
407 
408 static int
409 s3lfb_bitblt(video_adapter_t *adp,...)
410 {
411 	return (*prevvidsw->bitblt)(adp);		/* XXX */
412 }
413 
414 static int
415 s3lfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
416 {
417 	return (*prevvidsw->ioctl)(adp, cmd, arg);
418 }
419 
420 static int
421 s3lfb_diag(video_adapter_t *adp, int level)
422 {
423 	return (*prevvidsw->diag)(adp, level);
424 }
425 
426 static int
427 s3lfb_error(void)
428 {
429 	return 1;
430 }
431 
432 /***********************************/
433 /* PCI detection/attachement stuff */
434 /***********************************/
435 
436 static int
437 s3pci_probe(device_t dev)
438 {
439 	u_int32_t vendor, class, subclass, device_id;
440 
441 	device_id = pci_get_devid(dev);
442 	vendor = device_id & 0xffff;
443 	class = pci_get_class(dev);
444 	subclass = pci_get_subclass(dev);
445 
446 	if ((class != PCIC_DISPLAY) || (subclass != PCIS_DISPLAY_VGA) ||
447 		(vendor != PCI_S3_VENDOR_ID))
448 		return ENXIO;
449 
450 	device_set_desc(dev, "S3 graphic card");
451 
452 	bus_set_resource(dev, SYS_RES_IOPORT, 0,
453 				S3_CONFIG_IO, S3_CONFIG_IO_SIZE);
454 	bus_set_resource(dev, SYS_RES_IOPORT, 1,
455 				S3_ENHANCED_IO, S3_ENHANCED_IO_SIZE);
456 
457 	return BUS_PROBE_DEFAULT;
458 
459 };
460 
461 static int
462 s3pci_attach(device_t dev)
463 {
464 	struct s3pci_softc* sc = (struct s3pci_softc*)device_get_softc(dev);
465 	video_adapter_t *adp;
466 
467 #if 0
468 	unsigned char tmp;
469 #endif
470 	int rid, i;
471 
472 	if (s3pci_dev) {
473 		printf("%s: driver already attached!\n", __func__);
474 		goto error;
475 	}
476 
477 	/* Allocate resources
478 	 */
479 	rid = 0;
480 	if (!(sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
481 				0ul, ~0ul, 0, RF_ACTIVE | RF_SHAREABLE))) {
482 		printf("%s: port resource allocation failed!\n", __func__);
483 		goto error;
484 	}
485 	sc->st = rman_get_bustag(sc->port_res);
486 	sc->sh = rman_get_bushandle(sc->port_res);
487 
488 	rid = 1;
489 	if (!(sc->enh_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
490 				0ul, ~0ul, 0, RF_ACTIVE | RF_SHAREABLE))) {
491 		printf("%s: enhanced port resource allocation failed!\n",
492 			__func__);
493 		goto error;
494 	}
495 	sc->enh_st = rman_get_bustag(sc->enh_res);
496 	sc->enh_sh = rman_get_bushandle(sc->enh_res);
497 
498 	rid = PCI_BASE_MEMORY;
499 	if (!(sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
500 				 RF_ACTIVE))) {
501 
502 		printf("%s: mem resource allocation failed!\n", __func__);
503 		goto error;
504 	}
505 
506 	/* The memory base address will be our LFB base address
507 	 */
508 	/* sc->mem_base = (u_long)rman_get_virtual(sc->mem_res); */
509 	sc->mem_base = bus_get_resource_start(dev, SYS_RES_MEMORY, rid);
510 	sc->mem_size = bus_get_resource_count(dev, SYS_RES_MEMORY, rid);
511 
512 	/* Attach the driver to the VGA/VESA framework
513 	 */
514 	for (i = 0; (adp = vid_get_adapter(i)) != NULL; ++i) {
515 		if ((adp->va_type == KD_VGA))
516 			break;
517 	}
518 
519 	/* If the VESA module hasn't been loaded, or VGA doesn't
520 	 * exist, abort
521 	 */
522 	if ((adp == NULL) || !(adp->va_flags & V_ADP_VESA)) {
523 		printf("%s: VGA adapter not found or VESA module not loaded!\n",
524 			__func__);
525 		goto error;
526 	}
527 
528 	/* Replace the VESA video switch by owers
529 	 */
530 	prevvidsw = vidsw[adp->va_index];
531 	vidsw[adp->va_index] = &s3lfbvidsw;
532 
533 	/* Remember who we are on the bus */
534 	s3pci_dev = (void *)dev;			/* XXX */
535 
536 	return 0;
537 
538 error:
539 	if (sc->mem_res)
540 		bus_release_resource(dev, SYS_RES_MEMORY, PCI_BASE_MEMORY, sc->mem_res);
541 
542 	if (sc->enh_res)
543 		bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->enh_res);
544 
545 	if (sc->port_res)
546 		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res);
547 
548 	return ENXIO;
549 };
550 
551 static device_method_t s3pci_methods[] = {
552 
553         DEVMETHOD(device_probe, s3pci_probe),
554         DEVMETHOD(device_attach, s3pci_attach),
555         {0,0}
556 };
557 
558 static driver_t s3pci_driver = {
559 	"s3pci",
560 	s3pci_methods,
561 	sizeof(struct s3pci_softc),
562 };
563 
564 static devclass_t s3pci_devclass;
565 
566 DRIVER_MODULE(s3pci, pci, s3pci_driver, s3pci_devclass, 0, 0);
567