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