blob: fbace41a7cba4418dc8602f70eae8614b0db33c7 [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 *
Felipe Balbied86d972007-08-10 09:34:24 -040035 * Author: Thomas Winischhofer <thomas@winischhofer.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 *
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static void
76sisusb_free_buffers(struct sisusb_usb_data *sisusb)
77{
78 int i;
79
80 for (i = 0; i < NUMOBUFS; i++) {
81 if (sisusb->obuf[i]) {
82 usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize,
83 sisusb->obuf[i], sisusb->transfer_dma_out[i]);
84 sisusb->obuf[i] = NULL;
85 }
86 }
87 if (sisusb->ibuf) {
88 usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize,
89 sisusb->ibuf, sisusb->transfer_dma_in);
90 sisusb->ibuf = NULL;
91 }
92}
93
94static void
95sisusb_free_urbs(struct sisusb_usb_data *sisusb)
96{
97 int i;
98
99 for (i = 0; i < NUMOBUFS; i++) {
100 usb_free_urb(sisusb->sisurbout[i]);
101 sisusb->sisurbout[i] = NULL;
102 }
103 usb_free_urb(sisusb->sisurbin);
104 sisusb->sisurbin = NULL;
105}
106
107/* Level 0: USB transport layer */
108
109/* 1. out-bulks */
110
111/* out-urb management */
112
113/* Return 1 if all free, 0 otherwise */
114static int
115sisusb_all_free(struct sisusb_usb_data *sisusb)
116{
117 int i;
118
119 for (i = 0; i < sisusb->numobufs; i++) {
120
121 if (sisusb->urbstatus[i] & SU_URB_BUSY)
122 return 0;
123
124 }
125
126 return 1;
127}
128
129/* Kill all busy URBs */
130static void
131sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
132{
133 int i;
134
135 if (sisusb_all_free(sisusb))
136 return;
137
138 for (i = 0; i < sisusb->numobufs; i++) {
139
140 if (sisusb->urbstatus[i] & SU_URB_BUSY)
141 usb_kill_urb(sisusb->sisurbout[i]);
142
143 }
144}
145
146/* Return 1 if ok, 0 if error (not all complete within timeout) */
147static int
148sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
149{
150 int timeout = 5 * HZ, i = 1;
151
152 wait_event_timeout(sisusb->wait_q,
153 (i = sisusb_all_free(sisusb)),
154 timeout);
155
156 return i;
157}
158
159static int
160sisusb_outurb_available(struct sisusb_usb_data *sisusb)
161{
162 int i;
163
164 for (i = 0; i < sisusb->numobufs; i++) {
165
166 if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
167 return i;
168
169 }
170
171 return -1;
172}
173
174static int
175sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
176{
177 int i, timeout = 5 * HZ;
178
179 wait_event_timeout(sisusb->wait_q,
180 ((i = sisusb_outurb_available(sisusb)) >= 0),
181 timeout);
182
183 return i;
184}
185
186static int
187sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
188{
189 int i;
190
191 i = sisusb_outurb_available(sisusb);
192
193 if (i >= 0)
194 sisusb->urbstatus[i] |= SU_URB_ALLOC;
195
196 return i;
197}
198
199static void
200sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
201{
202 if ((index >= 0) && (index < sisusb->numobufs))
203 sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
204}
205
206/* completion callback */
207
208static void
David Howells7d12e782006-10-05 14:55:46 +0100209sisusb_bulk_completeout(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
211 struct sisusb_urb_context *context = urb->context;
212 struct sisusb_usb_data *sisusb;
213
214 if (!context)
215 return;
216
217 sisusb = context->sisusb;
218
219 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
220 return;
221
222#ifndef SISUSB_DONTSYNC
223 if (context->actual_length)
224 *(context->actual_length) += urb->actual_length;
225#endif
226
227 sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
228 wake_up(&sisusb->wait_q);
229}
230
231static int
232sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
233 int len, int *actual_length, int timeout, unsigned int tflags,
234 dma_addr_t transfer_dma)
235{
236 struct urb *urb = sisusb->sisurbout[index];
237 int retval, byteswritten = 0;
238
239 /* Set up URB */
240 urb->transfer_flags = 0;
241
242 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
243 sisusb_bulk_completeout, &sisusb->urbout_context[index]);
244
Alan Sternb375a042005-07-29 16:11:07 -0400245 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 urb->actual_length = 0;
247
248 if ((urb->transfer_dma = transfer_dma))
249 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
250
251 /* Set up context */
252 sisusb->urbout_context[index].actual_length = (timeout) ?
253 NULL : actual_length;
254
255 /* Declare this urb/buffer in use */
256 sisusb->urbstatus[index] |= SU_URB_BUSY;
257
258 /* Submit URB */
259 retval = usb_submit_urb(urb, GFP_ATOMIC);
260
261 /* If OK, and if timeout > 0, wait for completion */
262 if ((retval == 0) && timeout) {
263 wait_event_timeout(sisusb->wait_q,
264 (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
265 timeout);
266 if (sisusb->urbstatus[index] & SU_URB_BUSY) {
267 /* URB timed out... kill it and report error */
268 usb_kill_urb(urb);
269 retval = -ETIMEDOUT;
270 } else {
271 /* Otherwise, report urb status */
272 retval = urb->status;
273 byteswritten = urb->actual_length;
274 }
275 }
276
277 if (actual_length)
278 *actual_length = byteswritten;
279
280 return retval;
281}
282
283/* 2. in-bulks */
284
285/* completion callback */
286
287static void
David Howells7d12e782006-10-05 14:55:46 +0100288sisusb_bulk_completein(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
290 struct sisusb_usb_data *sisusb = urb->context;
291
292 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
293 return;
294
295 sisusb->completein = 1;
296 wake_up(&sisusb->wait_q);
297}
298
299static int
300sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len,
301 int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma)
302{
303 struct urb *urb = sisusb->sisurbin;
304 int retval, readbytes = 0;
305
306 urb->transfer_flags = 0;
307
308 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
309 sisusb_bulk_completein, sisusb);
310
Alan Sternb375a042005-07-29 16:11:07 -0400311 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 urb->actual_length = 0;
313
314 if ((urb->transfer_dma = transfer_dma))
315 urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
316
317 sisusb->completein = 0;
318 retval = usb_submit_urb(urb, GFP_ATOMIC);
319 if (retval == 0) {
320 wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
321 if (!sisusb->completein) {
322 /* URB timed out... kill it and report error */
323 usb_kill_urb(urb);
324 retval = -ETIMEDOUT;
325 } else {
Joe Perchesdc0d5c12007-12-17 11:40:18 -0800326 /* URB completed within timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 retval = urb->status;
328 readbytes = urb->actual_length;
329 }
330 }
331
332 if (actual_length)
333 *actual_length = readbytes;
334
335 return retval;
336}
337
338
339/* Level 1: */
340
341/* Send a bulk message of variable size
342 *
343 * To copy the data from userspace, give pointer to "userbuffer",
344 * to copy from (non-DMA) kernel memory, give "kernbuffer". If
345 * both of these are NULL, it is assumed, that the transfer
346 * buffer "sisusb->obuf[index]" is set up with the data to send.
347 * Index is ignored if either kernbuffer or userbuffer is set.
348 * If async is nonzero, URBs will be sent without waiting for
349 * completion of the previous URB.
350 *
351 * (return 0 on success)
352 */
353
354static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
355 char *kernbuffer, const char __user *userbuffer, int index,
356 ssize_t *bytes_written, unsigned int tflags, int async)
357{
358 int result = 0, retry, count = len;
359 int passsize, thispass, transferred_len = 0;
360 int fromuser = (userbuffer != NULL) ? 1 : 0;
361 int fromkern = (kernbuffer != NULL) ? 1 : 0;
362 unsigned int pipe;
363 char *buffer;
364
365 (*bytes_written) = 0;
366
367 /* Sanity check */
368 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
369 return -ENODEV;
370
371 /* If we copy data from kernel or userspace, force the
372 * allocation of a buffer/urb. If we have the data in
373 * the transfer buffer[index] already, reuse the buffer/URB
374 * if the length is > buffer size. (So, transmitting
375 * large data amounts directly from the transfer buffer
376 * treats the buffer as a ring buffer. However, we need
377 * to sync in this case.)
378 */
379 if (fromuser || fromkern)
380 index = -1;
381 else if (len > sisusb->obufsize)
382 async = 0;
383
384 pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
385
386 do {
387 passsize = thispass = (sisusb->obufsize < count) ?
388 sisusb->obufsize : count;
389
390 if (index < 0)
391 index = sisusb_get_free_outbuf(sisusb);
392
393 if (index < 0)
394 return -EIO;
395
396 buffer = sisusb->obuf[index];
397
398 if (fromuser) {
399
400 if (copy_from_user(buffer, userbuffer, passsize))
401 return -EFAULT;
402
403 userbuffer += passsize;
404
405 } else if (fromkern) {
406
407 memcpy(buffer, kernbuffer, passsize);
408 kernbuffer += passsize;
409
410 }
411
412 retry = 5;
413 while (thispass) {
414
415 if (!sisusb->sisusb_dev)
416 return -ENODEV;
417
418 result = sisusb_bulkout_msg(sisusb,
419 index,
420 pipe,
421 buffer,
422 thispass,
423 &transferred_len,
424 async ? 0 : 5 * HZ,
425 tflags,
426 sisusb->transfer_dma_out[index]);
427
428 if (result == -ETIMEDOUT) {
429
430 /* Will not happen if async */
431 if (!retry--)
432 return -ETIME;
433
434 continue;
435
436 } else if ((result == 0) && !async && transferred_len) {
437
438 thispass -= transferred_len;
439 if (thispass) {
440 if (sisusb->transfer_dma_out) {
441 /* If DMA, copy remaining
442 * to beginning of buffer
443 */
444 memcpy(buffer,
445 buffer + transferred_len,
446 thispass);
447 } else {
448 /* If not DMA, simply increase
449 * the pointer
450 */
451 buffer += transferred_len;
452 }
453 }
454
455 } else
456 break;
457 };
458
459 if (result)
460 return result;
461
462 (*bytes_written) += passsize;
463 count -= passsize;
464
465 /* Force new allocation in next iteration */
466 if (fromuser || fromkern)
467 index = -1;
468
469 } while (count > 0);
470
471 if (async) {
472#ifdef SISUSB_DONTSYNC
473 (*bytes_written) = len;
474 /* Some URBs/buffers might be busy */
475#else
476 sisusb_wait_all_out_complete(sisusb);
477 (*bytes_written) = transferred_len;
478 /* All URBs and all buffers are available */
479#endif
480 }
481
482 return ((*bytes_written) == len) ? 0 : -EIO;
483}
484
485/* Receive a bulk message of variable size
486 *
487 * To copy the data to userspace, give pointer to "userbuffer",
488 * to copy to kernel memory, give "kernbuffer". One of them
489 * MUST be set. (There is no technique for letting the caller
490 * read directly from the ibuf.)
491 *
492 */
493
494static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
495 void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
496 unsigned int tflags)
497{
498 int result = 0, retry, count = len;
499 int bufsize, thispass, transferred_len;
500 unsigned int pipe;
501 char *buffer;
502
503 (*bytes_read) = 0;
504
505 /* Sanity check */
506 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
507 return -ENODEV;
508
509 pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
510 buffer = sisusb->ibuf;
511 bufsize = sisusb->ibufsize;
512
513 retry = 5;
514
515#ifdef SISUSB_DONTSYNC
516 if (!(sisusb_wait_all_out_complete(sisusb)))
517 return -EIO;
518#endif
519
520 while (count > 0) {
521
522 if (!sisusb->sisusb_dev)
523 return -ENODEV;
524
525 thispass = (bufsize < count) ? bufsize : count;
526
527 result = sisusb_bulkin_msg(sisusb,
528 pipe,
529 buffer,
530 thispass,
531 &transferred_len,
532 5 * HZ,
533 tflags,
534 sisusb->transfer_dma_in);
535
536 if (transferred_len)
537 thispass = transferred_len;
538
539 else if (result == -ETIMEDOUT) {
540
541 if (!retry--)
542 return -ETIME;
543
544 continue;
545
546 } else
547 return -EIO;
548
549
550 if (thispass) {
551
552 (*bytes_read) += thispass;
553 count -= thispass;
554
555 if (userbuffer) {
556
557 if (copy_to_user(userbuffer, buffer, thispass))
558 return -EFAULT;
559
560 userbuffer += thispass;
561
562 } else {
563
564 memcpy(kernbuffer, buffer, thispass);
565 kernbuffer += thispass;
566
567 }
568
569 }
570
571 }
572
573 return ((*bytes_read) == len) ? 0 : -EIO;
574}
575
576static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
577 struct sisusb_packet *packet)
578{
579 int ret;
580 ssize_t bytes_transferred = 0;
581 __le32 tmp;
582
583 if (len == 6)
584 packet->data = 0;
585
586#ifdef SISUSB_DONTSYNC
587 if (!(sisusb_wait_all_out_complete(sisusb)))
588 return 1;
589#endif
590
591 /* Eventually correct endianness */
592 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
593
594 /* 1. send the packet */
595 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
596 (char *)packet, NULL, 0, &bytes_transferred, 0, 0);
597
598 if ((ret == 0) && (len == 6)) {
599
600 /* 2. if packet len == 6, it means we read, so wait for 32bit
601 * return value and write it to packet->data
602 */
603 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
604 (char *)&tmp, NULL, &bytes_transferred, 0);
605
606 packet->data = le32_to_cpu(tmp);
607 }
608
609 return ret;
610}
611
612static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
613 struct sisusb_packet *packet,
614 unsigned int tflags)
615{
616 int ret;
617 ssize_t bytes_transferred = 0;
618 __le32 tmp;
619
620 if (len == 6)
621 packet->data = 0;
622
623#ifdef SISUSB_DONTSYNC
624 if (!(sisusb_wait_all_out_complete(sisusb)))
625 return 1;
626#endif
627
628 /* Eventually correct endianness */
629 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
630
631 /* 1. send the packet */
632 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
633 (char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
634
635 if ((ret == 0) && (len == 6)) {
636
637 /* 2. if packet len == 6, it means we read, so wait for 32bit
638 * return value and write it to packet->data
639 */
640 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
641 (char *)&tmp, NULL, &bytes_transferred, 0);
642
643 packet->data = le32_to_cpu(tmp);
644 }
645
646 return ret;
647}
648
649/* access video memory and mmio (return 0 on success) */
650
651/* Low level */
652
653/* The following routines assume being used to transfer byte, word,
654 * long etc.
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200655 * This means that
656 * - the write routines expect "data" in machine endianness format.
657 * The data will be converted to leXX in sisusb_xxx_packet.
658 * - the read routines can expect read data in machine-endianess.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 */
660
661static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
662 u32 addr, u8 data)
663{
664 struct sisusb_packet packet;
665 int ret;
666
667 packet.header = (1 << (addr & 3)) | (type << 6);
668 packet.address = addr & ~3;
669 packet.data = data << ((addr & 3) << 3);
670 ret = sisusb_send_packet(sisusb, 10, &packet);
671 return ret;
672}
673
674static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
675 u32 addr, u16 data)
676{
677 struct sisusb_packet packet;
678 int ret = 0;
679
680 packet.address = addr & ~3;
681
682 switch (addr & 3) {
683 case 0:
684 packet.header = (type << 6) | 0x0003;
685 packet.data = (u32)data;
686 ret = sisusb_send_packet(sisusb, 10, &packet);
687 break;
688 case 1:
689 packet.header = (type << 6) | 0x0006;
690 packet.data = (u32)data << 8;
691 ret = sisusb_send_packet(sisusb, 10, &packet);
692 break;
693 case 2:
694 packet.header = (type << 6) | 0x000c;
695 packet.data = (u32)data << 16;
696 ret = sisusb_send_packet(sisusb, 10, &packet);
697 break;
698 case 3:
699 packet.header = (type << 6) | 0x0008;
700 packet.data = (u32)data << 24;
701 ret = sisusb_send_packet(sisusb, 10, &packet);
702 packet.header = (type << 6) | 0x0001;
703 packet.address = (addr & ~3) + 4;
704 packet.data = (u32)data >> 8;
705 ret |= sisusb_send_packet(sisusb, 10, &packet);
706 }
707
708 return ret;
709}
710
711static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
712 u32 addr, u32 data)
713{
714 struct sisusb_packet packet;
715 int ret = 0;
716
717 packet.address = addr & ~3;
718
719 switch (addr & 3) {
720 case 0:
721 packet.header = (type << 6) | 0x0007;
722 packet.data = data & 0x00ffffff;
723 ret = sisusb_send_packet(sisusb, 10, &packet);
724 break;
725 case 1:
726 packet.header = (type << 6) | 0x000e;
727 packet.data = data << 8;
728 ret = sisusb_send_packet(sisusb, 10, &packet);
729 break;
730 case 2:
731 packet.header = (type << 6) | 0x000c;
732 packet.data = data << 16;
733 ret = sisusb_send_packet(sisusb, 10, &packet);
734 packet.header = (type << 6) | 0x0001;
735 packet.address = (addr & ~3) + 4;
736 packet.data = (data >> 16) & 0x00ff;
737 ret |= sisusb_send_packet(sisusb, 10, &packet);
738 break;
739 case 3:
740 packet.header = (type << 6) | 0x0008;
741 packet.data = data << 24;
742 ret = sisusb_send_packet(sisusb, 10, &packet);
743 packet.header = (type << 6) | 0x0003;
744 packet.address = (addr & ~3) + 4;
745 packet.data = (data >> 8) & 0xffff;
746 ret |= sisusb_send_packet(sisusb, 10, &packet);
747 }
748
749 return ret;
750}
751
752static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
753 u32 addr, u32 data)
754{
755 struct sisusb_packet packet;
756 int ret = 0;
757
758 packet.address = addr & ~3;
759
760 switch (addr & 3) {
761 case 0:
762 packet.header = (type << 6) | 0x000f;
763 packet.data = data;
764 ret = sisusb_send_packet(sisusb, 10, &packet);
765 break;
766 case 1:
767 packet.header = (type << 6) | 0x000e;
768 packet.data = data << 8;
769 ret = sisusb_send_packet(sisusb, 10, &packet);
770 packet.header = (type << 6) | 0x0001;
771 packet.address = (addr & ~3) + 4;
772 packet.data = data >> 24;
773 ret |= sisusb_send_packet(sisusb, 10, &packet);
774 break;
775 case 2:
776 packet.header = (type << 6) | 0x000c;
777 packet.data = data << 16;
778 ret = sisusb_send_packet(sisusb, 10, &packet);
779 packet.header = (type << 6) | 0x0003;
780 packet.address = (addr & ~3) + 4;
781 packet.data = data >> 16;
782 ret |= sisusb_send_packet(sisusb, 10, &packet);
783 break;
784 case 3:
785 packet.header = (type << 6) | 0x0008;
786 packet.data = data << 24;
787 ret = sisusb_send_packet(sisusb, 10, &packet);
788 packet.header = (type << 6) | 0x0007;
789 packet.address = (addr & ~3) + 4;
790 packet.data = data >> 8;
791 ret |= sisusb_send_packet(sisusb, 10, &packet);
792 }
793
794 return ret;
795}
796
797/* The xxx_bulk routines copy a buffer of variable size. They treat the
798 * buffer as chars, therefore lsb/msb has to be corrected if using the
799 * byte/word/long/etc routines for speed-up
800 *
801 * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
802 * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
803 * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
804 * that the data already is in the transfer buffer "sisusb->obuf[index]".
805 */
806
807static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
808 char *kernbuffer, int length,
809 const char __user *userbuffer, int index,
810 ssize_t *bytes_written)
811{
812 struct sisusb_packet packet;
813 int ret = 0;
814 static int msgcount = 0;
815 u8 swap8, fromkern = kernbuffer ? 1 : 0;
816 u16 swap16;
817 u32 swap32, flag = (length >> 28) & 1;
818 char buf[4];
819
820 /* if neither kernbuffer not userbuffer are given, assume
821 * data in obuf
822 */
823 if (!fromkern && !userbuffer)
824 kernbuffer = sisusb->obuf[index];
825
826 (*bytes_written = 0);
827
828 length &= 0x00ffffff;
829
830 while (length) {
831
832 switch (length) {
833
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 case 1:
835 if (userbuffer) {
836 if (get_user(swap8, (u8 __user *)userbuffer))
837 return -EFAULT;
838 } else
839 swap8 = kernbuffer[0];
840
841 ret = sisusb_write_memio_byte(sisusb,
842 SISUSB_TYPE_MEM,
843 addr, swap8);
844
845 if (!ret)
846 (*bytes_written)++;
847
848 return ret;
849
850 case 2:
851 if (userbuffer) {
852 if (get_user(swap16, (u16 __user *)userbuffer))
853 return -EFAULT;
854 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200855 swap16 = *((u16 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
857 ret = sisusb_write_memio_word(sisusb,
858 SISUSB_TYPE_MEM,
859 addr,
860 swap16);
861
862 if (!ret)
863 (*bytes_written) += 2;
864
865 return ret;
866
867 case 3:
868 if (userbuffer) {
869 if (copy_from_user(&buf, userbuffer, 3))
870 return -EFAULT;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200871#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 swap32 = (buf[0] << 16) |
873 (buf[1] << 8) |
874 buf[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200875#else
876 swap32 = (buf[2] << 16) |
877 (buf[1] << 8) |
878 buf[0];
879#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200881#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 swap32 = (kernbuffer[0] << 16) |
883 (kernbuffer[1] << 8) |
884 kernbuffer[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200885#else
886 swap32 = (kernbuffer[2] << 16) |
887 (kernbuffer[1] << 8) |
888 kernbuffer[0];
889#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
891 ret = sisusb_write_memio_24bit(sisusb,
892 SISUSB_TYPE_MEM,
893 addr,
894 swap32);
895
896 if (!ret)
897 (*bytes_written) += 3;
898
899 return ret;
900
901 case 4:
902 if (userbuffer) {
903 if (get_user(swap32, (u32 __user *)userbuffer))
904 return -EFAULT;
905 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200906 swap32 = *((u32 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
908 ret = sisusb_write_memio_long(sisusb,
909 SISUSB_TYPE_MEM,
910 addr,
911 swap32);
912 if (!ret)
913 (*bytes_written) += 4;
914
915 return ret;
916
917 default:
918 if ((length & ~3) > 0x10000) {
919
920 packet.header = 0x001f;
921 packet.address = 0x000001d4;
922 packet.data = addr;
923 ret = sisusb_send_bridge_packet(sisusb, 10,
924 &packet, 0);
925 packet.header = 0x001f;
926 packet.address = 0x000001d0;
927 packet.data = (length & ~3);
928 ret |= sisusb_send_bridge_packet(sisusb, 10,
929 &packet, 0);
930 packet.header = 0x001f;
931 packet.address = 0x000001c0;
932 packet.data = flag | 0x16;
933 ret |= sisusb_send_bridge_packet(sisusb, 10,
934 &packet, 0);
935 if (userbuffer) {
936 ret |= sisusb_send_bulk_msg(sisusb,
937 SISUSB_EP_GFX_LBULK_OUT,
938 (length & ~3),
939 NULL, userbuffer, 0,
940 bytes_written, 0, 1);
941 userbuffer += (*bytes_written);
942 } else if (fromkern) {
943 ret |= sisusb_send_bulk_msg(sisusb,
944 SISUSB_EP_GFX_LBULK_OUT,
945 (length & ~3),
946 kernbuffer, NULL, 0,
947 bytes_written, 0, 1);
948 kernbuffer += (*bytes_written);
949 } else {
950 ret |= sisusb_send_bulk_msg(sisusb,
951 SISUSB_EP_GFX_LBULK_OUT,
952 (length & ~3),
953 NULL, NULL, index,
954 bytes_written, 0, 1);
955 kernbuffer += ((*bytes_written) &
956 (sisusb->obufsize-1));
957 }
958
959 } else {
960
961 packet.header = 0x001f;
962 packet.address = 0x00000194;
963 packet.data = addr;
964 ret = sisusb_send_bridge_packet(sisusb, 10,
Felipe Balbied86d972007-08-10 09:34:24 -0400965 &packet, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 packet.header = 0x001f;
967 packet.address = 0x00000190;
968 packet.data = (length & ~3);
969 ret |= sisusb_send_bridge_packet(sisusb, 10,
Felipe Balbied86d972007-08-10 09:34:24 -0400970 &packet, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 if (sisusb->flagb0 != 0x16) {
972 packet.header = 0x001f;
973 packet.address = 0x00000180;
974 packet.data = flag | 0x16;
975 ret |= sisusb_send_bridge_packet(sisusb, 10,
976 &packet, 0);
977 sisusb->flagb0 = 0x16;
978 }
979 if (userbuffer) {
980 ret |= sisusb_send_bulk_msg(sisusb,
981 SISUSB_EP_GFX_BULK_OUT,
982 (length & ~3),
983 NULL, userbuffer, 0,
984 bytes_written, 0, 1);
985 userbuffer += (*bytes_written);
986 } else if (fromkern) {
987 ret |= sisusb_send_bulk_msg(sisusb,
988 SISUSB_EP_GFX_BULK_OUT,
989 (length & ~3),
990 kernbuffer, NULL, 0,
991 bytes_written, 0, 1);
992 kernbuffer += (*bytes_written);
993 } else {
994 ret |= sisusb_send_bulk_msg(sisusb,
995 SISUSB_EP_GFX_BULK_OUT,
996 (length & ~3),
997 NULL, NULL, index,
998 bytes_written, 0, 1);
999 kernbuffer += ((*bytes_written) &
1000 (sisusb->obufsize-1));
1001 }
1002 }
1003 if (ret) {
1004 msgcount++;
1005 if (msgcount < 500)
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04001006 dev_err(&sisusb->sisusb_dev->dev, "Wrote %zd of %d bytes, error %d\n",
1007 *bytes_written, length, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 else if (msgcount == 500)
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04001009 dev_err(&sisusb->sisusb_dev->dev, "Too many errors, logging stopped\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 }
1011 addr += (*bytes_written);
1012 length -= (*bytes_written);
1013 }
1014
1015 if (ret)
Felipe Balbied86d972007-08-10 09:34:24 -04001016 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
1018 }
1019
1020 return ret ? -EIO : 0;
1021}
1022
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001023/* Remember: Read data in packet is in machine-endianess! So for
1024 * byte, word, 24bit, long no endian correction is necessary.
1025 */
1026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
1028 u32 addr, u8 *data)
1029{
1030 struct sisusb_packet packet;
1031 int ret;
1032
1033 CLEARPACKET(&packet);
1034 packet.header = (1 << (addr & 3)) | (type << 6);
1035 packet.address = addr & ~3;
1036 ret = sisusb_send_packet(sisusb, 6, &packet);
1037 *data = (u8)(packet.data >> ((addr & 3) << 3));
1038 return ret;
1039}
1040
1041static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
1042 u32 addr, u16 *data)
1043{
1044 struct sisusb_packet packet;
1045 int ret = 0;
1046
1047 CLEARPACKET(&packet);
1048
1049 packet.address = addr & ~3;
1050
1051 switch (addr & 3) {
1052 case 0:
1053 packet.header = (type << 6) | 0x0003;
1054 ret = sisusb_send_packet(sisusb, 6, &packet);
1055 *data = (u16)(packet.data);
1056 break;
1057 case 1:
1058 packet.header = (type << 6) | 0x0006;
1059 ret = sisusb_send_packet(sisusb, 6, &packet);
1060 *data = (u16)(packet.data >> 8);
1061 break;
1062 case 2:
1063 packet.header = (type << 6) | 0x000c;
1064 ret = sisusb_send_packet(sisusb, 6, &packet);
1065 *data = (u16)(packet.data >> 16);
1066 break;
1067 case 3:
1068 packet.header = (type << 6) | 0x0008;
1069 ret = sisusb_send_packet(sisusb, 6, &packet);
1070 *data = (u16)(packet.data >> 24);
1071 packet.header = (type << 6) | 0x0001;
1072 packet.address = (addr & ~3) + 4;
1073 ret |= sisusb_send_packet(sisusb, 6, &packet);
1074 *data |= (u16)(packet.data << 8);
1075 }
1076
1077 return ret;
1078}
1079
1080static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
1081 u32 addr, u32 *data)
1082{
1083 struct sisusb_packet packet;
1084 int ret = 0;
1085
1086 packet.address = addr & ~3;
1087
1088 switch (addr & 3) {
1089 case 0:
1090 packet.header = (type << 6) | 0x0007;
1091 ret = sisusb_send_packet(sisusb, 6, &packet);
1092 *data = packet.data & 0x00ffffff;
1093 break;
1094 case 1:
1095 packet.header = (type << 6) | 0x000e;
1096 ret = sisusb_send_packet(sisusb, 6, &packet);
1097 *data = packet.data >> 8;
1098 break;
1099 case 2:
1100 packet.header = (type << 6) | 0x000c;
1101 ret = sisusb_send_packet(sisusb, 6, &packet);
1102 *data = packet.data >> 16;
1103 packet.header = (type << 6) | 0x0001;
1104 packet.address = (addr & ~3) + 4;
1105 ret |= sisusb_send_packet(sisusb, 6, &packet);
1106 *data |= ((packet.data & 0xff) << 16);
1107 break;
1108 case 3:
1109 packet.header = (type << 6) | 0x0008;
1110 ret = sisusb_send_packet(sisusb, 6, &packet);
1111 *data = packet.data >> 24;
1112 packet.header = (type << 6) | 0x0003;
1113 packet.address = (addr & ~3) + 4;
1114 ret |= sisusb_send_packet(sisusb, 6, &packet);
1115 *data |= ((packet.data & 0xffff) << 8);
1116 }
1117
1118 return ret;
1119}
1120
1121static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
1122 u32 addr, u32 *data)
1123{
1124 struct sisusb_packet packet;
1125 int ret = 0;
1126
1127 packet.address = addr & ~3;
1128
1129 switch (addr & 3) {
1130 case 0:
1131 packet.header = (type << 6) | 0x000f;
1132 ret = sisusb_send_packet(sisusb, 6, &packet);
1133 *data = packet.data;
1134 break;
1135 case 1:
1136 packet.header = (type << 6) | 0x000e;
1137 ret = sisusb_send_packet(sisusb, 6, &packet);
1138 *data = packet.data >> 8;
1139 packet.header = (type << 6) | 0x0001;
1140 packet.address = (addr & ~3) + 4;
1141 ret |= sisusb_send_packet(sisusb, 6, &packet);
1142 *data |= (packet.data << 24);
1143 break;
1144 case 2:
1145 packet.header = (type << 6) | 0x000c;
1146 ret = sisusb_send_packet(sisusb, 6, &packet);
1147 *data = packet.data >> 16;
1148 packet.header = (type << 6) | 0x0003;
1149 packet.address = (addr & ~3) + 4;
1150 ret |= sisusb_send_packet(sisusb, 6, &packet);
1151 *data |= (packet.data << 16);
1152 break;
1153 case 3:
1154 packet.header = (type << 6) | 0x0008;
1155 ret = sisusb_send_packet(sisusb, 6, &packet);
1156 *data = packet.data >> 24;
1157 packet.header = (type << 6) | 0x0007;
1158 packet.address = (addr & ~3) + 4;
1159 ret |= sisusb_send_packet(sisusb, 6, &packet);
1160 *data |= (packet.data << 8);
1161 }
1162
1163 return ret;
1164}
1165
1166static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
1167 char *kernbuffer, int length,
1168 char __user *userbuffer, ssize_t *bytes_read)
1169{
1170 int ret = 0;
1171 char buf[4];
1172 u16 swap16;
1173 u32 swap32;
1174
1175 (*bytes_read = 0);
1176
1177 length &= 0x00ffffff;
1178
1179 while (length) {
1180
1181 switch (length) {
1182
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 case 1:
1184
1185 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
1186 addr, &buf[0]);
1187 if (!ret) {
1188 (*bytes_read)++;
1189 if (userbuffer) {
1190 if (put_user(buf[0],
1191 (u8 __user *)userbuffer)) {
1192 return -EFAULT;
1193 }
1194 } else {
1195 kernbuffer[0] = buf[0];
1196 }
1197 }
1198 return ret;
1199
1200 case 2:
1201 ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
1202 addr, &swap16);
1203 if (!ret) {
1204 (*bytes_read) += 2;
1205 if (userbuffer) {
1206 if (put_user(swap16,
1207 (u16 __user *)userbuffer))
1208 return -EFAULT;
1209 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001210 *((u16 *)kernbuffer) = swap16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 }
1212 }
1213 return ret;
1214
1215 case 3:
1216 ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
1217 addr, &swap32);
1218 if (!ret) {
1219 (*bytes_read) += 3;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001220#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 buf[0] = (swap32 >> 16) & 0xff;
1222 buf[1] = (swap32 >> 8) & 0xff;
1223 buf[2] = swap32 & 0xff;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001224#else
1225 buf[2] = (swap32 >> 16) & 0xff;
1226 buf[1] = (swap32 >> 8) & 0xff;
1227 buf[0] = swap32 & 0xff;
1228#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 if (userbuffer) {
1230 if (copy_to_user(userbuffer, &buf[0], 3))
1231 return -EFAULT;
1232 } else {
1233 kernbuffer[0] = buf[0];
1234 kernbuffer[1] = buf[1];
1235 kernbuffer[2] = buf[2];
1236 }
1237 }
1238 return ret;
1239
1240 default:
1241 ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
1242 addr, &swap32);
1243 if (!ret) {
1244 (*bytes_read) += 4;
1245 if (userbuffer) {
1246 if (put_user(swap32,
1247 (u32 __user *)userbuffer))
1248 return -EFAULT;
1249
1250 userbuffer += 4;
1251 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001252 *((u32 *)kernbuffer) = swap32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 kernbuffer += 4;
1254 }
1255 addr += 4;
1256 length -= 4;
1257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 }
1259
1260 if (ret)
Felipe Balbied86d972007-08-10 09:34:24 -04001261 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 }
1263
1264 return ret;
1265}
1266
1267/* High level: Gfx (indexed) register access */
1268
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001269#ifdef INCL_SISUSB_CON
1270int
1271sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
1272{
1273 return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1274}
1275
1276int
1277sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
1278{
1279 return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1280}
1281#endif
1282
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001283int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
1285{
1286 int ret;
1287 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1288 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1289 return ret;
1290}
1291
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001292int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
1294{
1295 int ret;
1296 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1297 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1298 return ret;
1299}
1300
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001301int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
1303 u8 myand, u8 myor)
1304{
1305 int ret;
1306 u8 tmp;
1307
1308 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1309 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1310 tmp &= myand;
1311 tmp |= myor;
1312 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1313 return ret;
1314}
1315
1316static int
1317sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx,
1318 u8 data, u8 mask)
1319{
1320 int ret;
1321 u8 tmp;
1322 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1323 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1324 tmp &= ~(mask);
1325 tmp |= (data & mask);
1326 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1327 return ret;
1328}
1329
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001330int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
1332{
1333 return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
1334}
1335
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001336int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
1338{
1339 return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00));
1340}
1341
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001342/* Write/read video ram */
1343
1344#ifdef INCL_SISUSB_CON
1345int
1346sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data)
1347{
1348 return(sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
1349}
1350
1351int
1352sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data)
1353{
1354 return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
1355}
1356
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001357int
1358sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
1359 u32 dest, int length, size_t *bytes_written)
1360{
1361 return(sisusb_write_mem_bulk(sisusb, dest, src, length, NULL, 0, bytes_written));
1362}
1363
1364#ifdef SISUSBENDIANTEST
1365int
1366sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest,
1367 u32 src, int length, size_t *bytes_written)
1368{
1369 return(sisusb_read_mem_bulk(sisusb, src, dest, length, NULL, bytes_written));
1370}
1371#endif
1372#endif
1373
1374#ifdef SISUSBENDIANTEST
1375static void
1376sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
1377{
1378 static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
1379 char destbuffer[10];
1380 size_t dummy;
1381 int i,j;
1382
1383 sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy);
1384
1385 for(i = 1; i <= 7; i++) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04001386 dev_dbg(&sisusb->sisusb_dev->dev, "sisusb: rwtest %d bytes\n", i);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001387 sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy);
1388 for(j = 0; j < i; j++) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04001389 dev_dbg(&sisusb->sisusb_dev->dev, "rwtest read[%d] = %x\n", j, destbuffer[j]);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001390 }
1391 }
1392}
1393#endif
1394
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395/* access pci config registers (reg numbers 0, 4, 8, etc) */
1396
1397static int
1398sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data)
1399{
1400 struct sisusb_packet packet;
1401 int ret;
1402
1403 packet.header = 0x008f;
1404 packet.address = regnum | 0x10000;
1405 packet.data = data;
1406 ret = sisusb_send_packet(sisusb, 10, &packet);
1407 return ret;
1408}
1409
1410static int
1411sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data)
1412{
1413 struct sisusb_packet packet;
1414 int ret;
1415
1416 packet.header = 0x008f;
1417 packet.address = (u32)regnum | 0x10000;
1418 ret = sisusb_send_packet(sisusb, 6, &packet);
1419 *data = packet.data;
1420 return ret;
1421}
1422
1423/* Clear video RAM */
1424
1425static int
1426sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
1427{
1428 int ret, i;
1429 ssize_t j;
1430
1431 if (address < sisusb->vrambase)
1432 return 1;
1433
1434 if (address >= sisusb->vrambase + sisusb->vramsize)
1435 return 1;
1436
1437 if (address + length > sisusb->vrambase + sisusb->vramsize)
1438 length = sisusb->vrambase + sisusb->vramsize - address;
1439
1440 if (length <= 0)
1441 return 0;
1442
1443 /* allocate free buffer/urb and clear the buffer */
1444 if ((i = sisusb_alloc_outbuf(sisusb)) < 0)
1445 return -EBUSY;
1446
1447 memset(sisusb->obuf[i], 0, sisusb->obufsize);
1448
1449 /* We can write a length > buffer size here. The buffer
1450 * data will simply be re-used (like a ring-buffer).
1451 */
1452 ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
1453
1454 /* Free the buffer/urb */
1455 sisusb_free_outbuf(sisusb, i);
1456
1457 return ret;
1458}
1459
1460/* Initialize the graphics core (return 0 on success)
1461 * This resets the graphics hardware and puts it into
1462 * a defined mode (640x480@60Hz)
1463 */
1464
1465#define GETREG(r,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1466#define SETREG(r,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1467#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d)
1468#define GETIREG(r,i,d) sisusb_getidxreg(sisusb, r, i, d)
1469#define SETIREGOR(r,i,o) sisusb_setidxregor(sisusb, r, i, o)
1470#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a)
1471#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o)
1472#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
Felipe Balbied86d972007-08-10 09:34:24 -04001473#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
Felipe Balbied86d972007-08-10 09:34:24 -04001475#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476
1477static int
1478sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
1479{
1480 int ret;
1481 u8 tmp8;
1482
1483 ret = GETIREG(SISSR, 0x16, &tmp8);
1484 if (ramtype <= 1) {
1485 tmp8 &= 0x3f;
1486 ret |= SETIREG(SISSR, 0x16, tmp8);
1487 tmp8 |= 0x80;
1488 ret |= SETIREG(SISSR, 0x16, tmp8);
1489 } else {
1490 tmp8 |= 0xc0;
1491 ret |= SETIREG(SISSR, 0x16, tmp8);
1492 tmp8 &= 0x0f;
1493 ret |= SETIREG(SISSR, 0x16, tmp8);
1494 tmp8 |= 0x80;
1495 ret |= SETIREG(SISSR, 0x16, tmp8);
1496 tmp8 &= 0x0f;
1497 ret |= SETIREG(SISSR, 0x16, tmp8);
1498 tmp8 |= 0xd0;
1499 ret |= SETIREG(SISSR, 0x16, tmp8);
1500 tmp8 &= 0x0f;
1501 ret |= SETIREG(SISSR, 0x16, tmp8);
1502 tmp8 |= 0xa0;
1503 ret |= SETIREG(SISSR, 0x16, tmp8);
1504 }
1505 return ret;
1506}
1507
1508static int
1509sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab)
1510{
1511 int ret;
1512 u8 ramtype, done = 0;
1513 u32 t0, t1, t2, t3;
1514 u32 ramptr = SISUSB_PCI_MEMBASE;
1515
1516 ret = GETIREG(SISSR, 0x3a, &ramtype);
1517 ramtype &= 3;
1518
1519 ret |= SETIREG(SISSR, 0x13, 0x00);
1520
1521 if (ramtype <= 1) {
1522 ret |= SETIREG(SISSR, 0x14, 0x12);
1523 ret |= SETIREGAND(SISSR, 0x15, 0xef);
1524 } else {
1525 ret |= SETIREG(SISSR, 0x14, 0x02);
1526 }
1527
1528 ret |= sisusb_triggersr16(sisusb, ramtype);
1529 ret |= WRITEL(ramptr + 0, 0x01234567);
1530 ret |= WRITEL(ramptr + 4, 0x456789ab);
1531 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1532 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1533 ret |= WRITEL(ramptr + 16, 0x55555555);
1534 ret |= WRITEL(ramptr + 20, 0x55555555);
1535 ret |= WRITEL(ramptr + 24, 0xffffffff);
1536 ret |= WRITEL(ramptr + 28, 0xffffffff);
1537 ret |= READL(ramptr + 0, &t0);
1538 ret |= READL(ramptr + 4, &t1);
1539 ret |= READL(ramptr + 8, &t2);
1540 ret |= READL(ramptr + 12, &t3);
1541
1542 if (ramtype <= 1) {
1543
1544 *chab = 0; *bw = 64;
1545
1546 if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
1547 if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
1548 *chab = 0; *bw = 64;
1549 ret |= SETIREGAND(SISSR, 0x14, 0xfd);
1550 }
1551 }
1552 if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
1553 *chab = 1; *bw = 64;
1554 ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01);
1555
1556 ret |= sisusb_triggersr16(sisusb, ramtype);
1557 ret |= WRITEL(ramptr + 0, 0x89abcdef);
1558 ret |= WRITEL(ramptr + 4, 0xcdef0123);
1559 ret |= WRITEL(ramptr + 8, 0x55555555);
1560 ret |= WRITEL(ramptr + 12, 0x55555555);
1561 ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
1562 ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
1563 ret |= READL(ramptr + 4, &t1);
1564
1565 if (t1 != 0xcdef0123) {
1566 *bw = 32;
1567 ret |= SETIREGOR(SISSR, 0x15, 0x10);
1568 }
1569 }
1570
1571 } else {
1572
1573 *chab = 0; *bw = 64; /* default: cha, bw = 64 */
1574
1575 done = 0;
1576
1577 if (t1 == 0x456789ab) {
1578 if (t0 == 0x01234567) {
1579 *chab = 0; *bw = 64;
1580 done = 1;
1581 }
1582 } else {
1583 if (t0 == 0x01234567) {
1584 *chab = 0; *bw = 32;
1585 ret |= SETIREG(SISSR, 0x14, 0x00);
1586 done = 1;
1587 }
1588 }
1589
1590 if (!done) {
1591 ret |= SETIREG(SISSR, 0x14, 0x03);
1592 ret |= sisusb_triggersr16(sisusb, ramtype);
1593
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
1605 if (t1 == 0x456789ab) {
1606 if (t0 == 0x01234567) {
1607 *chab = 1; *bw = 64;
1608 return ret;
1609 } /* else error */
1610 } else {
1611 if (t0 == 0x01234567) {
1612 *chab = 1; *bw = 32;
1613 ret |= SETIREG(SISSR, 0x14, 0x01);
1614 } /* else error */
1615 }
1616 }
1617 }
1618 return ret;
1619}
1620
1621static int
1622sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
1623{
1624 int ret = 0;
1625 u32 ramptr = SISUSB_PCI_MEMBASE;
1626 u8 tmp1, tmp2, i, j;
1627
1628 ret |= WRITEB(ramptr, 0xaa);
1629 ret |= WRITEB(ramptr + 16, 0x55);
1630 ret |= READB(ramptr, &tmp1);
1631 ret |= READB(ramptr + 16, &tmp2);
1632 if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
1633 for (i = 0, j = 16; i < 2; i++, j += 16) {
1634 ret |= GETIREG(SISSR, 0x21, &tmp1);
1635 ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
1636 ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */
1637 ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
1638 ret |= SETIREG(SISSR, 0x21, tmp1);
1639 ret |= WRITEB(ramptr + 16 + j, j);
1640 ret |= READB(ramptr + 16 + j, &tmp1);
1641 if (tmp1 == j) {
1642 ret |= WRITEB(ramptr + j, j);
1643 break;
1644 }
1645 }
1646 }
1647 return ret;
1648}
1649
1650static int
1651sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index,
1652 u8 rankno, u8 chab, const u8 dramtype[][5],
1653 int bw)
1654{
1655 int ret = 0, ranksize;
1656 u8 tmp;
1657
1658 *iret = 0;
1659
1660 if ((rankno == 2) && (dramtype[index][0] == 2))
1661 return ret;
1662
1663 ranksize = dramtype[index][3] / 2 * bw / 32;
1664
1665 if ((ranksize * rankno) > 128)
1666 return ret;
1667
1668 tmp = 0;
1669 while ((ranksize >>= 1) > 0) tmp += 0x10;
1670 tmp |= ((rankno - 1) << 2);
1671 tmp |= ((bw / 64) & 0x02);
1672 tmp |= (chab & 0x01);
1673
1674 ret = SETIREG(SISSR, 0x14, tmp);
1675 ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
1676
1677 *iret = 1;
1678
1679 return ret;
1680}
1681
1682static int
1683sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn)
1684{
1685 int ret = 0, i;
1686 u32 j, tmp;
1687
1688 *iret = 0;
1689
1690 for (i = 0, j = 0; i < testn; i++) {
1691 ret |= WRITEL(sisusb->vrambase + j, j);
1692 j += inc;
1693 }
1694
1695 for (i = 0, j = 0; i < testn; i++) {
1696 ret |= READL(sisusb->vrambase + j, &tmp);
1697 if (tmp != j) return ret;
1698 j += inc;
1699 }
1700
1701 *iret = 1;
1702 return ret;
1703}
1704
1705static int
1706sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno,
1707 int idx, int bw, const u8 rtype[][5])
1708{
1709 int ret = 0, i, i2ret;
1710 u32 inc;
1711
1712 *iret = 0;
1713
1714 for (i = rankno; i >= 1; i--) {
1715 inc = 1 << (rtype[idx][2] +
1716 rtype[idx][1] +
1717 rtype[idx][0] +
1718 bw / 64 + i);
1719 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1720 if (!i2ret)
1721 return ret;
1722 }
1723
1724 inc = 1 << (rtype[idx][2] + bw / 64 + 2);
1725 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
1726 if (!i2ret)
1727 return ret;
1728
1729 inc = 1 << (10 + bw / 64);
1730 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1731 if (!i2ret)
1732 return ret;
1733
1734 *iret = 1;
1735 return ret;
1736}
1737
1738static int
1739sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw,
1740 int chab)
1741{
1742 int ret = 0, i2ret = 0, i, j;
1743 static const u8 sdramtype[13][5] = {
1744 { 2, 12, 9, 64, 0x35 },
1745 { 1, 13, 9, 64, 0x44 },
1746 { 2, 12, 8, 32, 0x31 },
1747 { 2, 11, 9, 32, 0x25 },
1748 { 1, 12, 9, 32, 0x34 },
1749 { 1, 13, 8, 32, 0x40 },
1750 { 2, 11, 8, 16, 0x21 },
1751 { 1, 12, 8, 16, 0x30 },
1752 { 1, 11, 9, 16, 0x24 },
1753 { 1, 11, 8, 8, 0x20 },
1754 { 2, 9, 8, 4, 0x01 },
1755 { 1, 10, 8, 4, 0x10 },
1756 { 1, 9, 8, 2, 0x00 }
1757 };
1758
1759 *iret = 1; /* error */
1760
1761 for (i = 0; i < 13; i++) {
1762 ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
1763 for (j = 2; j > 0; j--) {
1764 ret |= sisusb_set_rank(sisusb, &i2ret, i, j,
1765 chab, sdramtype, bw);
1766 if (!i2ret)
1767 continue;
1768
1769 ret |= sisusb_check_ranks(sisusb, &i2ret, j, i,
1770 bw, sdramtype);
1771 if (i2ret) {
1772 *iret = 0; /* ram size found */
1773 return ret;
1774 }
1775 }
1776 }
1777
1778 return ret;
1779}
1780
1781static int
1782sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
1783{
1784 int ret = 0;
1785 u32 address;
1786 int i, length, modex, modey, bpp;
1787
1788 modex = 640; modey = 480; bpp = 2;
1789
1790 address = sisusb->vrambase; /* Clear video ram */
1791
1792 if (clrall)
1793 length = sisusb->vramsize;
1794 else
1795 length = modex * bpp * modey;
1796
1797 ret = sisusb_clear_vram(sisusb, address, length);
1798
1799 if (!ret && drwfr) {
1800 for (i = 0; i < modex; i++) {
1801 address = sisusb->vrambase + (i * bpp);
1802 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1803 address, 0xf100);
1804 address += (modex * (modey-1) * bpp);
1805 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1806 address, 0xf100);
1807 }
1808 for (i = 0; i < modey; i++) {
1809 address = sisusb->vrambase + ((i * modex) * bpp);
1810 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1811 address, 0xf100);
1812 address += ((modex - 1) * bpp);
1813 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1814 address, 0xf100);
1815 }
1816 }
1817
1818 return ret;
1819}
1820
1821static int
1822sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
1823{
1824 int ret = 0, i, j, modex, modey, bpp, du;
1825 u8 sr31, cr63, tmp8;
1826 static const char attrdata[] = {
1827 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
1828 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
1829 0x01,0x00,0x00,0x00
1830 };
1831 static const char crtcrdata[] = {
1832 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
1833 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
1834 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
1835 0xff
1836 };
1837 static const char grcdata[] = {
1838 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
1839 0xff
1840 };
1841 static const char crtcdata[] = {
1842 0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e,
1843 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05,
1844 0x00
1845 };
1846
1847 modex = 640; modey = 480; bpp = 2;
1848
1849 GETIREG(SISSR, 0x31, &sr31);
1850 GETIREG(SISCR, 0x63, &cr63);
1851 SETIREGOR(SISSR, 0x01, 0x20);
1852 SETIREG(SISCR, 0x63, cr63 & 0xbf);
1853 SETIREGOR(SISCR, 0x17, 0x80);
1854 SETIREGOR(SISSR, 0x1f, 0x04);
1855 SETIREGAND(SISSR, 0x07, 0xfb);
1856 SETIREG(SISSR, 0x00, 0x03); /* seq */
1857 SETIREG(SISSR, 0x01, 0x21);
1858 SETIREG(SISSR, 0x02, 0x0f);
1859 SETIREG(SISSR, 0x03, 0x00);
1860 SETIREG(SISSR, 0x04, 0x0e);
1861 SETREG(SISMISCW, 0x23); /* misc */
1862 for (i = 0; i <= 0x18; i++) { /* crtc */
1863 SETIREG(SISCR, i, crtcrdata[i]);
1864 }
1865 for (i = 0; i <= 0x13; i++) { /* att */
1866 GETREG(SISINPSTAT, &tmp8);
1867 SETREG(SISAR, i);
1868 SETREG(SISAR, attrdata[i]);
1869 }
1870 GETREG(SISINPSTAT, &tmp8);
1871 SETREG(SISAR, 0x14);
1872 SETREG(SISAR, 0x00);
1873 GETREG(SISINPSTAT, &tmp8);
1874 SETREG(SISAR, 0x20);
1875 GETREG(SISINPSTAT, &tmp8);
1876 for (i = 0; i <= 0x08; i++) { /* grc */
1877 SETIREG(SISGR, i, grcdata[i]);
1878 }
1879 SETIREGAND(SISGR, 0x05, 0xbf);
1880 for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */
1881 SETIREG(SISSR, i, 0x00);
1882 }
1883 SETIREGAND(SISSR, 0x37, 0xfe);
1884 SETREG(SISMISCW, 0xef); /* sync */
1885 SETIREG(SISCR, 0x11, 0x00); /* crtc */
1886 for (j = 0x00, i = 0; i <= 7; i++, j++) {
1887 SETIREG(SISCR, j, crtcdata[i]);
1888 }
1889 for (j = 0x10; i <= 10; i++, j++) {
1890 SETIREG(SISCR, j, crtcdata[i]);
1891 }
1892 for (j = 0x15; i <= 12; i++, j++) {
1893 SETIREG(SISCR, j, crtcdata[i]);
1894 }
1895 for (j = 0x0A; i <= 15; i++, j++) {
1896 SETIREG(SISSR, j, crtcdata[i]);
1897 }
1898 SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
1899 SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
1900 SETIREG(SISCR, 0x14, 0x4f);
1901 du = (modex / 16) * (bpp * 2); /* offset/pitch */
1902 if (modex % 16) du += bpp;
1903 SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
1904 SETIREG(SISCR, 0x13, (du & 0xff));
1905 du <<= 5;
1906 tmp8 = du >> 8;
1907 if (du & 0xff) tmp8++;
1908 SETIREG(SISSR, 0x10, tmp8);
1909 SETIREG(SISSR, 0x31, 0x00); /* VCLK */
1910 SETIREG(SISSR, 0x2b, 0x1b);
1911 SETIREG(SISSR, 0x2c, 0xe1);
1912 SETIREG(SISSR, 0x2d, 0x01);
1913 SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */
1914 SETIREG(SISSR, 0x08, 0xae);
1915 SETIREGAND(SISSR, 0x09, 0xf0);
1916 SETIREG(SISSR, 0x08, 0x34);
1917 SETIREGOR(SISSR, 0x3d, 0x01);
1918 SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */
1919 SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
1920 SETIREG(SISCR, 0x19, 0x00);
1921 SETIREGAND(SISCR, 0x1a, 0xfc);
1922 SETIREGAND(SISSR, 0x0f, 0xb7);
1923 SETIREGAND(SISSR, 0x31, 0xfb);
1924 SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
1925 SETIREGAND(SISSR, 0x32, 0xf3);
1926 SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
1927 SETIREG(SISCR, 0x52, 0x6c);
1928
1929 SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */
1930 SETIREG(SISCR, 0x0c, 0x00);
1931 SETIREG(SISSR, 0x0d, 0x00);
1932 SETIREGAND(SISSR, 0x37, 0xfe);
1933
1934 SETIREG(SISCR, 0x32, 0x20);
1935 SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */
1936 SETIREG(SISCR, 0x63, (cr63 & 0xbf));
1937 SETIREG(SISSR, 0x31, (sr31 & 0xfb));
1938
1939 if (touchengines) {
1940 SETIREG(SISSR, 0x20, 0xa1); /* enable engines */
1941 SETIREGOR(SISSR, 0x1e, 0x5a);
1942
1943 SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */
1944 SETIREG(SISSR, 0x27, 0x1f);
1945 SETIREG(SISSR, 0x26, 0x00);
1946 }
1947
Felipe Balbied86d972007-08-10 09:34:24 -04001948 SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949
1950 return ret;
1951}
1952
1953static int
1954sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
1955{
1956 int ret = 0, i, j, bw, chab, iret, retry = 3;
1957 u8 tmp8, ramtype;
1958 u32 tmp32;
1959 static const char mclktable[] = {
1960 0x3b, 0x22, 0x01, 143,
1961 0x3b, 0x22, 0x01, 143,
1962 0x3b, 0x22, 0x01, 143,
1963 0x3b, 0x22, 0x01, 143
1964 };
1965 static const char eclktable[] = {
1966 0x3b, 0x22, 0x01, 143,
1967 0x3b, 0x22, 0x01, 143,
1968 0x3b, 0x22, 0x01, 143,
1969 0x3b, 0x22, 0x01, 143
1970 };
1971 static const char ramtypetable1[] = {
1972 0x00, 0x04, 0x60, 0x60,
1973 0x0f, 0x0f, 0x1f, 0x1f,
1974 0xba, 0xba, 0xba, 0xba,
1975 0xa9, 0xa9, 0xac, 0xac,
1976 0xa0, 0xa0, 0xa0, 0xa8,
1977 0x00, 0x00, 0x02, 0x02,
1978 0x30, 0x30, 0x40, 0x40
1979 };
1980 static const char ramtypetable2[] = {
1981 0x77, 0x77, 0x44, 0x44,
1982 0x77, 0x77, 0x44, 0x44,
1983 0x00, 0x00, 0x00, 0x00,
1984 0x5b, 0x5b, 0xab, 0xab,
1985 0x00, 0x00, 0xf0, 0xf8
1986 };
1987
1988 while (retry--) {
1989
1990 /* Enable VGA */
1991 ret = GETREG(SISVGAEN, &tmp8);
1992 ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
1993
1994 /* Enable GPU access to VRAM */
1995 ret |= GETREG(SISMISCR, &tmp8);
1996 ret |= SETREG(SISMISCW, (tmp8 | 0x01));
1997
1998 if (ret) continue;
1999
2000 /* Reset registers */
2001 ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
2002 ret |= SETIREG(SISSR, 0x05, 0x86);
2003 ret |= SETIREGOR(SISSR, 0x20, 0x01);
2004
2005 ret |= SETREG(SISMISCW, 0x67);
2006
2007 for (i = 0x06; i <= 0x1f; i++) {
2008 ret |= SETIREG(SISSR, i, 0x00);
2009 }
2010 for (i = 0x21; i <= 0x27; i++) {
2011 ret |= SETIREG(SISSR, i, 0x00);
2012 }
2013 for (i = 0x31; i <= 0x3d; i++) {
2014 ret |= SETIREG(SISSR, i, 0x00);
2015 }
2016 for (i = 0x12; i <= 0x1b; i++) {
2017 ret |= SETIREG(SISSR, i, 0x00);
2018 }
2019 for (i = 0x79; i <= 0x7c; i++) {
2020 ret |= SETIREG(SISCR, i, 0x00);
2021 }
2022
2023 if (ret) continue;
2024
2025 ret |= SETIREG(SISCR, 0x63, 0x80);
2026
2027 ret |= GETIREG(SISSR, 0x3a, &ramtype);
2028 ramtype &= 0x03;
2029
2030 ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
2031 ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
2032 ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
2033
2034 ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
2035 ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
2036 ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
2037
2038 ret |= SETIREG(SISSR, 0x07, 0x18);
2039 ret |= SETIREG(SISSR, 0x11, 0x0f);
2040
2041 if (ret) continue;
2042
2043 for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
2044 ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]);
2045 }
2046 for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
2047 ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]);
2048 }
2049
2050 ret |= SETIREG(SISCR, 0x49, 0xaa);
2051
2052 ret |= SETIREG(SISSR, 0x1f, 0x00);
2053 ret |= SETIREG(SISSR, 0x20, 0xa0);
2054 ret |= SETIREG(SISSR, 0x23, 0xf6);
2055 ret |= SETIREG(SISSR, 0x24, 0x0d);
2056 ret |= SETIREG(SISSR, 0x25, 0x33);
2057
2058 ret |= SETIREG(SISSR, 0x11, 0x0f);
2059
2060 ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
2061
2062 ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
2063
2064 if (ret) continue;
2065
2066 ret |= SETIREG(SISPART1, 0x00, 0x00);
2067
2068 ret |= GETIREG(SISSR, 0x13, &tmp8);
2069 tmp8 >>= 4;
2070
2071 ret |= SETIREG(SISPART1, 0x02, 0x00);
2072 ret |= SETIREG(SISPART1, 0x2e, 0x08);
2073
2074 ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
2075 tmp32 &= 0x00f00000;
2076 tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
2077 ret |= SETIREG(SISSR, 0x25, tmp8);
2078 tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
2079 ret |= SETIREG(SISCR, 0x49, tmp8);
2080
2081 ret |= SETIREG(SISSR, 0x27, 0x1f);
2082 ret |= SETIREG(SISSR, 0x31, 0x00);
2083 ret |= SETIREG(SISSR, 0x32, 0x11);
2084 ret |= SETIREG(SISSR, 0x33, 0x00);
2085
2086 if (ret) continue;
2087
2088 ret |= SETIREG(SISCR, 0x83, 0x00);
2089
2090 ret |= sisusb_set_default_mode(sisusb, 0);
2091
2092 ret |= SETIREGAND(SISSR, 0x21, 0xdf);
2093 ret |= SETIREGOR(SISSR, 0x01, 0x20);
2094 ret |= SETIREGOR(SISSR, 0x16, 0x0f);
2095
2096 ret |= sisusb_triggersr16(sisusb, ramtype);
2097
2098 /* Disable refresh */
2099 ret |= SETIREGAND(SISSR, 0x17, 0xf8);
2100 ret |= SETIREGOR(SISSR, 0x19, 0x03);
2101
2102 ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
2103 ret |= sisusb_verify_mclk(sisusb);
2104
2105 if (ramtype <= 1) {
2106 ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
2107 if (iret) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002108 dev_err(&sisusb->sisusb_dev->dev,"RAM size detection failed, assuming 8MB video RAM\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 ret |= SETIREG(SISSR,0x14,0x31);
2110 /* TODO */
2111 }
2112 } else {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002113 dev_err(&sisusb->sisusb_dev->dev, "DDR RAM device found, assuming 8MB video RAM\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 ret |= SETIREG(SISSR,0x14,0x31);
2115 /* *** TODO *** */
2116 }
2117
2118 /* Enable refresh */
2119 ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
2120 ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
2121 ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
2122
2123 ret |= SETIREGOR(SISSR, 0x21, 0x20);
2124
2125 ret |= SETIREG(SISSR, 0x22, 0xfb);
2126 ret |= SETIREG(SISSR, 0x21, 0xa5);
2127
2128 if (ret == 0)
2129 break;
2130 }
2131
2132 return ret;
2133}
2134
2135#undef SETREG
2136#undef GETREG
2137#undef SETIREG
2138#undef GETIREG
2139#undef SETIREGOR
2140#undef SETIREGAND
2141#undef SETIREGANDOR
2142#undef READL
2143#undef WRITEL
2144
2145static void
2146sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
2147{
2148 u8 tmp8, tmp82, ramtype;
2149 int bw = 0;
2150 char *ramtypetext1 = NULL;
2151 const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM",
2152 "DDR SDRAM", "DDR SGRAM" };
2153 static const int busSDR[4] = {64, 64, 128, 128};
2154 static const int busDDR[4] = {32, 32, 64, 64};
2155 static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
2156
2157 sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
2158 sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
2159 sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
2160 sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
2161 ramtype &= 0x03;
2162 switch ((tmp8 >> 2) & 0x03) {
2163 case 0: ramtypetext1 = "1 ch/1 r";
2164 if (tmp82 & 0x10) {
2165 bw = 32;
2166 } else {
2167 bw = busSDR[(tmp8 & 0x03)];
2168 }
2169 break;
2170 case 1: ramtypetext1 = "1 ch/2 r";
2171 sisusb->vramsize <<= 1;
2172 bw = busSDR[(tmp8 & 0x03)];
2173 break;
2174 case 2: ramtypetext1 = "asymmeric";
2175 sisusb->vramsize += sisusb->vramsize/2;
2176 bw = busDDRA[(tmp8 & 0x03)];
2177 break;
2178 case 3: ramtypetext1 = "2 channel";
2179 sisusb->vramsize <<= 1;
2180 bw = busDDR[(tmp8 & 0x03)];
2181 break;
2182 }
2183
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002184 dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 ramtypetext2[ramtype], bw);
2186}
2187
2188static int
2189sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
2190{
2191 struct sisusb_packet packet;
2192 int ret;
2193 u32 tmp32;
2194
2195 /* Do some magic */
2196 packet.header = 0x001f;
2197 packet.address = 0x00000324;
2198 packet.data = 0x00000004;
2199 ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2200
2201 packet.header = 0x001f;
2202 packet.address = 0x00000364;
2203 packet.data = 0x00000004;
2204 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2205
2206 packet.header = 0x001f;
2207 packet.address = 0x00000384;
2208 packet.data = 0x00000004;
2209 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2210
2211 packet.header = 0x001f;
2212 packet.address = 0x00000100;
2213 packet.data = 0x00000700;
2214 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2215
2216 packet.header = 0x000f;
2217 packet.address = 0x00000004;
2218 ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
2219 packet.data |= 0x17;
2220 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2221
2222 /* Init BAR 0 (VRAM) */
2223 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2224 ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
2225 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2226 tmp32 &= 0x0f;
2227 tmp32 |= SISUSB_PCI_MEMBASE;
2228 ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
2229
2230 /* Init BAR 1 (MMIO) */
2231 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2232 ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
2233 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2234 tmp32 &= 0x0f;
2235 tmp32 |= SISUSB_PCI_MMIOBASE;
2236 ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
2237
2238 /* Init BAR 2 (i/o ports) */
2239 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2240 ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
2241 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2242 tmp32 &= 0x0f;
2243 tmp32 |= SISUSB_PCI_IOPORTBASE;
2244 ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
2245
2246 /* Enable memory and i/o access */
2247 ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
2248 tmp32 |= 0x3;
2249 ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
2250
2251 if (ret == 0) {
2252 /* Some further magic */
2253 packet.header = 0x001f;
2254 packet.address = 0x00000050;
2255 packet.data = 0x000000ff;
2256 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2257 }
2258
2259 return ret;
2260}
2261
2262/* Initialize the graphics device (return 0 on success)
2263 * This initializes the net2280 as well as the PCI registers
2264 * of the graphics board.
2265 */
2266
2267static int
2268sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
2269{
2270 int ret = 0, test = 0;
2271 u32 tmp32;
2272
2273 if (sisusb->devinit == 1) {
2274 /* Read PCI BARs and see if they have been set up */
2275 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2276 if (ret) return ret;
2277 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++;
2278
2279 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2280 if (ret) return ret;
2281 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++;
2282
2283 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2284 if (ret) return ret;
2285 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++;
2286 }
2287
2288 /* No? So reset the device */
2289 if ((sisusb->devinit == 0) || (test != 3)) {
2290
2291 ret |= sisusb_do_init_gfxdevice(sisusb);
2292
2293 if (ret == 0)
2294 sisusb->devinit = 1;
2295
2296 }
2297
2298 if (sisusb->devinit) {
2299 /* Initialize the graphics core */
2300 if (sisusb_init_gfxcore(sisusb) == 0) {
2301 sisusb->gfxinit = 1;
2302 sisusb_get_ramconfig(sisusb);
2303 ret |= sisusb_set_default_mode(sisusb, 1);
2304 ret |= sisusb_setup_screen(sisusb, 1, initscreen);
2305 }
2306 }
2307
2308 return ret;
2309}
2310
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002311
2312#ifdef INCL_SISUSB_CON
2313
2314/* Set up default text mode:
2315 - Set text mode (0x03)
2316 - Upload default font
2317 - Upload user font (if available)
2318*/
2319
2320int
2321sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
2322{
2323 int ret = 0, slot = sisusb->font_slot, i;
Andrew Mortondabb5922005-09-22 23:55:22 -07002324 const struct font_desc *myfont;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002325 u8 *tempbuf;
2326 u16 *tempbufb;
2327 size_t written;
Arjan van de Ven4c4c9432005-11-29 09:43:42 +01002328 static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
2329 static const char bootlogo[] = "(o_ //\\ V_/_";
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002330
2331 /* sisusb->lock is down */
2332
2333 if (!sisusb->SiS_Pr)
2334 return 1;
2335
2336 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2337 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2338
2339 /* Set mode 0x03 */
2340 SiSUSBSetMode(sisusb->SiS_Pr, 0x03);
2341
2342 if (!(myfont = find_font("VGA8x16")))
2343 return 1;
2344
2345 if (!(tempbuf = vmalloc(8192)))
2346 return 1;
2347
2348 for (i = 0; i < 256; i++)
2349 memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16);
2350
2351 /* Upload default font */
2352 ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, 0, 1, NULL, 16, 0);
2353
2354 vfree(tempbuf);
2355
2356 /* Upload user font (and reset current slot) */
2357 if (sisusb->font_backup) {
2358 ret |= sisusbcon_do_font_op(sisusb, 1, 2, sisusb->font_backup,
2359 8192, sisusb->font_backup_512, 1, NULL,
2360 sisusb->font_backup_height, 0);
2361 if (slot != 2)
2362 sisusbcon_do_font_op(sisusb, 1, 0, NULL, 0, 0, 1,
2363 NULL, 16, 0);
2364 }
2365
2366 if (init && !sisusb->scrbuf) {
2367
2368 if ((tempbuf = vmalloc(8192))) {
2369
2370 i = 4096;
2371 tempbufb = (u16 *)tempbuf;
2372 while (i--)
2373 *(tempbufb++) = 0x0720;
2374
2375 i = 0;
2376 tempbufb = (u16 *)tempbuf;
2377 while (bootlogo[i]) {
2378 *(tempbufb++) = 0x0700 | bootlogo[i++];
2379 if (!(i % 4))
2380 tempbufb += 76;
2381 }
2382
2383 i = 0;
2384 tempbufb = (u16 *)tempbuf + 6;
2385 while (bootstring[i])
2386 *(tempbufb++) = 0x0700 | bootstring[i++];
2387
2388 ret |= sisusb_copy_memory(sisusb, tempbuf,
2389 sisusb->vrambase, 8192, &written);
2390
2391 vfree(tempbuf);
2392
2393 }
2394
2395 } else if (sisusb->scrbuf) {
2396
2397 ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf,
2398 sisusb->vrambase, sisusb->scrbuf_size, &written);
2399
2400 }
2401
2402 if (sisusb->sisusb_cursor_size_from >= 0 &&
2403 sisusb->sisusb_cursor_size_to >= 0) {
2404 sisusb_setidxreg(sisusb, SISCR, 0x0a,
2405 sisusb->sisusb_cursor_size_from);
2406 sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0,
2407 sisusb->sisusb_cursor_size_to);
2408 } else {
2409 sisusb_setidxreg(sisusb, SISCR, 0x0a, 0x2d);
2410 sisusb_setidxreg(sisusb, SISCR, 0x0b, 0x0e);
2411 sisusb->sisusb_cursor_size_to = -1;
2412 }
2413
2414 slot = sisusb->sisusb_cursor_loc;
2415 if(slot < 0) slot = 0;
2416
2417 sisusb->sisusb_cursor_loc = -1;
2418 sisusb->bad_cursor_pos = 1;
2419
2420 sisusb_set_cursor(sisusb, slot);
2421
2422 sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
2423 sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
2424
2425 sisusb->textmodedestroyed = 0;
2426
2427 /* sisusb->lock is down */
2428
2429 return ret;
2430}
2431
2432#endif
2433
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434/* fops */
2435
2436static int
2437sisusb_open(struct inode *inode, struct file *file)
2438{
2439 struct sisusb_usb_data *sisusb;
2440 struct usb_interface *interface;
2441 int subminor = iminor(inode);
2442
Satyam Sharmae6d42f02007-09-03 01:37:31 +05302443 if (!(interface = usb_find_interface(&sisusb_driver, subminor)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445
Alan Sternd4ead162007-05-22 11:46:41 -04002446 if (!(sisusb = usb_get_intfdata(interface)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448
Arjan van de Ven2682d272006-03-28 01:00:21 -08002449 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450
2451 if (!sisusb->present || !sisusb->ready) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002452 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 return -ENODEV;
2454 }
2455
2456 if (sisusb->isopen) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002457 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 return -EBUSY;
2459 }
2460
2461 if (!sisusb->devinit) {
2462 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
2463 if (sisusb_init_gfxdevice(sisusb, 0)) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002464 mutex_unlock(&sisusb->lock);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002465 dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 return -EIO;
2467 }
2468 } else {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002469 mutex_unlock(&sisusb->lock);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002470 dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 return -EIO;
2472 }
2473 }
2474
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002475 /* Increment usage count for our sisusb */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 kref_get(&sisusb->kref);
2477
2478 sisusb->isopen = 1;
2479
2480 file->private_data = sisusb;
2481
Arjan van de Ven2682d272006-03-28 01:00:21 -08002482 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 return 0;
2485}
2486
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002487void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488sisusb_delete(struct kref *kref)
2489{
2490 struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
2491
2492 if (!sisusb)
2493 return;
2494
2495 if (sisusb->sisusb_dev)
2496 usb_put_dev(sisusb->sisusb_dev);
2497
2498 sisusb->sisusb_dev = NULL;
2499 sisusb_free_buffers(sisusb);
2500 sisusb_free_urbs(sisusb);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002501#ifdef INCL_SISUSB_CON
2502 kfree(sisusb->SiS_Pr);
2503#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 kfree(sisusb);
2505}
2506
2507static int
2508sisusb_release(struct inode *inode, struct file *file)
2509{
2510 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511
Alan Sternd4ead162007-05-22 11:46:41 -04002512 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514
Arjan van de Ven2682d272006-03-28 01:00:21 -08002515 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516
2517 if (sisusb->present) {
2518 /* Wait for all URBs to finish if device still present */
2519 if (!sisusb_wait_all_out_complete(sisusb))
2520 sisusb_kill_all_busy(sisusb);
2521 }
2522
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 sisusb->isopen = 0;
2524 file->private_data = NULL;
2525
Arjan van de Ven2682d272006-03-28 01:00:21 -08002526 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527
2528 /* decrement the usage count on our device */
2529 kref_put(&sisusb->kref, sisusb_delete);
2530
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 return 0;
2532}
2533
2534static ssize_t
2535sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
2536{
2537 struct sisusb_usb_data *sisusb;
2538 ssize_t bytes_read = 0;
2539 int errno = 0;
2540 u8 buf8;
2541 u16 buf16;
2542 u32 buf32, address;
2543
2544 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2545 return -ENODEV;
2546
Arjan van de Ven2682d272006-03-28 01:00:21 -08002547 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548
2549 /* Sanity check */
2550 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002551 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 return -ENODEV;
2553 }
2554
2555 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2556 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2557
2558 address = (*ppos) -
2559 SISUSB_PCI_PSEUDO_IOPORTBASE +
2560 SISUSB_PCI_IOPORTBASE;
2561
2562 /* Read i/o ports
2563 * Byte, word and long(32) can be read. As this
2564 * emulates inX instructions, the data returned is
2565 * in machine-endianness.
2566 */
2567 switch (count) {
2568
2569 case 1:
2570 if (sisusb_read_memio_byte(sisusb,
2571 SISUSB_TYPE_IO,
2572 address, &buf8))
2573 errno = -EIO;
2574 else if (put_user(buf8, (u8 __user *)buffer))
2575 errno = -EFAULT;
2576 else
2577 bytes_read = 1;
2578
2579 break;
2580
2581 case 2:
2582 if (sisusb_read_memio_word(sisusb,
2583 SISUSB_TYPE_IO,
2584 address, &buf16))
2585 errno = -EIO;
2586 else if (put_user(buf16, (u16 __user *)buffer))
2587 errno = -EFAULT;
2588 else
2589 bytes_read = 2;
2590
2591 break;
2592
2593 case 4:
2594 if (sisusb_read_memio_long(sisusb,
2595 SISUSB_TYPE_IO,
2596 address, &buf32))
2597 errno = -EIO;
2598 else if (put_user(buf32, (u32 __user *)buffer))
2599 errno = -EFAULT;
2600 else
2601 bytes_read = 4;
2602
2603 break;
2604
2605 default:
2606 errno = -EIO;
2607
2608 }
2609
2610 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2611 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2612
2613 address = (*ppos) -
2614 SISUSB_PCI_PSEUDO_MEMBASE +
2615 SISUSB_PCI_MEMBASE;
2616
2617 /* Read video ram
2618 * Remember: Data delivered is never endian-corrected
2619 */
2620 errno = sisusb_read_mem_bulk(sisusb, address,
2621 NULL, count, buffer, &bytes_read);
2622
2623 if (bytes_read)
2624 errno = bytes_read;
2625
2626 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2627 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2628
2629 address = (*ppos) -
2630 SISUSB_PCI_PSEUDO_MMIOBASE +
2631 SISUSB_PCI_MMIOBASE;
2632
2633 /* Read MMIO
2634 * Remember: Data delivered is never endian-corrected
2635 */
2636 errno = sisusb_read_mem_bulk(sisusb, address,
2637 NULL, count, buffer, &bytes_read);
2638
2639 if (bytes_read)
2640 errno = bytes_read;
2641
2642 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2643 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
2644
2645 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002646 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 return -EINVAL;
2648 }
2649
2650 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2651
2652 /* Read PCI config register
2653 * Return value delivered in machine endianness.
2654 */
2655 if (sisusb_read_pci_config(sisusb, address, &buf32))
2656 errno = -EIO;
2657 else if (put_user(buf32, (u32 __user *)buffer))
2658 errno = -EFAULT;
2659 else
2660 bytes_read = 4;
2661
2662 } else {
2663
2664 errno = -EBADFD;
2665
2666 }
2667
2668 (*ppos) += bytes_read;
2669
Arjan van de Ven2682d272006-03-28 01:00:21 -08002670 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671
2672 return errno ? errno : bytes_read;
2673}
2674
2675static ssize_t
2676sisusb_write(struct file *file, const char __user *buffer, size_t count,
2677 loff_t *ppos)
2678{
2679 struct sisusb_usb_data *sisusb;
2680 int errno = 0;
2681 ssize_t bytes_written = 0;
2682 u8 buf8;
2683 u16 buf16;
2684 u32 buf32, address;
2685
2686 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2687 return -ENODEV;
2688
Arjan van de Ven2682d272006-03-28 01:00:21 -08002689 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 /* Sanity check */
2692 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002693 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 return -ENODEV;
2695 }
2696
2697 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2698 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2699
2700 address = (*ppos) -
2701 SISUSB_PCI_PSEUDO_IOPORTBASE +
2702 SISUSB_PCI_IOPORTBASE;
2703
2704 /* Write i/o ports
2705 * Byte, word and long(32) can be written. As this
2706 * emulates outX instructions, the data is expected
2707 * in machine-endianness.
2708 */
2709 switch (count) {
2710
2711 case 1:
2712 if (get_user(buf8, (u8 __user *)buffer))
2713 errno = -EFAULT;
2714 else if (sisusb_write_memio_byte(sisusb,
2715 SISUSB_TYPE_IO,
2716 address, buf8))
2717 errno = -EIO;
2718 else
2719 bytes_written = 1;
2720
2721 break;
2722
2723 case 2:
2724 if (get_user(buf16, (u16 __user *)buffer))
2725 errno = -EFAULT;
2726 else if (sisusb_write_memio_word(sisusb,
2727 SISUSB_TYPE_IO,
2728 address, buf16))
2729 errno = -EIO;
2730 else
2731 bytes_written = 2;
2732
2733 break;
2734
2735 case 4:
2736 if (get_user(buf32, (u32 __user *)buffer))
2737 errno = -EFAULT;
2738 else if (sisusb_write_memio_long(sisusb,
2739 SISUSB_TYPE_IO,
2740 address, buf32))
2741 errno = -EIO;
2742 else
2743 bytes_written = 4;
2744
2745 break;
2746
2747 default:
2748 errno = -EIO;
2749 }
2750
2751 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2752 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2753
2754 address = (*ppos) -
2755 SISUSB_PCI_PSEUDO_MEMBASE +
2756 SISUSB_PCI_MEMBASE;
2757
2758 /* Write video ram.
2759 * Buffer is copied 1:1, therefore, on big-endian
2760 * machines, the data must be swapped by userland
2761 * in advance (if applicable; no swapping in 8bpp
2762 * mode or if YUV data is being transferred).
2763 */
2764 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2765 count, buffer, 0, &bytes_written);
2766
2767 if (bytes_written)
2768 errno = bytes_written;
2769
2770 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2771 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2772
2773 address = (*ppos) -
2774 SISUSB_PCI_PSEUDO_MMIOBASE +
2775 SISUSB_PCI_MMIOBASE;
2776
2777 /* Write MMIO.
2778 * Buffer is copied 1:1, therefore, on big-endian
2779 * machines, the data must be swapped by userland
2780 * in advance.
2781 */
2782 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2783 count, buffer, 0, &bytes_written);
2784
2785 if (bytes_written)
2786 errno = bytes_written;
2787
2788 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2789 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
2790
2791 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002792 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 return -EINVAL;
2794 }
2795
2796 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2797
2798 /* Write PCI config register.
2799 * Given value expected in machine endianness.
2800 */
2801 if (get_user(buf32, (u32 __user *)buffer))
2802 errno = -EFAULT;
2803 else if (sisusb_write_pci_config(sisusb, address, buf32))
2804 errno = -EIO;
2805 else
2806 bytes_written = 4;
2807
2808
2809 } else {
2810
2811 /* Error */
2812 errno = -EBADFD;
2813
2814 }
2815
2816 (*ppos) += bytes_written;
2817
Arjan van de Ven2682d272006-03-28 01:00:21 -08002818 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819
2820 return errno ? errno : bytes_written;
2821}
2822
2823static loff_t
2824sisusb_lseek(struct file *file, loff_t offset, int orig)
2825{
2826 struct sisusb_usb_data *sisusb;
2827 loff_t ret;
2828
2829 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2830 return -ENODEV;
2831
Arjan van de Ven2682d272006-03-28 01:00:21 -08002832 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833
2834 /* Sanity check */
2835 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002836 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837 return -ENODEV;
2838 }
2839
2840 switch (orig) {
2841 case 0:
2842 file->f_pos = offset;
2843 ret = file->f_pos;
2844 /* never negative, no force_successful_syscall needed */
2845 break;
2846 case 1:
2847 file->f_pos += offset;
2848 ret = file->f_pos;
2849 /* never negative, no force_successful_syscall needed */
2850 break;
2851 default:
2852 /* seeking relative to "end of file" is not supported */
2853 ret = -EINVAL;
2854 }
2855
Arjan van de Ven2682d272006-03-28 01:00:21 -08002856 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 return ret;
2858}
2859
2860static int
2861sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
2862 unsigned long arg)
2863{
Felipe Balbied86d972007-08-10 09:34:24 -04002864 int retval, port, length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 u32 address;
2866
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002867 /* All our commands require the device
2868 * to be initialized.
2869 */
2870 if (!sisusb->devinit)
2871 return -ENODEV;
2872
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 port = y->data3 -
2874 SISUSB_PCI_PSEUDO_IOPORTBASE +
2875 SISUSB_PCI_IOPORTBASE;
2876
2877 switch (y->operation) {
2878 case SUCMD_GET:
2879 retval = sisusb_getidxreg(sisusb, port,
2880 y->data0, &y->data1);
2881 if (!retval) {
2882 if (copy_to_user((void __user *)arg, y,
2883 sizeof(*y)))
2884 retval = -EFAULT;
2885 }
2886 break;
2887
2888 case SUCMD_SET:
2889 retval = sisusb_setidxreg(sisusb, port,
2890 y->data0, y->data1);
2891 break;
2892
2893 case SUCMD_SETOR:
2894 retval = sisusb_setidxregor(sisusb, port,
2895 y->data0, y->data1);
2896 break;
2897
2898 case SUCMD_SETAND:
2899 retval = sisusb_setidxregand(sisusb, port,
2900 y->data0, y->data1);
2901 break;
2902
2903 case SUCMD_SETANDOR:
2904 retval = sisusb_setidxregandor(sisusb, port,
2905 y->data0, y->data1, y->data2);
2906 break;
2907
2908 case SUCMD_SETMASK:
2909 retval = sisusb_setidxregmask(sisusb, port,
2910 y->data0, y->data1, y->data2);
2911 break;
2912
2913 case SUCMD_CLRSCR:
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002914 /* Gfx core must be initialized */
2915 if (!sisusb->gfxinit)
2916 return -ENODEV;
2917
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918 length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
2919 address = y->data3 -
2920 SISUSB_PCI_PSEUDO_MEMBASE +
2921 SISUSB_PCI_MEMBASE;
2922 retval = sisusb_clear_vram(sisusb, address, length);
2923 break;
2924
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002925 case SUCMD_HANDLETEXTMODE:
2926 retval = 0;
2927#ifdef INCL_SISUSB_CON
2928 /* Gfx core must be initialized, SiS_Pr must exist */
2929 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2930 return -ENODEV;
2931
2932 switch (y->data0) {
2933 case 0:
2934 retval = sisusb_reset_text_mode(sisusb, 0);
2935 break;
2936 case 1:
2937 sisusb->textmodedestroyed = 1;
2938 break;
2939 }
2940#endif
2941 break;
2942
2943#ifdef INCL_SISUSB_CON
2944 case SUCMD_SETMODE:
2945 /* Gfx core must be initialized, SiS_Pr must exist */
2946 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2947 return -ENODEV;
2948
2949 retval = 0;
2950
2951 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2952 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2953
2954 if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3))
2955 retval = -EINVAL;
2956
2957 break;
2958
2959 case SUCMD_SETVESAMODE:
2960 /* Gfx core must be initialized, SiS_Pr must exist */
2961 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2962 return -ENODEV;
2963
2964 retval = 0;
2965
2966 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2967 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2968
2969 if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3))
2970 retval = -EINVAL;
2971
2972 break;
2973#endif
2974
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 default:
2976 retval = -EINVAL;
2977 }
2978
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002979 if (retval > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002980 retval = -EIO;
2981
2982 return retval;
2983}
2984
Alan Cox49f15252008-05-22 22:48:48 +01002985static long
2986sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987{
2988 struct sisusb_usb_data *sisusb;
2989 struct sisusb_info x;
2990 struct sisusb_command y;
Felipe Balbied86d972007-08-10 09:34:24 -04002991 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992 u32 __user *argp = (u32 __user *)arg;
2993
2994 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2995 return -ENODEV;
2996
Alan Cox49f15252008-05-22 22:48:48 +01002997 lock_kernel();
Arjan van de Ven2682d272006-03-28 01:00:21 -08002998 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999
3000 /* Sanity check */
3001 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
3002 retval = -ENODEV;
3003 goto err_out;
3004 }
3005
3006 switch (cmd) {
3007
3008 case SISUSB_GET_CONFIG_SIZE:
3009
3010 if (put_user(sizeof(x), argp))
3011 retval = -EFAULT;
3012
3013 break;
3014
3015 case SISUSB_GET_CONFIG:
3016
Felipe Balbied86d972007-08-10 09:34:24 -04003017 x.sisusb_id = SISUSB_ID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003018 x.sisusb_version = SISUSB_VERSION;
3019 x.sisusb_revision = SISUSB_REVISION;
3020 x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
3021 x.sisusb_gfxinit = sisusb->gfxinit;
3022 x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE;
3023 x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE;
3024 x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE;
3025 x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE;
3026 x.sisusb_vramsize = sisusb->vramsize;
3027 x.sisusb_minor = sisusb->minor;
3028 x.sisusb_fbdevactive= 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003029#ifdef INCL_SISUSB_CON
3030 x.sisusb_conactive = sisusb->haveconsole ? 1 : 0;
3031#else
3032 x.sisusb_conactive = 0;
3033#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034
3035 if (copy_to_user((void __user *)arg, &x, sizeof(x)))
3036 retval = -EFAULT;
3037
3038 break;
3039
3040 case SISUSB_COMMAND:
3041
3042 if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
3043 retval = -EFAULT;
3044 else
3045 retval = sisusb_handle_command(sisusb, &y, arg);
3046
3047 break;
3048
3049 default:
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003050 retval = -ENOTTY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003051 break;
3052 }
3053
3054err_out:
Arjan van de Ven2682d272006-03-28 01:00:21 -08003055 mutex_unlock(&sisusb->lock);
Alan Cox49f15252008-05-22 22:48:48 +01003056 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 return retval;
3058}
3059
3060#ifdef SISUSB_NEW_CONFIG_COMPAT
3061static long
3062sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
3063{
3064 long retval;
3065
3066 switch (cmd) {
3067 case SISUSB_GET_CONFIG_SIZE:
3068 case SISUSB_GET_CONFIG:
3069 case SISUSB_COMMAND:
Alan Cox49f15252008-05-22 22:48:48 +01003070 retval = sisusb_ioctl(f, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003071 return retval;
3072
3073 default:
3074 return -ENOIOCTLCMD;
3075 }
3076}
3077#endif
3078
Luiz Fernando N. Capitulino066202d2006-08-05 20:37:11 -03003079static const struct file_operations usb_sisusb_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 .owner = THIS_MODULE,
3081 .open = sisusb_open,
3082 .release = sisusb_release,
3083 .read = sisusb_read,
3084 .write = sisusb_write,
Felipe Balbied86d972007-08-10 09:34:24 -04003085 .llseek = sisusb_lseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086#ifdef SISUSB_NEW_CONFIG_COMPAT
3087 .compat_ioctl = sisusb_compat_ioctl,
3088#endif
Alan Cox49f15252008-05-22 22:48:48 +01003089 .unlocked_ioctl = sisusb_ioctl
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090};
3091
3092static struct usb_class_driver usb_sisusb_class = {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003093 .name = "sisusbvga%d",
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003094 .fops = &usb_sisusb_fops,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095 .minor_base = SISUSB_MINOR
3096};
3097
3098static int sisusb_probe(struct usb_interface *intf,
3099 const struct usb_device_id *id)
3100{
3101 struct usb_device *dev = interface_to_usbdev(intf);
3102 struct sisusb_usb_data *sisusb;
3103 int retval = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003105 dev_info(&dev->dev, "USB2VGA dongle found at address %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106 dev->devnum);
3107
3108 /* Allocate memory for our private */
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003109 if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003110 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for private data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003111 return -ENOMEM;
3112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003113 kref_init(&sisusb->kref);
3114
Arjan van de Ven2682d272006-03-28 01:00:21 -08003115 mutex_init(&(sisusb->lock));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116
3117 /* Register device */
3118 if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003119 dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120 dev->devnum);
3121 retval = -ENODEV;
3122 goto error_1;
3123 }
3124
3125 sisusb->sisusb_dev = dev;
3126 sisusb->minor = intf->minor;
3127 sisusb->vrambase = SISUSB_PCI_MEMBASE;
3128 sisusb->mmiobase = SISUSB_PCI_MMIOBASE;
3129 sisusb->mmiosize = SISUSB_PCI_MMIOSIZE;
3130 sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
3131 /* Everything else is zero */
3132
3133 /* Allocate buffers */
3134 sisusb->ibufsize = SISUSB_IBUF_SIZE;
3135 if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
3136 GFP_KERNEL, &sisusb->transfer_dma_in))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003137 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003138 retval = -ENOMEM;
3139 goto error_2;
3140 }
3141
3142 sisusb->numobufs = 0;
3143 sisusb->obufsize = SISUSB_OBUF_SIZE;
3144 for (i = 0; i < NUMOBUFS; i++) {
3145 if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE,
3146 GFP_KERNEL,
3147 &sisusb->transfer_dma_out[i]))) {
3148 if (i == 0) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003149 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150 retval = -ENOMEM;
3151 goto error_3;
3152 }
3153 break;
3154 } else
3155 sisusb->numobufs++;
3156
3157 }
3158
3159 /* Allocate URBs */
3160 if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003161 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003162 retval = -ENOMEM;
3163 goto error_3;
3164 }
3165 sisusb->completein = 1;
3166
3167 for (i = 0; i < sisusb->numobufs; i++) {
3168 if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003169 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170 retval = -ENOMEM;
3171 goto error_4;
3172 }
3173 sisusb->urbout_context[i].sisusb = (void *)sisusb;
3174 sisusb->urbout_context[i].urbindex = i;
3175 sisusb->urbstatus[i] = 0;
3176 }
3177
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003178 dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", sisusb->numobufs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003180#ifdef INCL_SISUSB_CON
3181 /* Allocate our SiS_Pr */
3182 if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003183 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate SiS_Pr\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003184 }
3185#endif
3186
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187 /* Do remaining init stuff */
3188
3189 init_waitqueue_head(&sisusb->wait_q);
3190
3191 usb_set_intfdata(intf, sisusb);
3192
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003193 usb_get_dev(sisusb->sisusb_dev);
3194
3195 sisusb->present = 1;
3196
Linus Torvalds1da177e2005-04-16 15:20:36 -07003197 if (dev->speed == USB_SPEED_HIGH) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003198 int initscreen = 1;
3199#ifdef INCL_SISUSB_CON
3200 if (sisusb_first_vc > 0 &&
3201 sisusb_last_vc > 0 &&
3202 sisusb_first_vc <= sisusb_last_vc &&
3203 sisusb_last_vc <= MAX_NR_CONSOLES)
3204 initscreen = 0;
3205#endif
3206 if (sisusb_init_gfxdevice(sisusb, initscreen))
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003207 dev_err(&sisusb->sisusb_dev->dev, "Failed to early initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208
3209 } else
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003210 dev_info(&sisusb->sisusb_dev->dev, "Not attached to USB 2.0 hub, deferring init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211
3212 sisusb->ready = 1;
3213
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003214#ifdef SISUSBENDIANTEST
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003215 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003216 sisusb_testreadwrite(sisusb);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003217 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003218#endif
3219
3220#ifdef INCL_SISUSB_CON
3221 sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
3222#endif
3223
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224 return 0;
3225
3226error_4:
3227 sisusb_free_urbs(sisusb);
3228error_3:
3229 sisusb_free_buffers(sisusb);
3230error_2:
3231 usb_deregister_dev(intf, &usb_sisusb_class);
3232error_1:
3233 kfree(sisusb);
3234 return retval;
3235}
3236
3237static void sisusb_disconnect(struct usb_interface *intf)
3238{
3239 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003240
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241 /* This should *not* happen */
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003242 if (!(sisusb = usb_get_intfdata(intf)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 return;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003244
3245#ifdef INCL_SISUSB_CON
3246 sisusb_console_exit(sisusb);
3247#endif
3248
Alan Sternd4ead162007-05-22 11:46:41 -04003249 usb_deregister_dev(intf, &usb_sisusb_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003250
Arjan van de Ven2682d272006-03-28 01:00:21 -08003251 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252
3253 /* Wait for all URBs to complete and kill them in case (MUST do) */
3254 if (!sisusb_wait_all_out_complete(sisusb))
3255 sisusb_kill_all_busy(sisusb);
3256
Linus Torvalds1da177e2005-04-16 15:20:36 -07003257 usb_set_intfdata(intf, NULL);
3258
Linus Torvalds1da177e2005-04-16 15:20:36 -07003259 sisusb->present = 0;
3260 sisusb->ready = 0;
3261
Arjan van de Ven2682d272006-03-28 01:00:21 -08003262 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003263
3264 /* decrement our usage count */
3265 kref_put(&sisusb->kref, sisusb_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003266}
3267
3268static struct usb_device_id sisusb_table [] = {
samson yeungca9024e2007-08-31 16:40:40 -04003269 { USB_DEVICE(0x0711, 0x0550) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003270 { USB_DEVICE(0x0711, 0x0900) },
Nobuhiro Iwamatsu3003b9f2006-09-01 11:32:28 +09003271 { USB_DEVICE(0x0711, 0x0901) },
3272 { USB_DEVICE(0x0711, 0x0902) },
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003273 { USB_DEVICE(0x182d, 0x021c) },
Thomas Winischhofercef11122005-04-22 15:06:59 -07003274 { USB_DEVICE(0x182d, 0x0269) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003275 { }
3276};
3277
3278MODULE_DEVICE_TABLE (usb, sisusb_table);
3279
3280static struct usb_driver sisusb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003281 .name = "sisusb",
3282 .probe = sisusb_probe,
3283 .disconnect = sisusb_disconnect,
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003284 .id_table = sisusb_table,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003285};
3286
3287static int __init usb_sisusb_init(void)
3288{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003289
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003290#ifdef INCL_SISUSB_CON
3291 sisusb_init_concode();
3292#endif
3293
Andrew Morton9dcfbd92007-10-02 14:40:46 -07003294 return usb_register(&sisusb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003295}
3296
3297static void __exit usb_sisusb_exit(void)
3298{
3299 usb_deregister(&sisusb_driver);
3300}
3301
3302module_init(usb_sisusb_init);
3303module_exit(usb_sisusb_exit);
3304
3305MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003306MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307MODULE_LICENSE("GPL");
3308