1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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
86struct 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
98static int			s3lfb_error(void);
99static vi_probe_t		s3lfb_probe;
100static vi_init_t		s3lfb_init;
101static vi_get_info_t		s3lfb_get_info;
102static vi_query_mode_t		s3lfb_query_mode;
103static vi_set_mode_t		s3lfb_set_mode;
104static vi_save_font_t		s3lfb_save_font;
105static vi_load_font_t		s3lfb_load_font;
106static vi_show_font_t		s3lfb_show_font;
107static vi_save_palette_t	s3lfb_save_palette;
108static vi_load_palette_t	s3lfb_load_palette;
109static vi_set_border_t		s3lfb_set_border;
110static vi_save_state_t		s3lfb_save_state;
111static vi_load_state_t		s3lfb_load_state;
112static vi_set_win_org_t		s3lfb_set_origin;
113static vi_read_hw_cursor_t	s3lfb_read_hw_cursor;
114static vi_set_hw_cursor_t	s3lfb_set_hw_cursor;
115static vi_set_hw_cursor_shape_t	s3lfb_set_hw_cursor_shape;
116static vi_blank_display_t	s3lfb_blank_display;
117static vi_mmap_t		s3lfb_mmap;
118static vi_ioctl_t		s3lfb_ioctl;
119static vi_clear_t		s3lfb_clear;
120static vi_fill_rect_t		s3lfb_fill_rect;
121static vi_bitblt_t		s3lfb_bitblt;
122static vi_diag_t		s3lfb_diag;
123
124static 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
153static video_switch_t *prevvidsw;
154static device_t s3pci_dev = NULL;
155
156static int
157s3lfb_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
158{
159	return (*prevvidsw->probe)(unit, adpp, arg, flags);
160}
161
162static int
163s3lfb_init(int unit, video_adapter_t *adp, int flags)
164{
165	return (*prevvidsw->init)(unit, adp, flags);
166}
167
168static int
169s3lfb_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
198static int
199s3lfb_query_mode(video_adapter_t *adp, video_info_t *info)
200{
201	return (*prevvidsw->query_mode)(adp, info);
202}
203
204static vm_offset_t
205s3lfb_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
216static int
217s3lfb_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
308static int
309s3lfb_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
316static int
317s3lfb_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
324static int
325s3lfb_show_font(video_adapter_t *adp, int page)
326{
327	return (*prevvidsw->show_font)(adp, page);
328}
329
330static int
331s3lfb_save_palette(video_adapter_t *adp, u_char *palette)
332{
333	return (*prevvidsw->save_palette)(adp, palette);
334}
335
336static int
337s3lfb_load_palette(video_adapter_t *adp, u_char *palette)
338{
339	return (*prevvidsw->load_palette)(adp, palette);
340}
341
342static int
343s3lfb_set_border(video_adapter_t *adp, int color)
344{
345	return (*prevvidsw->set_border)(adp, color);
346}
347
348static int
349s3lfb_save_state(video_adapter_t *adp, void *p, size_t size)
350{
351	return (*prevvidsw->save_state)(adp, p, size);
352}
353
354static int
355s3lfb_load_state(video_adapter_t *adp, void *p)
356{
357	return (*prevvidsw->load_state)(adp, p);
358}
359
360static int
361s3lfb_set_origin(video_adapter_t *adp, off_t offset)
362{
363	return (*prevvidsw->set_win_org)(adp, offset);
364}
365
366static int
367s3lfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
368{
369	return (*prevvidsw->read_hw_cursor)(adp, col, row);
370}
371
372static int
373s3lfb_set_hw_cursor(video_adapter_t *adp, int col, int row)
374{
375	return (*prevvidsw->set_hw_cursor)(adp, col, row);
376}
377
378static int
379s3lfb_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
386static int
387s3lfb_blank_display(video_adapter_t *adp, int mode)
388{
389	return (*prevvidsw->blank_display)(adp, mode);
390}
391
392static int
393s3lfb_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
399static int
400s3lfb_clear(video_adapter_t *adp)
401{
402	return (*prevvidsw->clear)(adp);
403}
404
405static int
406s3lfb_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
411static int
412s3lfb_bitblt(video_adapter_t *adp,...)
413{
414	return (*prevvidsw->bitblt)(adp);		/* XXX */
415}
416
417static int
418s3lfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
419{
420	return (*prevvidsw->ioctl)(adp, cmd, arg);
421}
422
423static int
424s3lfb_diag(video_adapter_t *adp, int level)
425{
426	return (*prevvidsw->diag)(adp, level);
427}
428
429static int
430s3lfb_error(void)
431{
432	return 1;
433}
434
435/***********************************/
436/* PCI detection/attachement stuff */
437/***********************************/
438
439static int
440s3pci_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
464static int
465s3pci_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
541error:
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
554static device_method_t s3pci_methods[] = {
555
556        DEVMETHOD(device_probe, s3pci_probe),
557        DEVMETHOD(device_attach, s3pci_attach),
558        {0,0}
559};
560
561static driver_t s3pci_driver = {
562	"s3pci",
563	s3pci_methods,
564	sizeof(struct s3pci_softc),
565};
566
567static devclass_t s3pci_devclass;
568
569DRIVER_MODULE(s3pci, pci, s3pci_driver, s3pci_devclass, 0, 0);
570