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