blob: 19aedbc85ea4735200ace707efa9deab27d0383b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
3 *
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02004 * Main part
5 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
7 *
8 * If distributed as part of the Linux kernel, this code is licensed under the
9 * terms of the GPL v2.
10 *
11 * Otherwise, the following license terms apply:
12 *
13 * * Redistribution and use in source and binary forms, with or without
14 * * modification, are permitted provided that the following conditions
15 * * are met:
16 * * 1) Redistributions of source code must retain the above copyright
17 * * notice, this list of conditions and the following disclaimer.
18 * * 2) Redistributions in binary form must reproduce the above copyright
19 * * notice, this list of conditions and the following disclaimer in the
20 * * documentation and/or other materials provided with the distribution.
21 * * 3) The name of the author may not be used to endorse or promote products
22 * * derived from this software without specific psisusbr written permission.
23 * *
24 * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
25 * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 * Author: Thomas Winischhofer <thomas@winischhofer.net>
36 *
37 */
38
39#include <linux/config.h>
Arjan van de Ven2682d272006-03-28 01:00:21 -080040#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/module.h>
42#include <linux/kernel.h>
43#include <linux/signal.h>
44#include <linux/sched.h>
45#include <linux/errno.h>
46#include <linux/poll.h>
47#include <linux/init.h>
48#include <linux/slab.h>
49#include <linux/spinlock.h>
50#include <linux/kref.h>
51#include <linux/usb.h>
52#include <linux/smp_lock.h>
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +020053#include <linux/vmalloc.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
55#include "sisusb.h"
56
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +020057#ifdef INCL_SISUSB_CON
58#include <linux/font.h>
59#endif
60
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#define SISUSB_DONTSYNC
62
63/* Forward declarations / clean-up routines */
64
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +020065#ifdef INCL_SISUSB_CON
66int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
67int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data);
68int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data);
69int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data);
70int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand, u8 myor);
71int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor);
72int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand);
73
74int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
75int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);
76int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);
77int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data);
78int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
79 u32 dest, int length, size_t *bytes_written);
80
81int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
82
83extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
84extern int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
85
86extern void sisusb_init_concode(void);
87extern int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);
88extern void sisusb_console_exit(struct sisusb_usb_data *sisusb);
89
90extern void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);
91
92extern int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
93 u8 *arg, int cmapsz, int ch512, int dorecalc,
94 struct vc_data *c, int fh, int uplock);
95
96static int sisusb_first_vc = 0;
97static int sisusb_last_vc = 0;
98module_param_named(first, sisusb_first_vc, int, 0);
99module_param_named(last, sisusb_last_vc, int, 0);
100MODULE_PARM_DESC(first, "Number of first console to take over (1 - MAX_NR_CONSOLES)");
101MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES)");
102#endif
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104static struct usb_driver sisusb_driver;
105
Arjan van de Ven2682d272006-03-28 01:00:21 -0800106DEFINE_MUTEX(disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
108static void
109sisusb_free_buffers(struct sisusb_usb_data *sisusb)
110{
111 int i;
112
113 for (i = 0; i < NUMOBUFS; i++) {
114 if (sisusb->obuf[i]) {
115 usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize,
116 sisusb->obuf[i], sisusb->transfer_dma_out[i]);
117 sisusb->obuf[i] = NULL;
118 }
119 }
120 if (sisusb->ibuf) {
121 usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize,
122 sisusb->ibuf, sisusb->transfer_dma_in);
123 sisusb->ibuf = NULL;
124 }
125}
126
127static void
128sisusb_free_urbs(struct sisusb_usb_data *sisusb)
129{
130 int i;
131
132 for (i = 0; i < NUMOBUFS; i++) {
133 usb_free_urb(sisusb->sisurbout[i]);
134 sisusb->sisurbout[i] = NULL;
135 }
136 usb_free_urb(sisusb->sisurbin);
137 sisusb->sisurbin = NULL;
138}
139
140/* Level 0: USB transport layer */
141
142/* 1. out-bulks */
143
144/* out-urb management */
145
146/* Return 1 if all free, 0 otherwise */
147static int
148sisusb_all_free(struct sisusb_usb_data *sisusb)
149{
150 int i;
151
152 for (i = 0; i < sisusb->numobufs; i++) {
153
154 if (sisusb->urbstatus[i] & SU_URB_BUSY)
155 return 0;
156
157 }
158
159 return 1;
160}
161
162/* Kill all busy URBs */
163static void
164sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
165{
166 int i;
167
168 if (sisusb_all_free(sisusb))
169 return;
170
171 for (i = 0; i < sisusb->numobufs; i++) {
172
173 if (sisusb->urbstatus[i] & SU_URB_BUSY)
174 usb_kill_urb(sisusb->sisurbout[i]);
175
176 }
177}
178
179/* Return 1 if ok, 0 if error (not all complete within timeout) */
180static int
181sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
182{
183 int timeout = 5 * HZ, i = 1;
184
185 wait_event_timeout(sisusb->wait_q,
186 (i = sisusb_all_free(sisusb)),
187 timeout);
188
189 return i;
190}
191
192static int
193sisusb_outurb_available(struct sisusb_usb_data *sisusb)
194{
195 int i;
196
197 for (i = 0; i < sisusb->numobufs; i++) {
198
199 if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
200 return i;
201
202 }
203
204 return -1;
205}
206
207static int
208sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
209{
210 int i, timeout = 5 * HZ;
211
212 wait_event_timeout(sisusb->wait_q,
213 ((i = sisusb_outurb_available(sisusb)) >= 0),
214 timeout);
215
216 return i;
217}
218
219static int
220sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
221{
222 int i;
223
224 i = sisusb_outurb_available(sisusb);
225
226 if (i >= 0)
227 sisusb->urbstatus[i] |= SU_URB_ALLOC;
228
229 return i;
230}
231
232static void
233sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
234{
235 if ((index >= 0) && (index < sisusb->numobufs))
236 sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
237}
238
239/* completion callback */
240
241static void
242sisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs)
243{
244 struct sisusb_urb_context *context = urb->context;
245 struct sisusb_usb_data *sisusb;
246
247 if (!context)
248 return;
249
250 sisusb = context->sisusb;
251
252 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
253 return;
254
255#ifndef SISUSB_DONTSYNC
256 if (context->actual_length)
257 *(context->actual_length) += urb->actual_length;
258#endif
259
260 sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
261 wake_up(&sisusb->wait_q);
262}
263
264static int
265sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
266 int len, int *actual_length, int timeout, unsigned int tflags,
267 dma_addr_t transfer_dma)
268{
269 struct urb *urb = sisusb->sisurbout[index];
270 int retval, byteswritten = 0;
271
272 /* Set up URB */
273 urb->transfer_flags = 0;
274
275 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
276 sisusb_bulk_completeout, &sisusb->urbout_context[index]);
277
Alan Sternb375a042005-07-29 16:11:07 -0400278 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 urb->actual_length = 0;
280
281 if ((urb->transfer_dma = transfer_dma))
282 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
283
284 /* Set up context */
285 sisusb->urbout_context[index].actual_length = (timeout) ?
286 NULL : actual_length;
287
288 /* Declare this urb/buffer in use */
289 sisusb->urbstatus[index] |= SU_URB_BUSY;
290
291 /* Submit URB */
292 retval = usb_submit_urb(urb, GFP_ATOMIC);
293
294 /* If OK, and if timeout > 0, wait for completion */
295 if ((retval == 0) && timeout) {
296 wait_event_timeout(sisusb->wait_q,
297 (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
298 timeout);
299 if (sisusb->urbstatus[index] & SU_URB_BUSY) {
300 /* URB timed out... kill it and report error */
301 usb_kill_urb(urb);
302 retval = -ETIMEDOUT;
303 } else {
304 /* Otherwise, report urb status */
305 retval = urb->status;
306 byteswritten = urb->actual_length;
307 }
308 }
309
310 if (actual_length)
311 *actual_length = byteswritten;
312
313 return retval;
314}
315
316/* 2. in-bulks */
317
318/* completion callback */
319
320static void
321sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs)
322{
323 struct sisusb_usb_data *sisusb = urb->context;
324
325 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
326 return;
327
328 sisusb->completein = 1;
329 wake_up(&sisusb->wait_q);
330}
331
332static int
333sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len,
334 int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma)
335{
336 struct urb *urb = sisusb->sisurbin;
337 int retval, readbytes = 0;
338
339 urb->transfer_flags = 0;
340
341 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
342 sisusb_bulk_completein, sisusb);
343
Alan Sternb375a042005-07-29 16:11:07 -0400344 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 urb->actual_length = 0;
346
347 if ((urb->transfer_dma = transfer_dma))
348 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
349
350 sisusb->completein = 0;
351 retval = usb_submit_urb(urb, GFP_ATOMIC);
352 if (retval == 0) {
353 wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
354 if (!sisusb->completein) {
355 /* URB timed out... kill it and report error */
356 usb_kill_urb(urb);
357 retval = -ETIMEDOUT;
358 } else {
359 /* URB completed within timout */
360 retval = urb->status;
361 readbytes = urb->actual_length;
362 }
363 }
364
365 if (actual_length)
366 *actual_length = readbytes;
367
368 return retval;
369}
370
371
372/* Level 1: */
373
374/* Send a bulk message of variable size
375 *
376 * To copy the data from userspace, give pointer to "userbuffer",
377 * to copy from (non-DMA) kernel memory, give "kernbuffer". If
378 * both of these are NULL, it is assumed, that the transfer
379 * buffer "sisusb->obuf[index]" is set up with the data to send.
380 * Index is ignored if either kernbuffer or userbuffer is set.
381 * If async is nonzero, URBs will be sent without waiting for
382 * completion of the previous URB.
383 *
384 * (return 0 on success)
385 */
386
387static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
388 char *kernbuffer, const char __user *userbuffer, int index,
389 ssize_t *bytes_written, unsigned int tflags, int async)
390{
391 int result = 0, retry, count = len;
392 int passsize, thispass, transferred_len = 0;
393 int fromuser = (userbuffer != NULL) ? 1 : 0;
394 int fromkern = (kernbuffer != NULL) ? 1 : 0;
395 unsigned int pipe;
396 char *buffer;
397
398 (*bytes_written) = 0;
399
400 /* Sanity check */
401 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
402 return -ENODEV;
403
404 /* If we copy data from kernel or userspace, force the
405 * allocation of a buffer/urb. If we have the data in
406 * the transfer buffer[index] already, reuse the buffer/URB
407 * if the length is > buffer size. (So, transmitting
408 * large data amounts directly from the transfer buffer
409 * treats the buffer as a ring buffer. However, we need
410 * to sync in this case.)
411 */
412 if (fromuser || fromkern)
413 index = -1;
414 else if (len > sisusb->obufsize)
415 async = 0;
416
417 pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
418
419 do {
420 passsize = thispass = (sisusb->obufsize < count) ?
421 sisusb->obufsize : count;
422
423 if (index < 0)
424 index = sisusb_get_free_outbuf(sisusb);
425
426 if (index < 0)
427 return -EIO;
428
429 buffer = sisusb->obuf[index];
430
431 if (fromuser) {
432
433 if (copy_from_user(buffer, userbuffer, passsize))
434 return -EFAULT;
435
436 userbuffer += passsize;
437
438 } else if (fromkern) {
439
440 memcpy(buffer, kernbuffer, passsize);
441 kernbuffer += passsize;
442
443 }
444
445 retry = 5;
446 while (thispass) {
447
448 if (!sisusb->sisusb_dev)
449 return -ENODEV;
450
451 result = sisusb_bulkout_msg(sisusb,
452 index,
453 pipe,
454 buffer,
455 thispass,
456 &transferred_len,
457 async ? 0 : 5 * HZ,
458 tflags,
459 sisusb->transfer_dma_out[index]);
460
461 if (result == -ETIMEDOUT) {
462
463 /* Will not happen if async */
464 if (!retry--)
465 return -ETIME;
466
467 continue;
468
469 } else if ((result == 0) && !async && transferred_len) {
470
471 thispass -= transferred_len;
472 if (thispass) {
473 if (sisusb->transfer_dma_out) {
474 /* If DMA, copy remaining
475 * to beginning of buffer
476 */
477 memcpy(buffer,
478 buffer + transferred_len,
479 thispass);
480 } else {
481 /* If not DMA, simply increase
482 * the pointer
483 */
484 buffer += transferred_len;
485 }
486 }
487
488 } else
489 break;
490 };
491
492 if (result)
493 return result;
494
495 (*bytes_written) += passsize;
496 count -= passsize;
497
498 /* Force new allocation in next iteration */
499 if (fromuser || fromkern)
500 index = -1;
501
502 } while (count > 0);
503
504 if (async) {
505#ifdef SISUSB_DONTSYNC
506 (*bytes_written) = len;
507 /* Some URBs/buffers might be busy */
508#else
509 sisusb_wait_all_out_complete(sisusb);
510 (*bytes_written) = transferred_len;
511 /* All URBs and all buffers are available */
512#endif
513 }
514
515 return ((*bytes_written) == len) ? 0 : -EIO;
516}
517
518/* Receive a bulk message of variable size
519 *
520 * To copy the data to userspace, give pointer to "userbuffer",
521 * to copy to kernel memory, give "kernbuffer". One of them
522 * MUST be set. (There is no technique for letting the caller
523 * read directly from the ibuf.)
524 *
525 */
526
527static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
528 void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
529 unsigned int tflags)
530{
531 int result = 0, retry, count = len;
532 int bufsize, thispass, transferred_len;
533 unsigned int pipe;
534 char *buffer;
535
536 (*bytes_read) = 0;
537
538 /* Sanity check */
539 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
540 return -ENODEV;
541
542 pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
543 buffer = sisusb->ibuf;
544 bufsize = sisusb->ibufsize;
545
546 retry = 5;
547
548#ifdef SISUSB_DONTSYNC
549 if (!(sisusb_wait_all_out_complete(sisusb)))
550 return -EIO;
551#endif
552
553 while (count > 0) {
554
555 if (!sisusb->sisusb_dev)
556 return -ENODEV;
557
558 thispass = (bufsize < count) ? bufsize : count;
559
560 result = sisusb_bulkin_msg(sisusb,
561 pipe,
562 buffer,
563 thispass,
564 &transferred_len,
565 5 * HZ,
566 tflags,
567 sisusb->transfer_dma_in);
568
569 if (transferred_len)
570 thispass = transferred_len;
571
572 else if (result == -ETIMEDOUT) {
573
574 if (!retry--)
575 return -ETIME;
576
577 continue;
578
579 } else
580 return -EIO;
581
582
583 if (thispass) {
584
585 (*bytes_read) += thispass;
586 count -= thispass;
587
588 if (userbuffer) {
589
590 if (copy_to_user(userbuffer, buffer, thispass))
591 return -EFAULT;
592
593 userbuffer += thispass;
594
595 } else {
596
597 memcpy(kernbuffer, buffer, thispass);
598 kernbuffer += thispass;
599
600 }
601
602 }
603
604 }
605
606 return ((*bytes_read) == len) ? 0 : -EIO;
607}
608
609static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
610 struct sisusb_packet *packet)
611{
612 int ret;
613 ssize_t bytes_transferred = 0;
614 __le32 tmp;
615
616 if (len == 6)
617 packet->data = 0;
618
619#ifdef SISUSB_DONTSYNC
620 if (!(sisusb_wait_all_out_complete(sisusb)))
621 return 1;
622#endif
623
624 /* Eventually correct endianness */
625 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
626
627 /* 1. send the packet */
628 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
629 (char *)packet, NULL, 0, &bytes_transferred, 0, 0);
630
631 if ((ret == 0) && (len == 6)) {
632
633 /* 2. if packet len == 6, it means we read, so wait for 32bit
634 * return value and write it to packet->data
635 */
636 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
637 (char *)&tmp, NULL, &bytes_transferred, 0);
638
639 packet->data = le32_to_cpu(tmp);
640 }
641
642 return ret;
643}
644
645static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
646 struct sisusb_packet *packet,
647 unsigned int tflags)
648{
649 int ret;
650 ssize_t bytes_transferred = 0;
651 __le32 tmp;
652
653 if (len == 6)
654 packet->data = 0;
655
656#ifdef SISUSB_DONTSYNC
657 if (!(sisusb_wait_all_out_complete(sisusb)))
658 return 1;
659#endif
660
661 /* Eventually correct endianness */
662 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
663
664 /* 1. send the packet */
665 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
666 (char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
667
668 if ((ret == 0) && (len == 6)) {
669
670 /* 2. if packet len == 6, it means we read, so wait for 32bit
671 * return value and write it to packet->data
672 */
673 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
674 (char *)&tmp, NULL, &bytes_transferred, 0);
675
676 packet->data = le32_to_cpu(tmp);
677 }
678
679 return ret;
680}
681
682/* access video memory and mmio (return 0 on success) */
683
684/* Low level */
685
686/* The following routines assume being used to transfer byte, word,
687 * long etc.
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200688 * This means that
689 * - the write routines expect "data" in machine endianness format.
690 * The data will be converted to leXX in sisusb_xxx_packet.
691 * - the read routines can expect read data in machine-endianess.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 */
693
694static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
695 u32 addr, u8 data)
696{
697 struct sisusb_packet packet;
698 int ret;
699
700 packet.header = (1 << (addr & 3)) | (type << 6);
701 packet.address = addr & ~3;
702 packet.data = data << ((addr & 3) << 3);
703 ret = sisusb_send_packet(sisusb, 10, &packet);
704 return ret;
705}
706
707static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
708 u32 addr, u16 data)
709{
710 struct sisusb_packet packet;
711 int ret = 0;
712
713 packet.address = addr & ~3;
714
715 switch (addr & 3) {
716 case 0:
717 packet.header = (type << 6) | 0x0003;
718 packet.data = (u32)data;
719 ret = sisusb_send_packet(sisusb, 10, &packet);
720 break;
721 case 1:
722 packet.header = (type << 6) | 0x0006;
723 packet.data = (u32)data << 8;
724 ret = sisusb_send_packet(sisusb, 10, &packet);
725 break;
726 case 2:
727 packet.header = (type << 6) | 0x000c;
728 packet.data = (u32)data << 16;
729 ret = sisusb_send_packet(sisusb, 10, &packet);
730 break;
731 case 3:
732 packet.header = (type << 6) | 0x0008;
733 packet.data = (u32)data << 24;
734 ret = sisusb_send_packet(sisusb, 10, &packet);
735 packet.header = (type << 6) | 0x0001;
736 packet.address = (addr & ~3) + 4;
737 packet.data = (u32)data >> 8;
738 ret |= sisusb_send_packet(sisusb, 10, &packet);
739 }
740
741 return ret;
742}
743
744static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
745 u32 addr, u32 data)
746{
747 struct sisusb_packet packet;
748 int ret = 0;
749
750 packet.address = addr & ~3;
751
752 switch (addr & 3) {
753 case 0:
754 packet.header = (type << 6) | 0x0007;
755 packet.data = data & 0x00ffffff;
756 ret = sisusb_send_packet(sisusb, 10, &packet);
757 break;
758 case 1:
759 packet.header = (type << 6) | 0x000e;
760 packet.data = data << 8;
761 ret = sisusb_send_packet(sisusb, 10, &packet);
762 break;
763 case 2:
764 packet.header = (type << 6) | 0x000c;
765 packet.data = data << 16;
766 ret = sisusb_send_packet(sisusb, 10, &packet);
767 packet.header = (type << 6) | 0x0001;
768 packet.address = (addr & ~3) + 4;
769 packet.data = (data >> 16) & 0x00ff;
770 ret |= sisusb_send_packet(sisusb, 10, &packet);
771 break;
772 case 3:
773 packet.header = (type << 6) | 0x0008;
774 packet.data = data << 24;
775 ret = sisusb_send_packet(sisusb, 10, &packet);
776 packet.header = (type << 6) | 0x0003;
777 packet.address = (addr & ~3) + 4;
778 packet.data = (data >> 8) & 0xffff;
779 ret |= sisusb_send_packet(sisusb, 10, &packet);
780 }
781
782 return ret;
783}
784
785static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
786 u32 addr, u32 data)
787{
788 struct sisusb_packet packet;
789 int ret = 0;
790
791 packet.address = addr & ~3;
792
793 switch (addr & 3) {
794 case 0:
795 packet.header = (type << 6) | 0x000f;
796 packet.data = data;
797 ret = sisusb_send_packet(sisusb, 10, &packet);
798 break;
799 case 1:
800 packet.header = (type << 6) | 0x000e;
801 packet.data = data << 8;
802 ret = sisusb_send_packet(sisusb, 10, &packet);
803 packet.header = (type << 6) | 0x0001;
804 packet.address = (addr & ~3) + 4;
805 packet.data = data >> 24;
806 ret |= sisusb_send_packet(sisusb, 10, &packet);
807 break;
808 case 2:
809 packet.header = (type << 6) | 0x000c;
810 packet.data = data << 16;
811 ret = sisusb_send_packet(sisusb, 10, &packet);
812 packet.header = (type << 6) | 0x0003;
813 packet.address = (addr & ~3) + 4;
814 packet.data = data >> 16;
815 ret |= sisusb_send_packet(sisusb, 10, &packet);
816 break;
817 case 3:
818 packet.header = (type << 6) | 0x0008;
819 packet.data = data << 24;
820 ret = sisusb_send_packet(sisusb, 10, &packet);
821 packet.header = (type << 6) | 0x0007;
822 packet.address = (addr & ~3) + 4;
823 packet.data = data >> 8;
824 ret |= sisusb_send_packet(sisusb, 10, &packet);
825 }
826
827 return ret;
828}
829
830/* The xxx_bulk routines copy a buffer of variable size. They treat the
831 * buffer as chars, therefore lsb/msb has to be corrected if using the
832 * byte/word/long/etc routines for speed-up
833 *
834 * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
835 * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
836 * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
837 * that the data already is in the transfer buffer "sisusb->obuf[index]".
838 */
839
840static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
841 char *kernbuffer, int length,
842 const char __user *userbuffer, int index,
843 ssize_t *bytes_written)
844{
845 struct sisusb_packet packet;
846 int ret = 0;
847 static int msgcount = 0;
848 u8 swap8, fromkern = kernbuffer ? 1 : 0;
849 u16 swap16;
850 u32 swap32, flag = (length >> 28) & 1;
851 char buf[4];
852
853 /* if neither kernbuffer not userbuffer are given, assume
854 * data in obuf
855 */
856 if (!fromkern && !userbuffer)
857 kernbuffer = sisusb->obuf[index];
858
859 (*bytes_written = 0);
860
861 length &= 0x00ffffff;
862
863 while (length) {
864
865 switch (length) {
866
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 case 1:
868 if (userbuffer) {
869 if (get_user(swap8, (u8 __user *)userbuffer))
870 return -EFAULT;
871 } else
872 swap8 = kernbuffer[0];
873
874 ret = sisusb_write_memio_byte(sisusb,
875 SISUSB_TYPE_MEM,
876 addr, swap8);
877
878 if (!ret)
879 (*bytes_written)++;
880
881 return ret;
882
883 case 2:
884 if (userbuffer) {
885 if (get_user(swap16, (u16 __user *)userbuffer))
886 return -EFAULT;
887 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200888 swap16 = *((u16 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
890 ret = sisusb_write_memio_word(sisusb,
891 SISUSB_TYPE_MEM,
892 addr,
893 swap16);
894
895 if (!ret)
896 (*bytes_written) += 2;
897
898 return ret;
899
900 case 3:
901 if (userbuffer) {
902 if (copy_from_user(&buf, userbuffer, 3))
903 return -EFAULT;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200904#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 swap32 = (buf[0] << 16) |
906 (buf[1] << 8) |
907 buf[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200908#else
909 swap32 = (buf[2] << 16) |
910 (buf[1] << 8) |
911 buf[0];
912#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200914#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 swap32 = (kernbuffer[0] << 16) |
916 (kernbuffer[1] << 8) |
917 kernbuffer[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200918#else
919 swap32 = (kernbuffer[2] << 16) |
920 (kernbuffer[1] << 8) |
921 kernbuffer[0];
922#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
924 ret = sisusb_write_memio_24bit(sisusb,
925 SISUSB_TYPE_MEM,
926 addr,
927 swap32);
928
929 if (!ret)
930 (*bytes_written) += 3;
931
932 return ret;
933
934 case 4:
935 if (userbuffer) {
936 if (get_user(swap32, (u32 __user *)userbuffer))
937 return -EFAULT;
938 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200939 swap32 = *((u32 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
941 ret = sisusb_write_memio_long(sisusb,
942 SISUSB_TYPE_MEM,
943 addr,
944 swap32);
945 if (!ret)
946 (*bytes_written) += 4;
947
948 return ret;
949
950 default:
951 if ((length & ~3) > 0x10000) {
952
953 packet.header = 0x001f;
954 packet.address = 0x000001d4;
955 packet.data = addr;
956 ret = sisusb_send_bridge_packet(sisusb, 10,
957 &packet, 0);
958 packet.header = 0x001f;
959 packet.address = 0x000001d0;
960 packet.data = (length & ~3);
961 ret |= sisusb_send_bridge_packet(sisusb, 10,
962 &packet, 0);
963 packet.header = 0x001f;
964 packet.address = 0x000001c0;
965 packet.data = flag | 0x16;
966 ret |= sisusb_send_bridge_packet(sisusb, 10,
967 &packet, 0);
968 if (userbuffer) {
969 ret |= sisusb_send_bulk_msg(sisusb,
970 SISUSB_EP_GFX_LBULK_OUT,
971 (length & ~3),
972 NULL, userbuffer, 0,
973 bytes_written, 0, 1);
974 userbuffer += (*bytes_written);
975 } else if (fromkern) {
976 ret |= sisusb_send_bulk_msg(sisusb,
977 SISUSB_EP_GFX_LBULK_OUT,
978 (length & ~3),
979 kernbuffer, NULL, 0,
980 bytes_written, 0, 1);
981 kernbuffer += (*bytes_written);
982 } else {
983 ret |= sisusb_send_bulk_msg(sisusb,
984 SISUSB_EP_GFX_LBULK_OUT,
985 (length & ~3),
986 NULL, NULL, index,
987 bytes_written, 0, 1);
988 kernbuffer += ((*bytes_written) &
989 (sisusb->obufsize-1));
990 }
991
992 } else {
993
994 packet.header = 0x001f;
995 packet.address = 0x00000194;
996 packet.data = addr;
997 ret = sisusb_send_bridge_packet(sisusb, 10,
998 &packet, 0);
999 packet.header = 0x001f;
1000 packet.address = 0x00000190;
1001 packet.data = (length & ~3);
1002 ret |= sisusb_send_bridge_packet(sisusb, 10,
1003 &packet, 0);
1004 if (sisusb->flagb0 != 0x16) {
1005 packet.header = 0x001f;
1006 packet.address = 0x00000180;
1007 packet.data = flag | 0x16;
1008 ret |= sisusb_send_bridge_packet(sisusb, 10,
1009 &packet, 0);
1010 sisusb->flagb0 = 0x16;
1011 }
1012 if (userbuffer) {
1013 ret |= sisusb_send_bulk_msg(sisusb,
1014 SISUSB_EP_GFX_BULK_OUT,
1015 (length & ~3),
1016 NULL, userbuffer, 0,
1017 bytes_written, 0, 1);
1018 userbuffer += (*bytes_written);
1019 } else if (fromkern) {
1020 ret |= sisusb_send_bulk_msg(sisusb,
1021 SISUSB_EP_GFX_BULK_OUT,
1022 (length & ~3),
1023 kernbuffer, NULL, 0,
1024 bytes_written, 0, 1);
1025 kernbuffer += (*bytes_written);
1026 } else {
1027 ret |= sisusb_send_bulk_msg(sisusb,
1028 SISUSB_EP_GFX_BULK_OUT,
1029 (length & ~3),
1030 NULL, NULL, index,
1031 bytes_written, 0, 1);
1032 kernbuffer += ((*bytes_written) &
1033 (sisusb->obufsize-1));
1034 }
1035 }
1036 if (ret) {
1037 msgcount++;
1038 if (msgcount < 500)
1039 printk(KERN_ERR
Al Viro5330e922005-04-26 11:26:53 -07001040 "sisusbvga[%d]: Wrote %zd of "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 "%d bytes, error %d\n",
1042 sisusb->minor, *bytes_written,
1043 length, ret);
1044 else if (msgcount == 500)
1045 printk(KERN_ERR
1046 "sisusbvga[%d]: Too many errors"
1047 ", logging stopped\n",
1048 sisusb->minor);
1049 }
1050 addr += (*bytes_written);
1051 length -= (*bytes_written);
1052 }
1053
1054 if (ret)
1055 break;
1056
1057 }
1058
1059 return ret ? -EIO : 0;
1060}
1061
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001062/* Remember: Read data in packet is in machine-endianess! So for
1063 * byte, word, 24bit, long no endian correction is necessary.
1064 */
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
1067 u32 addr, u8 *data)
1068{
1069 struct sisusb_packet packet;
1070 int ret;
1071
1072 CLEARPACKET(&packet);
1073 packet.header = (1 << (addr & 3)) | (type << 6);
1074 packet.address = addr & ~3;
1075 ret = sisusb_send_packet(sisusb, 6, &packet);
1076 *data = (u8)(packet.data >> ((addr & 3) << 3));
1077 return ret;
1078}
1079
1080static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
1081 u32 addr, u16 *data)
1082{
1083 struct sisusb_packet packet;
1084 int ret = 0;
1085
1086 CLEARPACKET(&packet);
1087
1088 packet.address = addr & ~3;
1089
1090 switch (addr & 3) {
1091 case 0:
1092 packet.header = (type << 6) | 0x0003;
1093 ret = sisusb_send_packet(sisusb, 6, &packet);
1094 *data = (u16)(packet.data);
1095 break;
1096 case 1:
1097 packet.header = (type << 6) | 0x0006;
1098 ret = sisusb_send_packet(sisusb, 6, &packet);
1099 *data = (u16)(packet.data >> 8);
1100 break;
1101 case 2:
1102 packet.header = (type << 6) | 0x000c;
1103 ret = sisusb_send_packet(sisusb, 6, &packet);
1104 *data = (u16)(packet.data >> 16);
1105 break;
1106 case 3:
1107 packet.header = (type << 6) | 0x0008;
1108 ret = sisusb_send_packet(sisusb, 6, &packet);
1109 *data = (u16)(packet.data >> 24);
1110 packet.header = (type << 6) | 0x0001;
1111 packet.address = (addr & ~3) + 4;
1112 ret |= sisusb_send_packet(sisusb, 6, &packet);
1113 *data |= (u16)(packet.data << 8);
1114 }
1115
1116 return ret;
1117}
1118
1119static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
1120 u32 addr, u32 *data)
1121{
1122 struct sisusb_packet packet;
1123 int ret = 0;
1124
1125 packet.address = addr & ~3;
1126
1127 switch (addr & 3) {
1128 case 0:
1129 packet.header = (type << 6) | 0x0007;
1130 ret = sisusb_send_packet(sisusb, 6, &packet);
1131 *data = packet.data & 0x00ffffff;
1132 break;
1133 case 1:
1134 packet.header = (type << 6) | 0x000e;
1135 ret = sisusb_send_packet(sisusb, 6, &packet);
1136 *data = packet.data >> 8;
1137 break;
1138 case 2:
1139 packet.header = (type << 6) | 0x000c;
1140 ret = sisusb_send_packet(sisusb, 6, &packet);
1141 *data = packet.data >> 16;
1142 packet.header = (type << 6) | 0x0001;
1143 packet.address = (addr & ~3) + 4;
1144 ret |= sisusb_send_packet(sisusb, 6, &packet);
1145 *data |= ((packet.data & 0xff) << 16);
1146 break;
1147 case 3:
1148 packet.header = (type << 6) | 0x0008;
1149 ret = sisusb_send_packet(sisusb, 6, &packet);
1150 *data = packet.data >> 24;
1151 packet.header = (type << 6) | 0x0003;
1152 packet.address = (addr & ~3) + 4;
1153 ret |= sisusb_send_packet(sisusb, 6, &packet);
1154 *data |= ((packet.data & 0xffff) << 8);
1155 }
1156
1157 return ret;
1158}
1159
1160static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
1161 u32 addr, u32 *data)
1162{
1163 struct sisusb_packet packet;
1164 int ret = 0;
1165
1166 packet.address = addr & ~3;
1167
1168 switch (addr & 3) {
1169 case 0:
1170 packet.header = (type << 6) | 0x000f;
1171 ret = sisusb_send_packet(sisusb, 6, &packet);
1172 *data = packet.data;
1173 break;
1174 case 1:
1175 packet.header = (type << 6) | 0x000e;
1176 ret = sisusb_send_packet(sisusb, 6, &packet);
1177 *data = packet.data >> 8;
1178 packet.header = (type << 6) | 0x0001;
1179 packet.address = (addr & ~3) + 4;
1180 ret |= sisusb_send_packet(sisusb, 6, &packet);
1181 *data |= (packet.data << 24);
1182 break;
1183 case 2:
1184 packet.header = (type << 6) | 0x000c;
1185 ret = sisusb_send_packet(sisusb, 6, &packet);
1186 *data = packet.data >> 16;
1187 packet.header = (type << 6) | 0x0003;
1188 packet.address = (addr & ~3) + 4;
1189 ret |= sisusb_send_packet(sisusb, 6, &packet);
1190 *data |= (packet.data << 16);
1191 break;
1192 case 3:
1193 packet.header = (type << 6) | 0x0008;
1194 ret = sisusb_send_packet(sisusb, 6, &packet);
1195 *data = packet.data >> 24;
1196 packet.header = (type << 6) | 0x0007;
1197 packet.address = (addr & ~3) + 4;
1198 ret |= sisusb_send_packet(sisusb, 6, &packet);
1199 *data |= (packet.data << 8);
1200 }
1201
1202 return ret;
1203}
1204
1205static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
1206 char *kernbuffer, int length,
1207 char __user *userbuffer, ssize_t *bytes_read)
1208{
1209 int ret = 0;
1210 char buf[4];
1211 u16 swap16;
1212 u32 swap32;
1213
1214 (*bytes_read = 0);
1215
1216 length &= 0x00ffffff;
1217
1218 while (length) {
1219
1220 switch (length) {
1221
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 case 1:
1223
1224 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
1225 addr, &buf[0]);
1226 if (!ret) {
1227 (*bytes_read)++;
1228 if (userbuffer) {
1229 if (put_user(buf[0],
1230 (u8 __user *)userbuffer)) {
1231 return -EFAULT;
1232 }
1233 } else {
1234 kernbuffer[0] = buf[0];
1235 }
1236 }
1237 return ret;
1238
1239 case 2:
1240 ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
1241 addr, &swap16);
1242 if (!ret) {
1243 (*bytes_read) += 2;
1244 if (userbuffer) {
1245 if (put_user(swap16,
1246 (u16 __user *)userbuffer))
1247 return -EFAULT;
1248 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001249 *((u16 *)kernbuffer) = swap16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 }
1251 }
1252 return ret;
1253
1254 case 3:
1255 ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
1256 addr, &swap32);
1257 if (!ret) {
1258 (*bytes_read) += 3;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001259#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 buf[0] = (swap32 >> 16) & 0xff;
1261 buf[1] = (swap32 >> 8) & 0xff;
1262 buf[2] = swap32 & 0xff;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001263#else
1264 buf[2] = (swap32 >> 16) & 0xff;
1265 buf[1] = (swap32 >> 8) & 0xff;
1266 buf[0] = swap32 & 0xff;
1267#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 if (userbuffer) {
1269 if (copy_to_user(userbuffer, &buf[0], 3))
1270 return -EFAULT;
1271 } else {
1272 kernbuffer[0] = buf[0];
1273 kernbuffer[1] = buf[1];
1274 kernbuffer[2] = buf[2];
1275 }
1276 }
1277 return ret;
1278
1279 default:
1280 ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
1281 addr, &swap32);
1282 if (!ret) {
1283 (*bytes_read) += 4;
1284 if (userbuffer) {
1285 if (put_user(swap32,
1286 (u32 __user *)userbuffer))
1287 return -EFAULT;
1288
1289 userbuffer += 4;
1290 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001291 *((u32 *)kernbuffer) = swap32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 kernbuffer += 4;
1293 }
1294 addr += 4;
1295 length -= 4;
1296 }
1297#if 0 /* That does not work, as EP 2 is an OUT EP! */
1298 default:
1299 CLEARPACKET(&packet);
1300 packet.header = 0x001f;
1301 packet.address = 0x000001a0;
1302 packet.data = 0x00000006;
1303 ret |= sisusb_send_bridge_packet(sisusb, 10,
1304 &packet, 0);
1305 packet.header = 0x001f;
1306 packet.address = 0x000001b0;
1307 packet.data = (length & ~3) | 0x40000000;
1308 ret |= sisusb_send_bridge_packet(sisusb, 10,
1309 &packet, 0);
1310 packet.header = 0x001f;
1311 packet.address = 0x000001b4;
1312 packet.data = addr;
1313 ret |= sisusb_send_bridge_packet(sisusb, 10,
1314 &packet, 0);
1315 packet.header = 0x001f;
1316 packet.address = 0x000001a4;
1317 packet.data = 0x00000001;
1318 ret |= sisusb_send_bridge_packet(sisusb, 10,
1319 &packet, 0);
1320 if (userbuffer) {
1321 ret |= sisusb_recv_bulk_msg(sisusb,
1322 SISUSB_EP_GFX_BULK_IN,
1323 (length & ~3),
1324 NULL, userbuffer,
1325 bytes_read, 0);
1326 if (!ret) userbuffer += (*bytes_read);
1327 } else {
1328 ret |= sisusb_recv_bulk_msg(sisusb,
1329 SISUSB_EP_GFX_BULK_IN,
1330 (length & ~3),
1331 kernbuffer, NULL,
1332 bytes_read, 0);
1333 if (!ret) kernbuffer += (*bytes_read);
1334 }
1335 addr += (*bytes_read);
1336 length -= (*bytes_read);
1337#endif
1338 }
1339
1340 if (ret)
1341 break;
1342 }
1343
1344 return ret;
1345}
1346
1347/* High level: Gfx (indexed) register access */
1348
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001349#ifdef INCL_SISUSB_CON
1350int
1351sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
1352{
1353 return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1354}
1355
1356int
1357sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
1358{
1359 return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1360}
1361#endif
1362
1363#ifndef INCL_SISUSB_CON
1364static
1365#endif
1366int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
1368{
1369 int ret;
1370 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1371 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1372 return ret;
1373}
1374
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001375#ifndef INCL_SISUSB_CON
1376static
1377#endif
1378int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
1380{
1381 int ret;
1382 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1383 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1384 return ret;
1385}
1386
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001387#ifndef INCL_SISUSB_CON
1388static
1389#endif
1390int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
1392 u8 myand, u8 myor)
1393{
1394 int ret;
1395 u8 tmp;
1396
1397 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1398 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1399 tmp &= myand;
1400 tmp |= myor;
1401 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1402 return ret;
1403}
1404
1405static int
1406sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx,
1407 u8 data, u8 mask)
1408{
1409 int ret;
1410 u8 tmp;
1411 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1412 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1413 tmp &= ~(mask);
1414 tmp |= (data & mask);
1415 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1416 return ret;
1417}
1418
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001419#ifndef INCL_SISUSB_CON
1420static
1421#endif
1422int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
1424{
1425 return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
1426}
1427
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001428#ifndef INCL_SISUSB_CON
1429static
1430#endif
1431int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
1433{
1434 return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00));
1435}
1436
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001437/* Write/read video ram */
1438
1439#ifdef INCL_SISUSB_CON
1440int
1441sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data)
1442{
1443 return(sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
1444}
1445
1446int
1447sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data)
1448{
1449 return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
1450}
1451
1452int
1453sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data)
1454{
1455 return(sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data));
1456}
1457
1458int
1459sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data)
1460{
1461 return(sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data));
1462}
1463
1464int
1465sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
1466 u32 dest, int length, size_t *bytes_written)
1467{
1468 return(sisusb_write_mem_bulk(sisusb, dest, src, length, NULL, 0, bytes_written));
1469}
1470
1471#ifdef SISUSBENDIANTEST
1472int
1473sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest,
1474 u32 src, int length, size_t *bytes_written)
1475{
1476 return(sisusb_read_mem_bulk(sisusb, src, dest, length, NULL, bytes_written));
1477}
1478#endif
1479#endif
1480
1481#ifdef SISUSBENDIANTEST
1482static void
1483sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
1484{
1485 static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
1486 char destbuffer[10];
1487 size_t dummy;
1488 int i,j;
1489
1490 sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy);
1491
1492 for(i = 1; i <= 7; i++) {
1493 printk(KERN_DEBUG "sisusb: rwtest %d bytes\n", i);
1494 sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy);
1495 for(j = 0; j < i; j++) {
1496 printk(KERN_DEBUG "sisusb: rwtest read[%d] = %x\n", j, destbuffer[j]);
1497 }
1498 }
1499}
1500#endif
1501
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502/* access pci config registers (reg numbers 0, 4, 8, etc) */
1503
1504static int
1505sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data)
1506{
1507 struct sisusb_packet packet;
1508 int ret;
1509
1510 packet.header = 0x008f;
1511 packet.address = regnum | 0x10000;
1512 packet.data = data;
1513 ret = sisusb_send_packet(sisusb, 10, &packet);
1514 return ret;
1515}
1516
1517static int
1518sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data)
1519{
1520 struct sisusb_packet packet;
1521 int ret;
1522
1523 packet.header = 0x008f;
1524 packet.address = (u32)regnum | 0x10000;
1525 ret = sisusb_send_packet(sisusb, 6, &packet);
1526 *data = packet.data;
1527 return ret;
1528}
1529
1530/* Clear video RAM */
1531
1532static int
1533sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
1534{
1535 int ret, i;
1536 ssize_t j;
1537
1538 if (address < sisusb->vrambase)
1539 return 1;
1540
1541 if (address >= sisusb->vrambase + sisusb->vramsize)
1542 return 1;
1543
1544 if (address + length > sisusb->vrambase + sisusb->vramsize)
1545 length = sisusb->vrambase + sisusb->vramsize - address;
1546
1547 if (length <= 0)
1548 return 0;
1549
1550 /* allocate free buffer/urb and clear the buffer */
1551 if ((i = sisusb_alloc_outbuf(sisusb)) < 0)
1552 return -EBUSY;
1553
1554 memset(sisusb->obuf[i], 0, sisusb->obufsize);
1555
1556 /* We can write a length > buffer size here. The buffer
1557 * data will simply be re-used (like a ring-buffer).
1558 */
1559 ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
1560
1561 /* Free the buffer/urb */
1562 sisusb_free_outbuf(sisusb, i);
1563
1564 return ret;
1565}
1566
1567/* Initialize the graphics core (return 0 on success)
1568 * This resets the graphics hardware and puts it into
1569 * a defined mode (640x480@60Hz)
1570 */
1571
1572#define GETREG(r,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1573#define SETREG(r,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1574#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d)
1575#define GETIREG(r,i,d) sisusb_getidxreg(sisusb, r, i, d)
1576#define SETIREGOR(r,i,o) sisusb_setidxregor(sisusb, r, i, o)
1577#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a)
1578#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o)
1579#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
1580#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
1581#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
1582#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
1583
1584static int
1585sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
1586{
1587 int ret;
1588 u8 tmp8;
1589
1590 ret = GETIREG(SISSR, 0x16, &tmp8);
1591 if (ramtype <= 1) {
1592 tmp8 &= 0x3f;
1593 ret |= SETIREG(SISSR, 0x16, tmp8);
1594 tmp8 |= 0x80;
1595 ret |= SETIREG(SISSR, 0x16, tmp8);
1596 } else {
1597 tmp8 |= 0xc0;
1598 ret |= SETIREG(SISSR, 0x16, tmp8);
1599 tmp8 &= 0x0f;
1600 ret |= SETIREG(SISSR, 0x16, tmp8);
1601 tmp8 |= 0x80;
1602 ret |= SETIREG(SISSR, 0x16, tmp8);
1603 tmp8 &= 0x0f;
1604 ret |= SETIREG(SISSR, 0x16, tmp8);
1605 tmp8 |= 0xd0;
1606 ret |= SETIREG(SISSR, 0x16, tmp8);
1607 tmp8 &= 0x0f;
1608 ret |= SETIREG(SISSR, 0x16, tmp8);
1609 tmp8 |= 0xa0;
1610 ret |= SETIREG(SISSR, 0x16, tmp8);
1611 }
1612 return ret;
1613}
1614
1615static int
1616sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab)
1617{
1618 int ret;
1619 u8 ramtype, done = 0;
1620 u32 t0, t1, t2, t3;
1621 u32 ramptr = SISUSB_PCI_MEMBASE;
1622
1623 ret = GETIREG(SISSR, 0x3a, &ramtype);
1624 ramtype &= 3;
1625
1626 ret |= SETIREG(SISSR, 0x13, 0x00);
1627
1628 if (ramtype <= 1) {
1629 ret |= SETIREG(SISSR, 0x14, 0x12);
1630 ret |= SETIREGAND(SISSR, 0x15, 0xef);
1631 } else {
1632 ret |= SETIREG(SISSR, 0x14, 0x02);
1633 }
1634
1635 ret |= sisusb_triggersr16(sisusb, ramtype);
1636 ret |= WRITEL(ramptr + 0, 0x01234567);
1637 ret |= WRITEL(ramptr + 4, 0x456789ab);
1638 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1639 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1640 ret |= WRITEL(ramptr + 16, 0x55555555);
1641 ret |= WRITEL(ramptr + 20, 0x55555555);
1642 ret |= WRITEL(ramptr + 24, 0xffffffff);
1643 ret |= WRITEL(ramptr + 28, 0xffffffff);
1644 ret |= READL(ramptr + 0, &t0);
1645 ret |= READL(ramptr + 4, &t1);
1646 ret |= READL(ramptr + 8, &t2);
1647 ret |= READL(ramptr + 12, &t3);
1648
1649 if (ramtype <= 1) {
1650
1651 *chab = 0; *bw = 64;
1652
1653 if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
1654 if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
1655 *chab = 0; *bw = 64;
1656 ret |= SETIREGAND(SISSR, 0x14, 0xfd);
1657 }
1658 }
1659 if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
1660 *chab = 1; *bw = 64;
1661 ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01);
1662
1663 ret |= sisusb_triggersr16(sisusb, ramtype);
1664 ret |= WRITEL(ramptr + 0, 0x89abcdef);
1665 ret |= WRITEL(ramptr + 4, 0xcdef0123);
1666 ret |= WRITEL(ramptr + 8, 0x55555555);
1667 ret |= WRITEL(ramptr + 12, 0x55555555);
1668 ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
1669 ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
1670 ret |= READL(ramptr + 4, &t1);
1671
1672 if (t1 != 0xcdef0123) {
1673 *bw = 32;
1674 ret |= SETIREGOR(SISSR, 0x15, 0x10);
1675 }
1676 }
1677
1678 } else {
1679
1680 *chab = 0; *bw = 64; /* default: cha, bw = 64 */
1681
1682 done = 0;
1683
1684 if (t1 == 0x456789ab) {
1685 if (t0 == 0x01234567) {
1686 *chab = 0; *bw = 64;
1687 done = 1;
1688 }
1689 } else {
1690 if (t0 == 0x01234567) {
1691 *chab = 0; *bw = 32;
1692 ret |= SETIREG(SISSR, 0x14, 0x00);
1693 done = 1;
1694 }
1695 }
1696
1697 if (!done) {
1698 ret |= SETIREG(SISSR, 0x14, 0x03);
1699 ret |= sisusb_triggersr16(sisusb, ramtype);
1700
1701 ret |= WRITEL(ramptr + 0, 0x01234567);
1702 ret |= WRITEL(ramptr + 4, 0x456789ab);
1703 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1704 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1705 ret |= WRITEL(ramptr + 16, 0x55555555);
1706 ret |= WRITEL(ramptr + 20, 0x55555555);
1707 ret |= WRITEL(ramptr + 24, 0xffffffff);
1708 ret |= WRITEL(ramptr + 28, 0xffffffff);
1709 ret |= READL(ramptr + 0, &t0);
1710 ret |= READL(ramptr + 4, &t1);
1711
1712 if (t1 == 0x456789ab) {
1713 if (t0 == 0x01234567) {
1714 *chab = 1; *bw = 64;
1715 return ret;
1716 } /* else error */
1717 } else {
1718 if (t0 == 0x01234567) {
1719 *chab = 1; *bw = 32;
1720 ret |= SETIREG(SISSR, 0x14, 0x01);
1721 } /* else error */
1722 }
1723 }
1724 }
1725 return ret;
1726}
1727
1728static int
1729sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
1730{
1731 int ret = 0;
1732 u32 ramptr = SISUSB_PCI_MEMBASE;
1733 u8 tmp1, tmp2, i, j;
1734
1735 ret |= WRITEB(ramptr, 0xaa);
1736 ret |= WRITEB(ramptr + 16, 0x55);
1737 ret |= READB(ramptr, &tmp1);
1738 ret |= READB(ramptr + 16, &tmp2);
1739 if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
1740 for (i = 0, j = 16; i < 2; i++, j += 16) {
1741 ret |= GETIREG(SISSR, 0x21, &tmp1);
1742 ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
1743 ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */
1744 ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
1745 ret |= SETIREG(SISSR, 0x21, tmp1);
1746 ret |= WRITEB(ramptr + 16 + j, j);
1747 ret |= READB(ramptr + 16 + j, &tmp1);
1748 if (tmp1 == j) {
1749 ret |= WRITEB(ramptr + j, j);
1750 break;
1751 }
1752 }
1753 }
1754 return ret;
1755}
1756
1757static int
1758sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index,
1759 u8 rankno, u8 chab, const u8 dramtype[][5],
1760 int bw)
1761{
1762 int ret = 0, ranksize;
1763 u8 tmp;
1764
1765 *iret = 0;
1766
1767 if ((rankno == 2) && (dramtype[index][0] == 2))
1768 return ret;
1769
1770 ranksize = dramtype[index][3] / 2 * bw / 32;
1771
1772 if ((ranksize * rankno) > 128)
1773 return ret;
1774
1775 tmp = 0;
1776 while ((ranksize >>= 1) > 0) tmp += 0x10;
1777 tmp |= ((rankno - 1) << 2);
1778 tmp |= ((bw / 64) & 0x02);
1779 tmp |= (chab & 0x01);
1780
1781 ret = SETIREG(SISSR, 0x14, tmp);
1782 ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
1783
1784 *iret = 1;
1785
1786 return ret;
1787}
1788
1789static int
1790sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn)
1791{
1792 int ret = 0, i;
1793 u32 j, tmp;
1794
1795 *iret = 0;
1796
1797 for (i = 0, j = 0; i < testn; i++) {
1798 ret |= WRITEL(sisusb->vrambase + j, j);
1799 j += inc;
1800 }
1801
1802 for (i = 0, j = 0; i < testn; i++) {
1803 ret |= READL(sisusb->vrambase + j, &tmp);
1804 if (tmp != j) return ret;
1805 j += inc;
1806 }
1807
1808 *iret = 1;
1809 return ret;
1810}
1811
1812static int
1813sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno,
1814 int idx, int bw, const u8 rtype[][5])
1815{
1816 int ret = 0, i, i2ret;
1817 u32 inc;
1818
1819 *iret = 0;
1820
1821 for (i = rankno; i >= 1; i--) {
1822 inc = 1 << (rtype[idx][2] +
1823 rtype[idx][1] +
1824 rtype[idx][0] +
1825 bw / 64 + i);
1826 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1827 if (!i2ret)
1828 return ret;
1829 }
1830
1831 inc = 1 << (rtype[idx][2] + bw / 64 + 2);
1832 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
1833 if (!i2ret)
1834 return ret;
1835
1836 inc = 1 << (10 + bw / 64);
1837 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1838 if (!i2ret)
1839 return ret;
1840
1841 *iret = 1;
1842 return ret;
1843}
1844
1845static int
1846sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw,
1847 int chab)
1848{
1849 int ret = 0, i2ret = 0, i, j;
1850 static const u8 sdramtype[13][5] = {
1851 { 2, 12, 9, 64, 0x35 },
1852 { 1, 13, 9, 64, 0x44 },
1853 { 2, 12, 8, 32, 0x31 },
1854 { 2, 11, 9, 32, 0x25 },
1855 { 1, 12, 9, 32, 0x34 },
1856 { 1, 13, 8, 32, 0x40 },
1857 { 2, 11, 8, 16, 0x21 },
1858 { 1, 12, 8, 16, 0x30 },
1859 { 1, 11, 9, 16, 0x24 },
1860 { 1, 11, 8, 8, 0x20 },
1861 { 2, 9, 8, 4, 0x01 },
1862 { 1, 10, 8, 4, 0x10 },
1863 { 1, 9, 8, 2, 0x00 }
1864 };
1865
1866 *iret = 1; /* error */
1867
1868 for (i = 0; i < 13; i++) {
1869 ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
1870 for (j = 2; j > 0; j--) {
1871 ret |= sisusb_set_rank(sisusb, &i2ret, i, j,
1872 chab, sdramtype, bw);
1873 if (!i2ret)
1874 continue;
1875
1876 ret |= sisusb_check_ranks(sisusb, &i2ret, j, i,
1877 bw, sdramtype);
1878 if (i2ret) {
1879 *iret = 0; /* ram size found */
1880 return ret;
1881 }
1882 }
1883 }
1884
1885 return ret;
1886}
1887
1888static int
1889sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
1890{
1891 int ret = 0;
1892 u32 address;
1893 int i, length, modex, modey, bpp;
1894
1895 modex = 640; modey = 480; bpp = 2;
1896
1897 address = sisusb->vrambase; /* Clear video ram */
1898
1899 if (clrall)
1900 length = sisusb->vramsize;
1901 else
1902 length = modex * bpp * modey;
1903
1904 ret = sisusb_clear_vram(sisusb, address, length);
1905
1906 if (!ret && drwfr) {
1907 for (i = 0; i < modex; i++) {
1908 address = sisusb->vrambase + (i * bpp);
1909 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1910 address, 0xf100);
1911 address += (modex * (modey-1) * bpp);
1912 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1913 address, 0xf100);
1914 }
1915 for (i = 0; i < modey; i++) {
1916 address = sisusb->vrambase + ((i * modex) * bpp);
1917 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1918 address, 0xf100);
1919 address += ((modex - 1) * bpp);
1920 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1921 address, 0xf100);
1922 }
1923 }
1924
1925 return ret;
1926}
1927
1928static int
1929sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
1930{
1931 int ret = 0, i, j, modex, modey, bpp, du;
1932 u8 sr31, cr63, tmp8;
1933 static const char attrdata[] = {
1934 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
1935 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
1936 0x01,0x00,0x00,0x00
1937 };
1938 static const char crtcrdata[] = {
1939 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
1940 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
1941 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
1942 0xff
1943 };
1944 static const char grcdata[] = {
1945 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
1946 0xff
1947 };
1948 static const char crtcdata[] = {
1949 0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e,
1950 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05,
1951 0x00
1952 };
1953
1954 modex = 640; modey = 480; bpp = 2;
1955
1956 GETIREG(SISSR, 0x31, &sr31);
1957 GETIREG(SISCR, 0x63, &cr63);
1958 SETIREGOR(SISSR, 0x01, 0x20);
1959 SETIREG(SISCR, 0x63, cr63 & 0xbf);
1960 SETIREGOR(SISCR, 0x17, 0x80);
1961 SETIREGOR(SISSR, 0x1f, 0x04);
1962 SETIREGAND(SISSR, 0x07, 0xfb);
1963 SETIREG(SISSR, 0x00, 0x03); /* seq */
1964 SETIREG(SISSR, 0x01, 0x21);
1965 SETIREG(SISSR, 0x02, 0x0f);
1966 SETIREG(SISSR, 0x03, 0x00);
1967 SETIREG(SISSR, 0x04, 0x0e);
1968 SETREG(SISMISCW, 0x23); /* misc */
1969 for (i = 0; i <= 0x18; i++) { /* crtc */
1970 SETIREG(SISCR, i, crtcrdata[i]);
1971 }
1972 for (i = 0; i <= 0x13; i++) { /* att */
1973 GETREG(SISINPSTAT, &tmp8);
1974 SETREG(SISAR, i);
1975 SETREG(SISAR, attrdata[i]);
1976 }
1977 GETREG(SISINPSTAT, &tmp8);
1978 SETREG(SISAR, 0x14);
1979 SETREG(SISAR, 0x00);
1980 GETREG(SISINPSTAT, &tmp8);
1981 SETREG(SISAR, 0x20);
1982 GETREG(SISINPSTAT, &tmp8);
1983 for (i = 0; i <= 0x08; i++) { /* grc */
1984 SETIREG(SISGR, i, grcdata[i]);
1985 }
1986 SETIREGAND(SISGR, 0x05, 0xbf);
1987 for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */
1988 SETIREG(SISSR, i, 0x00);
1989 }
1990 SETIREGAND(SISSR, 0x37, 0xfe);
1991 SETREG(SISMISCW, 0xef); /* sync */
1992 SETIREG(SISCR, 0x11, 0x00); /* crtc */
1993 for (j = 0x00, i = 0; i <= 7; i++, j++) {
1994 SETIREG(SISCR, j, crtcdata[i]);
1995 }
1996 for (j = 0x10; i <= 10; i++, j++) {
1997 SETIREG(SISCR, j, crtcdata[i]);
1998 }
1999 for (j = 0x15; i <= 12; i++, j++) {
2000 SETIREG(SISCR, j, crtcdata[i]);
2001 }
2002 for (j = 0x0A; i <= 15; i++, j++) {
2003 SETIREG(SISSR, j, crtcdata[i]);
2004 }
2005 SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
2006 SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
2007 SETIREG(SISCR, 0x14, 0x4f);
2008 du = (modex / 16) * (bpp * 2); /* offset/pitch */
2009 if (modex % 16) du += bpp;
2010 SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
2011 SETIREG(SISCR, 0x13, (du & 0xff));
2012 du <<= 5;
2013 tmp8 = du >> 8;
2014 if (du & 0xff) tmp8++;
2015 SETIREG(SISSR, 0x10, tmp8);
2016 SETIREG(SISSR, 0x31, 0x00); /* VCLK */
2017 SETIREG(SISSR, 0x2b, 0x1b);
2018 SETIREG(SISSR, 0x2c, 0xe1);
2019 SETIREG(SISSR, 0x2d, 0x01);
2020 SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */
2021 SETIREG(SISSR, 0x08, 0xae);
2022 SETIREGAND(SISSR, 0x09, 0xf0);
2023 SETIREG(SISSR, 0x08, 0x34);
2024 SETIREGOR(SISSR, 0x3d, 0x01);
2025 SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */
2026 SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
2027 SETIREG(SISCR, 0x19, 0x00);
2028 SETIREGAND(SISCR, 0x1a, 0xfc);
2029 SETIREGAND(SISSR, 0x0f, 0xb7);
2030 SETIREGAND(SISSR, 0x31, 0xfb);
2031 SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
2032 SETIREGAND(SISSR, 0x32, 0xf3);
2033 SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
2034 SETIREG(SISCR, 0x52, 0x6c);
2035
2036 SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */
2037 SETIREG(SISCR, 0x0c, 0x00);
2038 SETIREG(SISSR, 0x0d, 0x00);
2039 SETIREGAND(SISSR, 0x37, 0xfe);
2040
2041 SETIREG(SISCR, 0x32, 0x20);
2042 SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */
2043 SETIREG(SISCR, 0x63, (cr63 & 0xbf));
2044 SETIREG(SISSR, 0x31, (sr31 & 0xfb));
2045
2046 if (touchengines) {
2047 SETIREG(SISSR, 0x20, 0xa1); /* enable engines */
2048 SETIREGOR(SISSR, 0x1e, 0x5a);
2049
2050 SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */
2051 SETIREG(SISSR, 0x27, 0x1f);
2052 SETIREG(SISSR, 0x26, 0x00);
2053 }
2054
2055 SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */
2056
2057 return ret;
2058}
2059
2060static int
2061sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
2062{
2063 int ret = 0, i, j, bw, chab, iret, retry = 3;
2064 u8 tmp8, ramtype;
2065 u32 tmp32;
2066 static const char mclktable[] = {
2067 0x3b, 0x22, 0x01, 143,
2068 0x3b, 0x22, 0x01, 143,
2069 0x3b, 0x22, 0x01, 143,
2070 0x3b, 0x22, 0x01, 143
2071 };
2072 static const char eclktable[] = {
2073 0x3b, 0x22, 0x01, 143,
2074 0x3b, 0x22, 0x01, 143,
2075 0x3b, 0x22, 0x01, 143,
2076 0x3b, 0x22, 0x01, 143
2077 };
2078 static const char ramtypetable1[] = {
2079 0x00, 0x04, 0x60, 0x60,
2080 0x0f, 0x0f, 0x1f, 0x1f,
2081 0xba, 0xba, 0xba, 0xba,
2082 0xa9, 0xa9, 0xac, 0xac,
2083 0xa0, 0xa0, 0xa0, 0xa8,
2084 0x00, 0x00, 0x02, 0x02,
2085 0x30, 0x30, 0x40, 0x40
2086 };
2087 static const char ramtypetable2[] = {
2088 0x77, 0x77, 0x44, 0x44,
2089 0x77, 0x77, 0x44, 0x44,
2090 0x00, 0x00, 0x00, 0x00,
2091 0x5b, 0x5b, 0xab, 0xab,
2092 0x00, 0x00, 0xf0, 0xf8
2093 };
2094
2095 while (retry--) {
2096
2097 /* Enable VGA */
2098 ret = GETREG(SISVGAEN, &tmp8);
2099 ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
2100
2101 /* Enable GPU access to VRAM */
2102 ret |= GETREG(SISMISCR, &tmp8);
2103 ret |= SETREG(SISMISCW, (tmp8 | 0x01));
2104
2105 if (ret) continue;
2106
2107 /* Reset registers */
2108 ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
2109 ret |= SETIREG(SISSR, 0x05, 0x86);
2110 ret |= SETIREGOR(SISSR, 0x20, 0x01);
2111
2112 ret |= SETREG(SISMISCW, 0x67);
2113
2114 for (i = 0x06; i <= 0x1f; i++) {
2115 ret |= SETIREG(SISSR, i, 0x00);
2116 }
2117 for (i = 0x21; i <= 0x27; i++) {
2118 ret |= SETIREG(SISSR, i, 0x00);
2119 }
2120 for (i = 0x31; i <= 0x3d; i++) {
2121 ret |= SETIREG(SISSR, i, 0x00);
2122 }
2123 for (i = 0x12; i <= 0x1b; i++) {
2124 ret |= SETIREG(SISSR, i, 0x00);
2125 }
2126 for (i = 0x79; i <= 0x7c; i++) {
2127 ret |= SETIREG(SISCR, i, 0x00);
2128 }
2129
2130 if (ret) continue;
2131
2132 ret |= SETIREG(SISCR, 0x63, 0x80);
2133
2134 ret |= GETIREG(SISSR, 0x3a, &ramtype);
2135 ramtype &= 0x03;
2136
2137 ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
2138 ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
2139 ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
2140
2141 ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
2142 ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
2143 ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
2144
2145 ret |= SETIREG(SISSR, 0x07, 0x18);
2146 ret |= SETIREG(SISSR, 0x11, 0x0f);
2147
2148 if (ret) continue;
2149
2150 for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
2151 ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]);
2152 }
2153 for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
2154 ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]);
2155 }
2156
2157 ret |= SETIREG(SISCR, 0x49, 0xaa);
2158
2159 ret |= SETIREG(SISSR, 0x1f, 0x00);
2160 ret |= SETIREG(SISSR, 0x20, 0xa0);
2161 ret |= SETIREG(SISSR, 0x23, 0xf6);
2162 ret |= SETIREG(SISSR, 0x24, 0x0d);
2163 ret |= SETIREG(SISSR, 0x25, 0x33);
2164
2165 ret |= SETIREG(SISSR, 0x11, 0x0f);
2166
2167 ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
2168
2169 ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
2170
2171 if (ret) continue;
2172
2173 ret |= SETIREG(SISPART1, 0x00, 0x00);
2174
2175 ret |= GETIREG(SISSR, 0x13, &tmp8);
2176 tmp8 >>= 4;
2177
2178 ret |= SETIREG(SISPART1, 0x02, 0x00);
2179 ret |= SETIREG(SISPART1, 0x2e, 0x08);
2180
2181 ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
2182 tmp32 &= 0x00f00000;
2183 tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
2184 ret |= SETIREG(SISSR, 0x25, tmp8);
2185 tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
2186 ret |= SETIREG(SISCR, 0x49, tmp8);
2187
2188 ret |= SETIREG(SISSR, 0x27, 0x1f);
2189 ret |= SETIREG(SISSR, 0x31, 0x00);
2190 ret |= SETIREG(SISSR, 0x32, 0x11);
2191 ret |= SETIREG(SISSR, 0x33, 0x00);
2192
2193 if (ret) continue;
2194
2195 ret |= SETIREG(SISCR, 0x83, 0x00);
2196
2197 ret |= sisusb_set_default_mode(sisusb, 0);
2198
2199 ret |= SETIREGAND(SISSR, 0x21, 0xdf);
2200 ret |= SETIREGOR(SISSR, 0x01, 0x20);
2201 ret |= SETIREGOR(SISSR, 0x16, 0x0f);
2202
2203 ret |= sisusb_triggersr16(sisusb, ramtype);
2204
2205 /* Disable refresh */
2206 ret |= SETIREGAND(SISSR, 0x17, 0xf8);
2207 ret |= SETIREGOR(SISSR, 0x19, 0x03);
2208
2209 ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
2210 ret |= sisusb_verify_mclk(sisusb);
2211
2212 if (ramtype <= 1) {
2213 ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
2214 if (iret) {
2215 printk(KERN_ERR "sisusbvga[%d]: RAM size "
2216 "detection failed, "
2217 "assuming 8MB video RAM\n",
2218 sisusb->minor);
2219 ret |= SETIREG(SISSR,0x14,0x31);
2220 /* TODO */
2221 }
2222 } else {
2223 printk(KERN_ERR "sisusbvga[%d]: DDR RAM device found, "
2224 "assuming 8MB video RAM\n",
2225 sisusb->minor);
2226 ret |= SETIREG(SISSR,0x14,0x31);
2227 /* *** TODO *** */
2228 }
2229
2230 /* Enable refresh */
2231 ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
2232 ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
2233 ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
2234
2235 ret |= SETIREGOR(SISSR, 0x21, 0x20);
2236
2237 ret |= SETIREG(SISSR, 0x22, 0xfb);
2238 ret |= SETIREG(SISSR, 0x21, 0xa5);
2239
2240 if (ret == 0)
2241 break;
2242 }
2243
2244 return ret;
2245}
2246
2247#undef SETREG
2248#undef GETREG
2249#undef SETIREG
2250#undef GETIREG
2251#undef SETIREGOR
2252#undef SETIREGAND
2253#undef SETIREGANDOR
2254#undef READL
2255#undef WRITEL
2256
2257static void
2258sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
2259{
2260 u8 tmp8, tmp82, ramtype;
2261 int bw = 0;
2262 char *ramtypetext1 = NULL;
2263 const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM",
2264 "DDR SDRAM", "DDR SGRAM" };
2265 static const int busSDR[4] = {64, 64, 128, 128};
2266 static const int busDDR[4] = {32, 32, 64, 64};
2267 static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
2268
2269 sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
2270 sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
2271 sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
2272 sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
2273 ramtype &= 0x03;
2274 switch ((tmp8 >> 2) & 0x03) {
2275 case 0: ramtypetext1 = "1 ch/1 r";
2276 if (tmp82 & 0x10) {
2277 bw = 32;
2278 } else {
2279 bw = busSDR[(tmp8 & 0x03)];
2280 }
2281 break;
2282 case 1: ramtypetext1 = "1 ch/2 r";
2283 sisusb->vramsize <<= 1;
2284 bw = busSDR[(tmp8 & 0x03)];
2285 break;
2286 case 2: ramtypetext1 = "asymmeric";
2287 sisusb->vramsize += sisusb->vramsize/2;
2288 bw = busDDRA[(tmp8 & 0x03)];
2289 break;
2290 case 3: ramtypetext1 = "2 channel";
2291 sisusb->vramsize <<= 1;
2292 bw = busDDR[(tmp8 & 0x03)];
2293 break;
2294 }
2295
2296 printk(KERN_INFO "sisusbvga[%d]: %dMB %s %s, bus width %d\n",
2297 sisusb->minor, (sisusb->vramsize >> 20), ramtypetext1,
2298 ramtypetext2[ramtype], bw);
2299}
2300
2301static int
2302sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
2303{
2304 struct sisusb_packet packet;
2305 int ret;
2306 u32 tmp32;
2307
2308 /* Do some magic */
2309 packet.header = 0x001f;
2310 packet.address = 0x00000324;
2311 packet.data = 0x00000004;
2312 ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2313
2314 packet.header = 0x001f;
2315 packet.address = 0x00000364;
2316 packet.data = 0x00000004;
2317 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2318
2319 packet.header = 0x001f;
2320 packet.address = 0x00000384;
2321 packet.data = 0x00000004;
2322 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2323
2324 packet.header = 0x001f;
2325 packet.address = 0x00000100;
2326 packet.data = 0x00000700;
2327 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2328
2329 packet.header = 0x000f;
2330 packet.address = 0x00000004;
2331 ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
2332 packet.data |= 0x17;
2333 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2334
2335 /* Init BAR 0 (VRAM) */
2336 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2337 ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
2338 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2339 tmp32 &= 0x0f;
2340 tmp32 |= SISUSB_PCI_MEMBASE;
2341 ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
2342
2343 /* Init BAR 1 (MMIO) */
2344 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2345 ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
2346 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2347 tmp32 &= 0x0f;
2348 tmp32 |= SISUSB_PCI_MMIOBASE;
2349 ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
2350
2351 /* Init BAR 2 (i/o ports) */
2352 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2353 ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
2354 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2355 tmp32 &= 0x0f;
2356 tmp32 |= SISUSB_PCI_IOPORTBASE;
2357 ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
2358
2359 /* Enable memory and i/o access */
2360 ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
2361 tmp32 |= 0x3;
2362 ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
2363
2364 if (ret == 0) {
2365 /* Some further magic */
2366 packet.header = 0x001f;
2367 packet.address = 0x00000050;
2368 packet.data = 0x000000ff;
2369 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2370 }
2371
2372 return ret;
2373}
2374
2375/* Initialize the graphics device (return 0 on success)
2376 * This initializes the net2280 as well as the PCI registers
2377 * of the graphics board.
2378 */
2379
2380static int
2381sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
2382{
2383 int ret = 0, test = 0;
2384 u32 tmp32;
2385
2386 if (sisusb->devinit == 1) {
2387 /* Read PCI BARs and see if they have been set up */
2388 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2389 if (ret) return ret;
2390 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++;
2391
2392 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2393 if (ret) return ret;
2394 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++;
2395
2396 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2397 if (ret) return ret;
2398 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++;
2399 }
2400
2401 /* No? So reset the device */
2402 if ((sisusb->devinit == 0) || (test != 3)) {
2403
2404 ret |= sisusb_do_init_gfxdevice(sisusb);
2405
2406 if (ret == 0)
2407 sisusb->devinit = 1;
2408
2409 }
2410
2411 if (sisusb->devinit) {
2412 /* Initialize the graphics core */
2413 if (sisusb_init_gfxcore(sisusb) == 0) {
2414 sisusb->gfxinit = 1;
2415 sisusb_get_ramconfig(sisusb);
2416 ret |= sisusb_set_default_mode(sisusb, 1);
2417 ret |= sisusb_setup_screen(sisusb, 1, initscreen);
2418 }
2419 }
2420
2421 return ret;
2422}
2423
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002424
2425#ifdef INCL_SISUSB_CON
2426
2427/* Set up default text mode:
2428 - Set text mode (0x03)
2429 - Upload default font
2430 - Upload user font (if available)
2431*/
2432
2433int
2434sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
2435{
2436 int ret = 0, slot = sisusb->font_slot, i;
Andrew Mortondabb5922005-09-22 23:55:22 -07002437 const struct font_desc *myfont;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002438 u8 *tempbuf;
2439 u16 *tempbufb;
2440 size_t written;
Arjan van de Ven4c4c9432005-11-29 09:43:42 +01002441 static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
2442 static const char bootlogo[] = "(o_ //\\ V_/_";
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002443
2444 /* sisusb->lock is down */
2445
2446 if (!sisusb->SiS_Pr)
2447 return 1;
2448
2449 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2450 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2451
2452 /* Set mode 0x03 */
2453 SiSUSBSetMode(sisusb->SiS_Pr, 0x03);
2454
2455 if (!(myfont = find_font("VGA8x16")))
2456 return 1;
2457
2458 if (!(tempbuf = vmalloc(8192)))
2459 return 1;
2460
2461 for (i = 0; i < 256; i++)
2462 memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16);
2463
2464 /* Upload default font */
2465 ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, 0, 1, NULL, 16, 0);
2466
2467 vfree(tempbuf);
2468
2469 /* Upload user font (and reset current slot) */
2470 if (sisusb->font_backup) {
2471 ret |= sisusbcon_do_font_op(sisusb, 1, 2, sisusb->font_backup,
2472 8192, sisusb->font_backup_512, 1, NULL,
2473 sisusb->font_backup_height, 0);
2474 if (slot != 2)
2475 sisusbcon_do_font_op(sisusb, 1, 0, NULL, 0, 0, 1,
2476 NULL, 16, 0);
2477 }
2478
2479 if (init && !sisusb->scrbuf) {
2480
2481 if ((tempbuf = vmalloc(8192))) {
2482
2483 i = 4096;
2484 tempbufb = (u16 *)tempbuf;
2485 while (i--)
2486 *(tempbufb++) = 0x0720;
2487
2488 i = 0;
2489 tempbufb = (u16 *)tempbuf;
2490 while (bootlogo[i]) {
2491 *(tempbufb++) = 0x0700 | bootlogo[i++];
2492 if (!(i % 4))
2493 tempbufb += 76;
2494 }
2495
2496 i = 0;
2497 tempbufb = (u16 *)tempbuf + 6;
2498 while (bootstring[i])
2499 *(tempbufb++) = 0x0700 | bootstring[i++];
2500
2501 ret |= sisusb_copy_memory(sisusb, tempbuf,
2502 sisusb->vrambase, 8192, &written);
2503
2504 vfree(tempbuf);
2505
2506 }
2507
2508 } else if (sisusb->scrbuf) {
2509
2510 ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf,
2511 sisusb->vrambase, sisusb->scrbuf_size, &written);
2512
2513 }
2514
2515 if (sisusb->sisusb_cursor_size_from >= 0 &&
2516 sisusb->sisusb_cursor_size_to >= 0) {
2517 sisusb_setidxreg(sisusb, SISCR, 0x0a,
2518 sisusb->sisusb_cursor_size_from);
2519 sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0,
2520 sisusb->sisusb_cursor_size_to);
2521 } else {
2522 sisusb_setidxreg(sisusb, SISCR, 0x0a, 0x2d);
2523 sisusb_setidxreg(sisusb, SISCR, 0x0b, 0x0e);
2524 sisusb->sisusb_cursor_size_to = -1;
2525 }
2526
2527 slot = sisusb->sisusb_cursor_loc;
2528 if(slot < 0) slot = 0;
2529
2530 sisusb->sisusb_cursor_loc = -1;
2531 sisusb->bad_cursor_pos = 1;
2532
2533 sisusb_set_cursor(sisusb, slot);
2534
2535 sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
2536 sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
2537
2538 sisusb->textmodedestroyed = 0;
2539
2540 /* sisusb->lock is down */
2541
2542 return ret;
2543}
2544
2545#endif
2546
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547/* fops */
2548
2549static int
2550sisusb_open(struct inode *inode, struct file *file)
2551{
2552 struct sisusb_usb_data *sisusb;
2553 struct usb_interface *interface;
2554 int subminor = iminor(inode);
2555
Arjan van de Ven2682d272006-03-28 01:00:21 -08002556 mutex_lock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557
2558 if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
2559 printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
2560 subminor);
Arjan van de Ven2682d272006-03-28 01:00:21 -08002561 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 return -ENODEV;
2563 }
2564
2565 if (!(sisusb = usb_get_intfdata(interface))) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002566 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 return -ENODEV;
2568 }
2569
Arjan van de Ven2682d272006-03-28 01:00:21 -08002570 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571
2572 if (!sisusb->present || !sisusb->ready) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002573 mutex_unlock(&sisusb->lock);
2574 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 return -ENODEV;
2576 }
2577
2578 if (sisusb->isopen) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002579 mutex_unlock(&sisusb->lock);
2580 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 return -EBUSY;
2582 }
2583
2584 if (!sisusb->devinit) {
2585 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
2586 if (sisusb_init_gfxdevice(sisusb, 0)) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002587 mutex_unlock(&sisusb->lock);
2588 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 printk(KERN_ERR
2590 "sisusbvga[%d]: Failed to initialize "
2591 "device\n",
2592 sisusb->minor);
2593 return -EIO;
2594 }
2595 } else {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002596 mutex_unlock(&sisusb->lock);
2597 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598 printk(KERN_ERR
2599 "sisusbvga[%d]: Device not attached to "
2600 "USB 2.0 hub\n",
2601 sisusb->minor);
2602 return -EIO;
2603 }
2604 }
2605
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002606 /* Increment usage count for our sisusb */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607 kref_get(&sisusb->kref);
2608
2609 sisusb->isopen = 1;
2610
2611 file->private_data = sisusb;
2612
Arjan van de Ven2682d272006-03-28 01:00:21 -08002613 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614
Arjan van de Ven2682d272006-03-28 01:00:21 -08002615 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617 return 0;
2618}
2619
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002620void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621sisusb_delete(struct kref *kref)
2622{
2623 struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
2624
2625 if (!sisusb)
2626 return;
2627
2628 if (sisusb->sisusb_dev)
2629 usb_put_dev(sisusb->sisusb_dev);
2630
2631 sisusb->sisusb_dev = NULL;
2632 sisusb_free_buffers(sisusb);
2633 sisusb_free_urbs(sisusb);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002634#ifdef INCL_SISUSB_CON
2635 kfree(sisusb->SiS_Pr);
2636#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 kfree(sisusb);
2638}
2639
2640static int
2641sisusb_release(struct inode *inode, struct file *file)
2642{
2643 struct sisusb_usb_data *sisusb;
2644 int myminor;
2645
Arjan van de Ven2682d272006-03-28 01:00:21 -08002646 mutex_lock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647
2648 if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002649 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 return -ENODEV;
2651 }
2652
Arjan van de Ven2682d272006-03-28 01:00:21 -08002653 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654
2655 if (sisusb->present) {
2656 /* Wait for all URBs to finish if device still present */
2657 if (!sisusb_wait_all_out_complete(sisusb))
2658 sisusb_kill_all_busy(sisusb);
2659 }
2660
2661 myminor = sisusb->minor;
2662
2663 sisusb->isopen = 0;
2664 file->private_data = NULL;
2665
Arjan van de Ven2682d272006-03-28 01:00:21 -08002666 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
2668 /* decrement the usage count on our device */
2669 kref_put(&sisusb->kref, sisusb_delete);
2670
Arjan van de Ven2682d272006-03-28 01:00:21 -08002671 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 return 0;
2674}
2675
2676static ssize_t
2677sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
2678{
2679 struct sisusb_usb_data *sisusb;
2680 ssize_t bytes_read = 0;
2681 int errno = 0;
2682 u8 buf8;
2683 u16 buf16;
2684 u32 buf32, address;
2685
2686 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2687 return -ENODEV;
2688
Arjan van de Ven2682d272006-03-28 01:00:21 -08002689 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 /* Sanity check */
2692 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002693 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 return -ENODEV;
2695 }
2696
2697 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2698 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2699
2700 address = (*ppos) -
2701 SISUSB_PCI_PSEUDO_IOPORTBASE +
2702 SISUSB_PCI_IOPORTBASE;
2703
2704 /* Read i/o ports
2705 * Byte, word and long(32) can be read. As this
2706 * emulates inX instructions, the data returned is
2707 * in machine-endianness.
2708 */
2709 switch (count) {
2710
2711 case 1:
2712 if (sisusb_read_memio_byte(sisusb,
2713 SISUSB_TYPE_IO,
2714 address, &buf8))
2715 errno = -EIO;
2716 else if (put_user(buf8, (u8 __user *)buffer))
2717 errno = -EFAULT;
2718 else
2719 bytes_read = 1;
2720
2721 break;
2722
2723 case 2:
2724 if (sisusb_read_memio_word(sisusb,
2725 SISUSB_TYPE_IO,
2726 address, &buf16))
2727 errno = -EIO;
2728 else if (put_user(buf16, (u16 __user *)buffer))
2729 errno = -EFAULT;
2730 else
2731 bytes_read = 2;
2732
2733 break;
2734
2735 case 4:
2736 if (sisusb_read_memio_long(sisusb,
2737 SISUSB_TYPE_IO,
2738 address, &buf32))
2739 errno = -EIO;
2740 else if (put_user(buf32, (u32 __user *)buffer))
2741 errno = -EFAULT;
2742 else
2743 bytes_read = 4;
2744
2745 break;
2746
2747 default:
2748 errno = -EIO;
2749
2750 }
2751
2752 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2753 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2754
2755 address = (*ppos) -
2756 SISUSB_PCI_PSEUDO_MEMBASE +
2757 SISUSB_PCI_MEMBASE;
2758
2759 /* Read video ram
2760 * Remember: Data delivered is never endian-corrected
2761 */
2762 errno = sisusb_read_mem_bulk(sisusb, address,
2763 NULL, count, buffer, &bytes_read);
2764
2765 if (bytes_read)
2766 errno = bytes_read;
2767
2768 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2769 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2770
2771 address = (*ppos) -
2772 SISUSB_PCI_PSEUDO_MMIOBASE +
2773 SISUSB_PCI_MMIOBASE;
2774
2775 /* Read MMIO
2776 * Remember: Data delivered is never endian-corrected
2777 */
2778 errno = sisusb_read_mem_bulk(sisusb, address,
2779 NULL, count, buffer, &bytes_read);
2780
2781 if (bytes_read)
2782 errno = bytes_read;
2783
2784 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2785 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
2786
2787 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002788 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 return -EINVAL;
2790 }
2791
2792 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2793
2794 /* Read PCI config register
2795 * Return value delivered in machine endianness.
2796 */
2797 if (sisusb_read_pci_config(sisusb, address, &buf32))
2798 errno = -EIO;
2799 else if (put_user(buf32, (u32 __user *)buffer))
2800 errno = -EFAULT;
2801 else
2802 bytes_read = 4;
2803
2804 } else {
2805
2806 errno = -EBADFD;
2807
2808 }
2809
2810 (*ppos) += bytes_read;
2811
Arjan van de Ven2682d272006-03-28 01:00:21 -08002812 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813
2814 return errno ? errno : bytes_read;
2815}
2816
2817static ssize_t
2818sisusb_write(struct file *file, const char __user *buffer, size_t count,
2819 loff_t *ppos)
2820{
2821 struct sisusb_usb_data *sisusb;
2822 int errno = 0;
2823 ssize_t bytes_written = 0;
2824 u8 buf8;
2825 u16 buf16;
2826 u32 buf32, address;
2827
2828 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2829 return -ENODEV;
2830
Arjan van de Ven2682d272006-03-28 01:00:21 -08002831 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832
2833 /* Sanity check */
2834 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002835 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 return -ENODEV;
2837 }
2838
2839 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2840 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2841
2842 address = (*ppos) -
2843 SISUSB_PCI_PSEUDO_IOPORTBASE +
2844 SISUSB_PCI_IOPORTBASE;
2845
2846 /* Write i/o ports
2847 * Byte, word and long(32) can be written. As this
2848 * emulates outX instructions, the data is expected
2849 * in machine-endianness.
2850 */
2851 switch (count) {
2852
2853 case 1:
2854 if (get_user(buf8, (u8 __user *)buffer))
2855 errno = -EFAULT;
2856 else if (sisusb_write_memio_byte(sisusb,
2857 SISUSB_TYPE_IO,
2858 address, buf8))
2859 errno = -EIO;
2860 else
2861 bytes_written = 1;
2862
2863 break;
2864
2865 case 2:
2866 if (get_user(buf16, (u16 __user *)buffer))
2867 errno = -EFAULT;
2868 else if (sisusb_write_memio_word(sisusb,
2869 SISUSB_TYPE_IO,
2870 address, buf16))
2871 errno = -EIO;
2872 else
2873 bytes_written = 2;
2874
2875 break;
2876
2877 case 4:
2878 if (get_user(buf32, (u32 __user *)buffer))
2879 errno = -EFAULT;
2880 else if (sisusb_write_memio_long(sisusb,
2881 SISUSB_TYPE_IO,
2882 address, buf32))
2883 errno = -EIO;
2884 else
2885 bytes_written = 4;
2886
2887 break;
2888
2889 default:
2890 errno = -EIO;
2891 }
2892
2893 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2894 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2895
2896 address = (*ppos) -
2897 SISUSB_PCI_PSEUDO_MEMBASE +
2898 SISUSB_PCI_MEMBASE;
2899
2900 /* Write video ram.
2901 * Buffer is copied 1:1, therefore, on big-endian
2902 * machines, the data must be swapped by userland
2903 * in advance (if applicable; no swapping in 8bpp
2904 * mode or if YUV data is being transferred).
2905 */
2906 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2907 count, buffer, 0, &bytes_written);
2908
2909 if (bytes_written)
2910 errno = bytes_written;
2911
2912 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2913 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2914
2915 address = (*ppos) -
2916 SISUSB_PCI_PSEUDO_MMIOBASE +
2917 SISUSB_PCI_MMIOBASE;
2918
2919 /* Write MMIO.
2920 * Buffer is copied 1:1, therefore, on big-endian
2921 * machines, the data must be swapped by userland
2922 * in advance.
2923 */
2924 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2925 count, buffer, 0, &bytes_written);
2926
2927 if (bytes_written)
2928 errno = bytes_written;
2929
2930 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2931 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
2932
2933 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002934 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935 return -EINVAL;
2936 }
2937
2938 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2939
2940 /* Write PCI config register.
2941 * Given value expected in machine endianness.
2942 */
2943 if (get_user(buf32, (u32 __user *)buffer))
2944 errno = -EFAULT;
2945 else if (sisusb_write_pci_config(sisusb, address, buf32))
2946 errno = -EIO;
2947 else
2948 bytes_written = 4;
2949
2950
2951 } else {
2952
2953 /* Error */
2954 errno = -EBADFD;
2955
2956 }
2957
2958 (*ppos) += bytes_written;
2959
Arjan van de Ven2682d272006-03-28 01:00:21 -08002960 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961
2962 return errno ? errno : bytes_written;
2963}
2964
2965static loff_t
2966sisusb_lseek(struct file *file, loff_t offset, int orig)
2967{
2968 struct sisusb_usb_data *sisusb;
2969 loff_t ret;
2970
2971 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2972 return -ENODEV;
2973
Arjan van de Ven2682d272006-03-28 01:00:21 -08002974 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975
2976 /* Sanity check */
2977 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002978 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 return -ENODEV;
2980 }
2981
2982 switch (orig) {
2983 case 0:
2984 file->f_pos = offset;
2985 ret = file->f_pos;
2986 /* never negative, no force_successful_syscall needed */
2987 break;
2988 case 1:
2989 file->f_pos += offset;
2990 ret = file->f_pos;
2991 /* never negative, no force_successful_syscall needed */
2992 break;
2993 default:
2994 /* seeking relative to "end of file" is not supported */
2995 ret = -EINVAL;
2996 }
2997
Arjan van de Ven2682d272006-03-28 01:00:21 -08002998 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999 return ret;
3000}
3001
3002static int
3003sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
3004 unsigned long arg)
3005{
3006 int retval, port, length;
3007 u32 address;
3008
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003009 /* All our commands require the device
3010 * to be initialized.
3011 */
3012 if (!sisusb->devinit)
3013 return -ENODEV;
3014
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015 port = y->data3 -
3016 SISUSB_PCI_PSEUDO_IOPORTBASE +
3017 SISUSB_PCI_IOPORTBASE;
3018
3019 switch (y->operation) {
3020 case SUCMD_GET:
3021 retval = sisusb_getidxreg(sisusb, port,
3022 y->data0, &y->data1);
3023 if (!retval) {
3024 if (copy_to_user((void __user *)arg, y,
3025 sizeof(*y)))
3026 retval = -EFAULT;
3027 }
3028 break;
3029
3030 case SUCMD_SET:
3031 retval = sisusb_setidxreg(sisusb, port,
3032 y->data0, y->data1);
3033 break;
3034
3035 case SUCMD_SETOR:
3036 retval = sisusb_setidxregor(sisusb, port,
3037 y->data0, y->data1);
3038 break;
3039
3040 case SUCMD_SETAND:
3041 retval = sisusb_setidxregand(sisusb, port,
3042 y->data0, y->data1);
3043 break;
3044
3045 case SUCMD_SETANDOR:
3046 retval = sisusb_setidxregandor(sisusb, port,
3047 y->data0, y->data1, y->data2);
3048 break;
3049
3050 case SUCMD_SETMASK:
3051 retval = sisusb_setidxregmask(sisusb, port,
3052 y->data0, y->data1, y->data2);
3053 break;
3054
3055 case SUCMD_CLRSCR:
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003056 /* Gfx core must be initialized */
3057 if (!sisusb->gfxinit)
3058 return -ENODEV;
3059
Linus Torvalds1da177e2005-04-16 15:20:36 -07003060 length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
3061 address = y->data3 -
3062 SISUSB_PCI_PSEUDO_MEMBASE +
3063 SISUSB_PCI_MEMBASE;
3064 retval = sisusb_clear_vram(sisusb, address, length);
3065 break;
3066
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003067 case SUCMD_HANDLETEXTMODE:
3068 retval = 0;
3069#ifdef INCL_SISUSB_CON
3070 /* Gfx core must be initialized, SiS_Pr must exist */
3071 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
3072 return -ENODEV;
3073
3074 switch (y->data0) {
3075 case 0:
3076 retval = sisusb_reset_text_mode(sisusb, 0);
3077 break;
3078 case 1:
3079 sisusb->textmodedestroyed = 1;
3080 break;
3081 }
3082#endif
3083 break;
3084
3085#ifdef INCL_SISUSB_CON
3086 case SUCMD_SETMODE:
3087 /* Gfx core must be initialized, SiS_Pr must exist */
3088 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
3089 return -ENODEV;
3090
3091 retval = 0;
3092
3093 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
3094 sisusb->SiS_Pr->sisusb = (void *)sisusb;
3095
3096 if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3))
3097 retval = -EINVAL;
3098
3099 break;
3100
3101 case SUCMD_SETVESAMODE:
3102 /* Gfx core must be initialized, SiS_Pr must exist */
3103 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
3104 return -ENODEV;
3105
3106 retval = 0;
3107
3108 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
3109 sisusb->SiS_Pr->sisusb = (void *)sisusb;
3110
3111 if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3))
3112 retval = -EINVAL;
3113
3114 break;
3115#endif
3116
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117 default:
3118 retval = -EINVAL;
3119 }
3120
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003121 if (retval > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122 retval = -EIO;
3123
3124 return retval;
3125}
3126
3127static int
3128sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
3129 unsigned long arg)
3130{
3131 struct sisusb_usb_data *sisusb;
3132 struct sisusb_info x;
3133 struct sisusb_command y;
3134 int retval = 0;
3135 u32 __user *argp = (u32 __user *)arg;
3136
3137 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
3138 return -ENODEV;
3139
Arjan van de Ven2682d272006-03-28 01:00:21 -08003140 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141
3142 /* Sanity check */
3143 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
3144 retval = -ENODEV;
3145 goto err_out;
3146 }
3147
3148 switch (cmd) {
3149
3150 case SISUSB_GET_CONFIG_SIZE:
3151
3152 if (put_user(sizeof(x), argp))
3153 retval = -EFAULT;
3154
3155 break;
3156
3157 case SISUSB_GET_CONFIG:
3158
3159 x.sisusb_id = SISUSB_ID;
3160 x.sisusb_version = SISUSB_VERSION;
3161 x.sisusb_revision = SISUSB_REVISION;
3162 x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
3163 x.sisusb_gfxinit = sisusb->gfxinit;
3164 x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE;
3165 x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE;
3166 x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE;
3167 x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE;
3168 x.sisusb_vramsize = sisusb->vramsize;
3169 x.sisusb_minor = sisusb->minor;
3170 x.sisusb_fbdevactive= 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003171#ifdef INCL_SISUSB_CON
3172 x.sisusb_conactive = sisusb->haveconsole ? 1 : 0;
3173#else
3174 x.sisusb_conactive = 0;
3175#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176
3177 if (copy_to_user((void __user *)arg, &x, sizeof(x)))
3178 retval = -EFAULT;
3179
3180 break;
3181
3182 case SISUSB_COMMAND:
3183
3184 if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
3185 retval = -EFAULT;
3186 else
3187 retval = sisusb_handle_command(sisusb, &y, arg);
3188
3189 break;
3190
3191 default:
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003192 retval = -ENOTTY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193 break;
3194 }
3195
3196err_out:
Arjan van de Ven2682d272006-03-28 01:00:21 -08003197 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198 return retval;
3199}
3200
3201#ifdef SISUSB_NEW_CONFIG_COMPAT
3202static long
3203sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
3204{
3205 long retval;
3206
3207 switch (cmd) {
3208 case SISUSB_GET_CONFIG_SIZE:
3209 case SISUSB_GET_CONFIG:
3210 case SISUSB_COMMAND:
3211 lock_kernel();
3212 retval = sisusb_ioctl(f->f_dentry->d_inode, f, cmd, arg);
3213 unlock_kernel();
3214 return retval;
3215
3216 default:
3217 return -ENOIOCTLCMD;
3218 }
3219}
3220#endif
3221
3222static struct file_operations usb_sisusb_fops = {
3223 .owner = THIS_MODULE,
3224 .open = sisusb_open,
3225 .release = sisusb_release,
3226 .read = sisusb_read,
3227 .write = sisusb_write,
3228 .llseek = sisusb_lseek,
3229#ifdef SISUSB_NEW_CONFIG_COMPAT
3230 .compat_ioctl = sisusb_compat_ioctl,
3231#endif
3232 .ioctl = sisusb_ioctl
3233};
3234
3235static struct usb_class_driver usb_sisusb_class = {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003236 .name = "sisusbvga%d",
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003237 .fops = &usb_sisusb_fops,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 .minor_base = SISUSB_MINOR
3239};
3240
3241static int sisusb_probe(struct usb_interface *intf,
3242 const struct usb_device_id *id)
3243{
3244 struct usb_device *dev = interface_to_usbdev(intf);
3245 struct sisusb_usb_data *sisusb;
3246 int retval = 0, i;
3247 const char *memfail =
3248 KERN_ERR
3249 "sisusbvga[%d]: Failed to allocate memory for %s buffer\n";
3250
3251 printk(KERN_INFO "sisusb: USB2VGA dongle found at address %d\n",
3252 dev->devnum);
3253
3254 /* Allocate memory for our private */
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003255 if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003256 printk(KERN_ERR
3257 "sisusb: Failed to allocate memory for private data\n");
3258 return -ENOMEM;
3259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003260 kref_init(&sisusb->kref);
3261
Arjan van de Ven2682d272006-03-28 01:00:21 -08003262 mutex_init(&(sisusb->lock));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003263
3264 /* Register device */
3265 if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
3266 printk(KERN_ERR
3267 "sisusb: Failed to get a minor for device %d\n",
3268 dev->devnum);
3269 retval = -ENODEV;
3270 goto error_1;
3271 }
3272
3273 sisusb->sisusb_dev = dev;
3274 sisusb->minor = intf->minor;
3275 sisusb->vrambase = SISUSB_PCI_MEMBASE;
3276 sisusb->mmiobase = SISUSB_PCI_MMIOBASE;
3277 sisusb->mmiosize = SISUSB_PCI_MMIOSIZE;
3278 sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
3279 /* Everything else is zero */
3280
3281 /* Allocate buffers */
3282 sisusb->ibufsize = SISUSB_IBUF_SIZE;
3283 if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
3284 GFP_KERNEL, &sisusb->transfer_dma_in))) {
3285 printk(memfail, "input", sisusb->minor);
3286 retval = -ENOMEM;
3287 goto error_2;
3288 }
3289
3290 sisusb->numobufs = 0;
3291 sisusb->obufsize = SISUSB_OBUF_SIZE;
3292 for (i = 0; i < NUMOBUFS; i++) {
3293 if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE,
3294 GFP_KERNEL,
3295 &sisusb->transfer_dma_out[i]))) {
3296 if (i == 0) {
3297 printk(memfail, "output", sisusb->minor);
3298 retval = -ENOMEM;
3299 goto error_3;
3300 }
3301 break;
3302 } else
3303 sisusb->numobufs++;
3304
3305 }
3306
3307 /* Allocate URBs */
3308 if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
3309 printk(KERN_ERR
3310 "sisusbvga[%d]: Failed to allocate URBs\n",
3311 sisusb->minor);
3312 retval = -ENOMEM;
3313 goto error_3;
3314 }
3315 sisusb->completein = 1;
3316
3317 for (i = 0; i < sisusb->numobufs; i++) {
3318 if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
3319 printk(KERN_ERR
3320 "sisusbvga[%d]: Failed to allocate URBs\n",
3321 sisusb->minor);
3322 retval = -ENOMEM;
3323 goto error_4;
3324 }
3325 sisusb->urbout_context[i].sisusb = (void *)sisusb;
3326 sisusb->urbout_context[i].urbindex = i;
3327 sisusb->urbstatus[i] = 0;
3328 }
3329
3330 printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n",
3331 sisusb->minor, sisusb->numobufs);
3332
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003333#ifdef INCL_SISUSB_CON
3334 /* Allocate our SiS_Pr */
3335 if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
3336 printk(KERN_ERR
3337 "sisusbvga[%d]: Failed to allocate SiS_Pr\n",
3338 sisusb->minor);
3339 }
3340#endif
3341
Linus Torvalds1da177e2005-04-16 15:20:36 -07003342 /* Do remaining init stuff */
3343
3344 init_waitqueue_head(&sisusb->wait_q);
3345
3346 usb_set_intfdata(intf, sisusb);
3347
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003348 usb_get_dev(sisusb->sisusb_dev);
3349
3350 sisusb->present = 1;
3351
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352#ifdef SISUSB_OLD_CONFIG_COMPAT
3353 {
3354 int ret;
3355 /* Our ioctls are all "32/64bit compatible" */
3356 ret = register_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE, NULL);
3357 ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG, NULL);
3358 ret |= register_ioctl32_conversion(SISUSB_COMMAND, NULL);
3359 if (ret)
3360 printk(KERN_ERR
3361 "sisusbvga[%d]: Error registering ioctl32 "
3362 "translations\n",
3363 sisusb->minor);
3364 else
3365 sisusb->ioctl32registered = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003366 }
3367#endif
3368
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 if (dev->speed == USB_SPEED_HIGH) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003370 int initscreen = 1;
3371#ifdef INCL_SISUSB_CON
3372 if (sisusb_first_vc > 0 &&
3373 sisusb_last_vc > 0 &&
3374 sisusb_first_vc <= sisusb_last_vc &&
3375 sisusb_last_vc <= MAX_NR_CONSOLES)
3376 initscreen = 0;
3377#endif
3378 if (sisusb_init_gfxdevice(sisusb, initscreen))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003379 printk(KERN_ERR
3380 "sisusbvga[%d]: Failed to early "
3381 "initialize device\n",
3382 sisusb->minor);
3383
3384 } else
3385 printk(KERN_INFO
3386 "sisusbvga[%d]: Not attached to USB 2.0 hub, "
3387 "deferring init\n",
3388 sisusb->minor);
3389
3390 sisusb->ready = 1;
3391
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003392#ifdef SISUSBENDIANTEST
3393 printk(KERN_DEBUG "sisusb: *** RWTEST ***\n");
3394 sisusb_testreadwrite(sisusb);
3395 printk(KERN_DEBUG "sisusb: *** RWTEST END ***\n");
3396#endif
3397
3398#ifdef INCL_SISUSB_CON
3399 sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
3400#endif
3401
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 return 0;
3403
3404error_4:
3405 sisusb_free_urbs(sisusb);
3406error_3:
3407 sisusb_free_buffers(sisusb);
3408error_2:
3409 usb_deregister_dev(intf, &usb_sisusb_class);
3410error_1:
3411 kfree(sisusb);
3412 return retval;
3413}
3414
3415static void sisusb_disconnect(struct usb_interface *intf)
3416{
3417 struct sisusb_usb_data *sisusb;
3418 int minor;
3419
Linus Torvalds1da177e2005-04-16 15:20:36 -07003420 /* This should *not* happen */
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003421 if (!(sisusb = usb_get_intfdata(intf)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422 return;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003423
3424#ifdef INCL_SISUSB_CON
3425 sisusb_console_exit(sisusb);
3426#endif
3427
3428 /* The above code doesn't need the disconnect
3429 * semaphore to be down; its meaning is to
3430 * protect all other routines from the disconnect
3431 * case, not the other way round.
3432 */
Arjan van de Ven2682d272006-03-28 01:00:21 -08003433 mutex_lock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434
Arjan van de Ven2682d272006-03-28 01:00:21 -08003435 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003436
3437 /* Wait for all URBs to complete and kill them in case (MUST do) */
3438 if (!sisusb_wait_all_out_complete(sisusb))
3439 sisusb_kill_all_busy(sisusb);
3440
3441 minor = sisusb->minor;
3442
3443 usb_set_intfdata(intf, NULL);
3444
3445 usb_deregister_dev(intf, &usb_sisusb_class);
3446
3447#ifdef SISUSB_OLD_CONFIG_COMPAT
3448 if (sisusb->ioctl32registered) {
3449 int ret;
3450 sisusb->ioctl32registered = 0;
3451 ret = unregister_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE);
3452 ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG);
3453 ret |= unregister_ioctl32_conversion(SISUSB_COMMAND);
3454 if (ret) {
3455 printk(KERN_ERR
3456 "sisusbvga[%d]: Error unregistering "
3457 "ioctl32 translations\n",
3458 minor);
3459 }
3460 }
3461#endif
3462
3463 sisusb->present = 0;
3464 sisusb->ready = 0;
3465
Arjan van de Ven2682d272006-03-28 01:00:21 -08003466 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003467
3468 /* decrement our usage count */
3469 kref_put(&sisusb->kref, sisusb_delete);
3470
Arjan van de Ven2682d272006-03-28 01:00:21 -08003471 mutex_unlock(&disconnect_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003472
3473 printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
3474}
3475
3476static struct usb_device_id sisusb_table [] = {
3477 { USB_DEVICE(0x0711, 0x0900) },
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003478 { USB_DEVICE(0x182d, 0x021c) },
Thomas Winischhofercef11122005-04-22 15:06:59 -07003479 { USB_DEVICE(0x182d, 0x0269) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003480 { }
3481};
3482
3483MODULE_DEVICE_TABLE (usb, sisusb_table);
3484
3485static struct usb_driver sisusb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003486 .name = "sisusb",
3487 .probe = sisusb_probe,
3488 .disconnect = sisusb_disconnect,
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003489 .id_table = sisusb_table,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003490};
3491
3492static int __init usb_sisusb_init(void)
3493{
3494 int retval;
3495
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003496#ifdef INCL_SISUSB_CON
3497 sisusb_init_concode();
3498#endif
3499
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500 if (!(retval = usb_register(&sisusb_driver))) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003501
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502 printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n",
3503 SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL);
3504 printk(KERN_INFO
3505 "sisusb: Copyright (C) 2005 Thomas Winischhofer\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003506
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 }
3508
3509 return retval;
3510}
3511
3512static void __exit usb_sisusb_exit(void)
3513{
3514 usb_deregister(&sisusb_driver);
3515}
3516
3517module_init(usb_sisusb_init);
3518module_exit(usb_sisusb_exit);
3519
3520MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003521MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003522MODULE_LICENSE("GPL");
3523