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