blob: 4d6b89336e6332aef22483a8cf26b6143d9d3863 [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 {
326 /* URB completed within timout */
327 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002444 dev_err(&sisusb->sisusb_dev->dev, "Failed to find interface\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 return -ENODEV;
2446 }
2447
Alan Sternd4ead162007-05-22 11:46:41 -04002448 if (!(sisusb = usb_get_intfdata(interface)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450
Arjan van de Ven2682d272006-03-28 01:00:21 -08002451 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452
2453 if (!sisusb->present || !sisusb->ready) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002454 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455 return -ENODEV;
2456 }
2457
2458 if (sisusb->isopen) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002459 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460 return -EBUSY;
2461 }
2462
2463 if (!sisusb->devinit) {
2464 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
2465 if (sisusb_init_gfxdevice(sisusb, 0)) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002466 mutex_unlock(&sisusb->lock);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002467 dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 return -EIO;
2469 }
2470 } else {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002471 mutex_unlock(&sisusb->lock);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002472 dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473 return -EIO;
2474 }
2475 }
2476
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002477 /* Increment usage count for our sisusb */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 kref_get(&sisusb->kref);
2479
2480 sisusb->isopen = 1;
2481
2482 file->private_data = sisusb;
2483
Arjan van de Ven2682d272006-03-28 01:00:21 -08002484 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486 return 0;
2487}
2488
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002489void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490sisusb_delete(struct kref *kref)
2491{
2492 struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
2493
2494 if (!sisusb)
2495 return;
2496
2497 if (sisusb->sisusb_dev)
2498 usb_put_dev(sisusb->sisusb_dev);
2499
2500 sisusb->sisusb_dev = NULL;
2501 sisusb_free_buffers(sisusb);
2502 sisusb_free_urbs(sisusb);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002503#ifdef INCL_SISUSB_CON
2504 kfree(sisusb->SiS_Pr);
2505#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506 kfree(sisusb);
2507}
2508
2509static int
2510sisusb_release(struct inode *inode, struct file *file)
2511{
2512 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513
Alan Sternd4ead162007-05-22 11:46:41 -04002514 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516
Arjan van de Ven2682d272006-03-28 01:00:21 -08002517 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
2519 if (sisusb->present) {
2520 /* Wait for all URBs to finish if device still present */
2521 if (!sisusb_wait_all_out_complete(sisusb))
2522 sisusb_kill_all_busy(sisusb);
2523 }
2524
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 sisusb->isopen = 0;
2526 file->private_data = NULL;
2527
Arjan van de Ven2682d272006-03-28 01:00:21 -08002528 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
2530 /* decrement the usage count on our device */
2531 kref_put(&sisusb->kref, sisusb_delete);
2532
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533 return 0;
2534}
2535
2536static ssize_t
2537sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
2538{
2539 struct sisusb_usb_data *sisusb;
2540 ssize_t bytes_read = 0;
2541 int errno = 0;
2542 u8 buf8;
2543 u16 buf16;
2544 u32 buf32, address;
2545
2546 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2547 return -ENODEV;
2548
Arjan van de Ven2682d272006-03-28 01:00:21 -08002549 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550
2551 /* Sanity check */
2552 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002553 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 return -ENODEV;
2555 }
2556
2557 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2558 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2559
2560 address = (*ppos) -
2561 SISUSB_PCI_PSEUDO_IOPORTBASE +
2562 SISUSB_PCI_IOPORTBASE;
2563
2564 /* Read i/o ports
2565 * Byte, word and long(32) can be read. As this
2566 * emulates inX instructions, the data returned is
2567 * in machine-endianness.
2568 */
2569 switch (count) {
2570
2571 case 1:
2572 if (sisusb_read_memio_byte(sisusb,
2573 SISUSB_TYPE_IO,
2574 address, &buf8))
2575 errno = -EIO;
2576 else if (put_user(buf8, (u8 __user *)buffer))
2577 errno = -EFAULT;
2578 else
2579 bytes_read = 1;
2580
2581 break;
2582
2583 case 2:
2584 if (sisusb_read_memio_word(sisusb,
2585 SISUSB_TYPE_IO,
2586 address, &buf16))
2587 errno = -EIO;
2588 else if (put_user(buf16, (u16 __user *)buffer))
2589 errno = -EFAULT;
2590 else
2591 bytes_read = 2;
2592
2593 break;
2594
2595 case 4:
2596 if (sisusb_read_memio_long(sisusb,
2597 SISUSB_TYPE_IO,
2598 address, &buf32))
2599 errno = -EIO;
2600 else if (put_user(buf32, (u32 __user *)buffer))
2601 errno = -EFAULT;
2602 else
2603 bytes_read = 4;
2604
2605 break;
2606
2607 default:
2608 errno = -EIO;
2609
2610 }
2611
2612 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2613 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2614
2615 address = (*ppos) -
2616 SISUSB_PCI_PSEUDO_MEMBASE +
2617 SISUSB_PCI_MEMBASE;
2618
2619 /* Read video ram
2620 * Remember: Data delivered is never endian-corrected
2621 */
2622 errno = sisusb_read_mem_bulk(sisusb, address,
2623 NULL, count, buffer, &bytes_read);
2624
2625 if (bytes_read)
2626 errno = bytes_read;
2627
2628 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2629 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2630
2631 address = (*ppos) -
2632 SISUSB_PCI_PSEUDO_MMIOBASE +
2633 SISUSB_PCI_MMIOBASE;
2634
2635 /* Read MMIO
2636 * Remember: Data delivered is never endian-corrected
2637 */
2638 errno = sisusb_read_mem_bulk(sisusb, address,
2639 NULL, count, buffer, &bytes_read);
2640
2641 if (bytes_read)
2642 errno = bytes_read;
2643
2644 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2645 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
2646
2647 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002648 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649 return -EINVAL;
2650 }
2651
2652 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2653
2654 /* Read PCI config register
2655 * Return value delivered in machine endianness.
2656 */
2657 if (sisusb_read_pci_config(sisusb, address, &buf32))
2658 errno = -EIO;
2659 else if (put_user(buf32, (u32 __user *)buffer))
2660 errno = -EFAULT;
2661 else
2662 bytes_read = 4;
2663
2664 } else {
2665
2666 errno = -EBADFD;
2667
2668 }
2669
2670 (*ppos) += bytes_read;
2671
Arjan van de Ven2682d272006-03-28 01:00:21 -08002672 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673
2674 return errno ? errno : bytes_read;
2675}
2676
2677static ssize_t
2678sisusb_write(struct file *file, const char __user *buffer, size_t count,
2679 loff_t *ppos)
2680{
2681 struct sisusb_usb_data *sisusb;
2682 int errno = 0;
2683 ssize_t bytes_written = 0;
2684 u8 buf8;
2685 u16 buf16;
2686 u32 buf32, address;
2687
2688 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2689 return -ENODEV;
2690
Arjan van de Ven2682d272006-03-28 01:00:21 -08002691 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692
2693 /* Sanity check */
2694 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002695 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 return -ENODEV;
2697 }
2698
2699 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2700 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2701
2702 address = (*ppos) -
2703 SISUSB_PCI_PSEUDO_IOPORTBASE +
2704 SISUSB_PCI_IOPORTBASE;
2705
2706 /* Write i/o ports
2707 * Byte, word and long(32) can be written. As this
2708 * emulates outX instructions, the data is expected
2709 * in machine-endianness.
2710 */
2711 switch (count) {
2712
2713 case 1:
2714 if (get_user(buf8, (u8 __user *)buffer))
2715 errno = -EFAULT;
2716 else if (sisusb_write_memio_byte(sisusb,
2717 SISUSB_TYPE_IO,
2718 address, buf8))
2719 errno = -EIO;
2720 else
2721 bytes_written = 1;
2722
2723 break;
2724
2725 case 2:
2726 if (get_user(buf16, (u16 __user *)buffer))
2727 errno = -EFAULT;
2728 else if (sisusb_write_memio_word(sisusb,
2729 SISUSB_TYPE_IO,
2730 address, buf16))
2731 errno = -EIO;
2732 else
2733 bytes_written = 2;
2734
2735 break;
2736
2737 case 4:
2738 if (get_user(buf32, (u32 __user *)buffer))
2739 errno = -EFAULT;
2740 else if (sisusb_write_memio_long(sisusb,
2741 SISUSB_TYPE_IO,
2742 address, buf32))
2743 errno = -EIO;
2744 else
2745 bytes_written = 4;
2746
2747 break;
2748
2749 default:
2750 errno = -EIO;
2751 }
2752
2753 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2754 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2755
2756 address = (*ppos) -
2757 SISUSB_PCI_PSEUDO_MEMBASE +
2758 SISUSB_PCI_MEMBASE;
2759
2760 /* Write video ram.
2761 * Buffer is copied 1:1, therefore, on big-endian
2762 * machines, the data must be swapped by userland
2763 * in advance (if applicable; no swapping in 8bpp
2764 * mode or if YUV data is being transferred).
2765 */
2766 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2767 count, buffer, 0, &bytes_written);
2768
2769 if (bytes_written)
2770 errno = bytes_written;
2771
2772 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2773 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2774
2775 address = (*ppos) -
2776 SISUSB_PCI_PSEUDO_MMIOBASE +
2777 SISUSB_PCI_MMIOBASE;
2778
2779 /* Write MMIO.
2780 * Buffer is copied 1:1, therefore, on big-endian
2781 * machines, the data must be swapped by userland
2782 * in advance.
2783 */
2784 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2785 count, buffer, 0, &bytes_written);
2786
2787 if (bytes_written)
2788 errno = bytes_written;
2789
2790 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2791 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
2792
2793 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002794 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 return -EINVAL;
2796 }
2797
2798 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2799
2800 /* Write PCI config register.
2801 * Given value expected in machine endianness.
2802 */
2803 if (get_user(buf32, (u32 __user *)buffer))
2804 errno = -EFAULT;
2805 else if (sisusb_write_pci_config(sisusb, address, buf32))
2806 errno = -EIO;
2807 else
2808 bytes_written = 4;
2809
2810
2811 } else {
2812
2813 /* Error */
2814 errno = -EBADFD;
2815
2816 }
2817
2818 (*ppos) += bytes_written;
2819
Arjan van de Ven2682d272006-03-28 01:00:21 -08002820 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821
2822 return errno ? errno : bytes_written;
2823}
2824
2825static loff_t
2826sisusb_lseek(struct file *file, loff_t offset, int orig)
2827{
2828 struct sisusb_usb_data *sisusb;
2829 loff_t ret;
2830
2831 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2832 return -ENODEV;
2833
Arjan van de Ven2682d272006-03-28 01:00:21 -08002834 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835
2836 /* Sanity check */
2837 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002838 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 return -ENODEV;
2840 }
2841
2842 switch (orig) {
2843 case 0:
2844 file->f_pos = offset;
2845 ret = file->f_pos;
2846 /* never negative, no force_successful_syscall needed */
2847 break;
2848 case 1:
2849 file->f_pos += offset;
2850 ret = file->f_pos;
2851 /* never negative, no force_successful_syscall needed */
2852 break;
2853 default:
2854 /* seeking relative to "end of file" is not supported */
2855 ret = -EINVAL;
2856 }
2857
Arjan van de Ven2682d272006-03-28 01:00:21 -08002858 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 return ret;
2860}
2861
2862static int
2863sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
2864 unsigned long arg)
2865{
Felipe Balbied86d972007-08-10 09:34:24 -04002866 int retval, port, length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867 u32 address;
2868
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002869 /* All our commands require the device
2870 * to be initialized.
2871 */
2872 if (!sisusb->devinit)
2873 return -ENODEV;
2874
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 port = y->data3 -
2876 SISUSB_PCI_PSEUDO_IOPORTBASE +
2877 SISUSB_PCI_IOPORTBASE;
2878
2879 switch (y->operation) {
2880 case SUCMD_GET:
2881 retval = sisusb_getidxreg(sisusb, port,
2882 y->data0, &y->data1);
2883 if (!retval) {
2884 if (copy_to_user((void __user *)arg, y,
2885 sizeof(*y)))
2886 retval = -EFAULT;
2887 }
2888 break;
2889
2890 case SUCMD_SET:
2891 retval = sisusb_setidxreg(sisusb, port,
2892 y->data0, y->data1);
2893 break;
2894
2895 case SUCMD_SETOR:
2896 retval = sisusb_setidxregor(sisusb, port,
2897 y->data0, y->data1);
2898 break;
2899
2900 case SUCMD_SETAND:
2901 retval = sisusb_setidxregand(sisusb, port,
2902 y->data0, y->data1);
2903 break;
2904
2905 case SUCMD_SETANDOR:
2906 retval = sisusb_setidxregandor(sisusb, port,
2907 y->data0, y->data1, y->data2);
2908 break;
2909
2910 case SUCMD_SETMASK:
2911 retval = sisusb_setidxregmask(sisusb, port,
2912 y->data0, y->data1, y->data2);
2913 break;
2914
2915 case SUCMD_CLRSCR:
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002916 /* Gfx core must be initialized */
2917 if (!sisusb->gfxinit)
2918 return -ENODEV;
2919
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920 length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
2921 address = y->data3 -
2922 SISUSB_PCI_PSEUDO_MEMBASE +
2923 SISUSB_PCI_MEMBASE;
2924 retval = sisusb_clear_vram(sisusb, address, length);
2925 break;
2926
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002927 case SUCMD_HANDLETEXTMODE:
2928 retval = 0;
2929#ifdef INCL_SISUSB_CON
2930 /* Gfx core must be initialized, SiS_Pr must exist */
2931 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2932 return -ENODEV;
2933
2934 switch (y->data0) {
2935 case 0:
2936 retval = sisusb_reset_text_mode(sisusb, 0);
2937 break;
2938 case 1:
2939 sisusb->textmodedestroyed = 1;
2940 break;
2941 }
2942#endif
2943 break;
2944
2945#ifdef INCL_SISUSB_CON
2946 case SUCMD_SETMODE:
2947 /* Gfx core must be initialized, SiS_Pr must exist */
2948 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2949 return -ENODEV;
2950
2951 retval = 0;
2952
2953 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2954 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2955
2956 if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3))
2957 retval = -EINVAL;
2958
2959 break;
2960
2961 case SUCMD_SETVESAMODE:
2962 /* Gfx core must be initialized, SiS_Pr must exist */
2963 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2964 return -ENODEV;
2965
2966 retval = 0;
2967
2968 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2969 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2970
2971 if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3))
2972 retval = -EINVAL;
2973
2974 break;
2975#endif
2976
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977 default:
2978 retval = -EINVAL;
2979 }
2980
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002981 if (retval > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002982 retval = -EIO;
2983
2984 return retval;
2985}
2986
2987static int
2988sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
Felipe Balbied86d972007-08-10 09:34:24 -04002989 unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990{
2991 struct sisusb_usb_data *sisusb;
2992 struct sisusb_info x;
2993 struct sisusb_command y;
Felipe Balbied86d972007-08-10 09:34:24 -04002994 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995 u32 __user *argp = (u32 __user *)arg;
2996
2997 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2998 return -ENODEV;
2999
Arjan van de Ven2682d272006-03-28 01:00:21 -08003000 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003001
3002 /* Sanity check */
3003 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
3004 retval = -ENODEV;
3005 goto err_out;
3006 }
3007
3008 switch (cmd) {
3009
3010 case SISUSB_GET_CONFIG_SIZE:
3011
3012 if (put_user(sizeof(x), argp))
3013 retval = -EFAULT;
3014
3015 break;
3016
3017 case SISUSB_GET_CONFIG:
3018
Felipe Balbied86d972007-08-10 09:34:24 -04003019 x.sisusb_id = SISUSB_ID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020 x.sisusb_version = SISUSB_VERSION;
3021 x.sisusb_revision = SISUSB_REVISION;
3022 x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
3023 x.sisusb_gfxinit = sisusb->gfxinit;
3024 x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE;
3025 x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE;
3026 x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE;
3027 x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE;
3028 x.sisusb_vramsize = sisusb->vramsize;
3029 x.sisusb_minor = sisusb->minor;
3030 x.sisusb_fbdevactive= 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003031#ifdef INCL_SISUSB_CON
3032 x.sisusb_conactive = sisusb->haveconsole ? 1 : 0;
3033#else
3034 x.sisusb_conactive = 0;
3035#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036
3037 if (copy_to_user((void __user *)arg, &x, sizeof(x)))
3038 retval = -EFAULT;
3039
3040 break;
3041
3042 case SISUSB_COMMAND:
3043
3044 if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
3045 retval = -EFAULT;
3046 else
3047 retval = sisusb_handle_command(sisusb, &y, arg);
3048
3049 break;
3050
3051 default:
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003052 retval = -ENOTTY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003053 break;
3054 }
3055
3056err_out:
Arjan van de Ven2682d272006-03-28 01:00:21 -08003057 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058 return retval;
3059}
3060
3061#ifdef SISUSB_NEW_CONFIG_COMPAT
3062static long
3063sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
3064{
3065 long retval;
3066
3067 switch (cmd) {
3068 case SISUSB_GET_CONFIG_SIZE:
3069 case SISUSB_GET_CONFIG:
3070 case SISUSB_COMMAND:
3071 lock_kernel();
Josef Sipek33cb8992006-12-08 02:37:46 -08003072 retval = sisusb_ioctl(f->f_path.dentry->d_inode, f, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003073 unlock_kernel();
3074 return retval;
3075
3076 default:
3077 return -ENOIOCTLCMD;
3078 }
3079}
3080#endif
3081
Luiz Fernando N. Capitulino066202d2006-08-05 20:37:11 -03003082static const struct file_operations usb_sisusb_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083 .owner = THIS_MODULE,
3084 .open = sisusb_open,
3085 .release = sisusb_release,
3086 .read = sisusb_read,
3087 .write = sisusb_write,
Felipe Balbied86d972007-08-10 09:34:24 -04003088 .llseek = sisusb_lseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003089#ifdef SISUSB_NEW_CONFIG_COMPAT
3090 .compat_ioctl = sisusb_compat_ioctl,
3091#endif
3092 .ioctl = sisusb_ioctl
3093};
3094
3095static struct usb_class_driver usb_sisusb_class = {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003096 .name = "sisusbvga%d",
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003097 .fops = &usb_sisusb_fops,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003098 .minor_base = SISUSB_MINOR
3099};
3100
3101static int sisusb_probe(struct usb_interface *intf,
3102 const struct usb_device_id *id)
3103{
3104 struct usb_device *dev = interface_to_usbdev(intf);
3105 struct sisusb_usb_data *sisusb;
3106 int retval = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003108 dev_info(&dev->dev, "USB2VGA dongle found at address %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 dev->devnum);
3110
3111 /* Allocate memory for our private */
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003112 if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003113 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for private data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114 return -ENOMEM;
3115 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116 kref_init(&sisusb->kref);
3117
Arjan van de Ven2682d272006-03-28 01:00:21 -08003118 mutex_init(&(sisusb->lock));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119
3120 /* Register device */
3121 if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003122 dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003123 dev->devnum);
3124 retval = -ENODEV;
3125 goto error_1;
3126 }
3127
3128 sisusb->sisusb_dev = dev;
3129 sisusb->minor = intf->minor;
3130 sisusb->vrambase = SISUSB_PCI_MEMBASE;
3131 sisusb->mmiobase = SISUSB_PCI_MMIOBASE;
3132 sisusb->mmiosize = SISUSB_PCI_MMIOSIZE;
3133 sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
3134 /* Everything else is zero */
3135
3136 /* Allocate buffers */
3137 sisusb->ibufsize = SISUSB_IBUF_SIZE;
3138 if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
3139 GFP_KERNEL, &sisusb->transfer_dma_in))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003140 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 retval = -ENOMEM;
3142 goto error_2;
3143 }
3144
3145 sisusb->numobufs = 0;
3146 sisusb->obufsize = SISUSB_OBUF_SIZE;
3147 for (i = 0; i < NUMOBUFS; i++) {
3148 if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE,
3149 GFP_KERNEL,
3150 &sisusb->transfer_dma_out[i]))) {
3151 if (i == 0) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003152 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003153 retval = -ENOMEM;
3154 goto error_3;
3155 }
3156 break;
3157 } else
3158 sisusb->numobufs++;
3159
3160 }
3161
3162 /* Allocate URBs */
3163 if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003164 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165 retval = -ENOMEM;
3166 goto error_3;
3167 }
3168 sisusb->completein = 1;
3169
3170 for (i = 0; i < sisusb->numobufs; i++) {
3171 if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003172 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173 retval = -ENOMEM;
3174 goto error_4;
3175 }
3176 sisusb->urbout_context[i].sisusb = (void *)sisusb;
3177 sisusb->urbout_context[i].urbindex = i;
3178 sisusb->urbstatus[i] = 0;
3179 }
3180
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003181 dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", sisusb->numobufs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003183#ifdef INCL_SISUSB_CON
3184 /* Allocate our SiS_Pr */
3185 if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003186 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate SiS_Pr\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003187 }
3188#endif
3189
Linus Torvalds1da177e2005-04-16 15:20:36 -07003190 /* Do remaining init stuff */
3191
3192 init_waitqueue_head(&sisusb->wait_q);
3193
3194 usb_set_intfdata(intf, sisusb);
3195
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003196 usb_get_dev(sisusb->sisusb_dev);
3197
3198 sisusb->present = 1;
3199
Linus Torvalds1da177e2005-04-16 15:20:36 -07003200#ifdef SISUSB_OLD_CONFIG_COMPAT
3201 {
3202 int ret;
3203 /* Our ioctls are all "32/64bit compatible" */
3204 ret = register_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE, NULL);
3205 ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG, NULL);
3206 ret |= register_ioctl32_conversion(SISUSB_COMMAND, NULL);
3207 if (ret)
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003208 dev_err(&sisusb->sisusb_dev->dev, "Error registering ioctl32 translations\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 else
3210 sisusb->ioctl32registered = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211 }
3212#endif
3213
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 if (dev->speed == USB_SPEED_HIGH) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003215 int initscreen = 1;
3216#ifdef INCL_SISUSB_CON
3217 if (sisusb_first_vc > 0 &&
3218 sisusb_last_vc > 0 &&
3219 sisusb_first_vc <= sisusb_last_vc &&
3220 sisusb_last_vc <= MAX_NR_CONSOLES)
3221 initscreen = 0;
3222#endif
3223 if (sisusb_init_gfxdevice(sisusb, initscreen))
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003224 dev_err(&sisusb->sisusb_dev->dev, "Failed to early initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003225
3226 } else
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003227 dev_info(&sisusb->sisusb_dev->dev, "Not attached to USB 2.0 hub, deferring init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228
3229 sisusb->ready = 1;
3230
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003231#ifdef SISUSBENDIANTEST
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003232 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003233 sisusb_testreadwrite(sisusb);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003234 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003235#endif
3236
3237#ifdef INCL_SISUSB_CON
3238 sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
3239#endif
3240
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241 return 0;
3242
3243error_4:
3244 sisusb_free_urbs(sisusb);
3245error_3:
3246 sisusb_free_buffers(sisusb);
3247error_2:
3248 usb_deregister_dev(intf, &usb_sisusb_class);
3249error_1:
3250 kfree(sisusb);
3251 return retval;
3252}
3253
3254static void sisusb_disconnect(struct usb_interface *intf)
3255{
3256 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003257
Linus Torvalds1da177e2005-04-16 15:20:36 -07003258 /* This should *not* happen */
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003259 if (!(sisusb = usb_get_intfdata(intf)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003260 return;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003261
3262#ifdef INCL_SISUSB_CON
3263 sisusb_console_exit(sisusb);
3264#endif
3265
Alan Sternd4ead162007-05-22 11:46:41 -04003266 usb_deregister_dev(intf, &usb_sisusb_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003267
Arjan van de Ven2682d272006-03-28 01:00:21 -08003268 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003269
3270 /* Wait for all URBs to complete and kill them in case (MUST do) */
3271 if (!sisusb_wait_all_out_complete(sisusb))
3272 sisusb_kill_all_busy(sisusb);
3273
Linus Torvalds1da177e2005-04-16 15:20:36 -07003274 usb_set_intfdata(intf, NULL);
3275
Linus Torvalds1da177e2005-04-16 15:20:36 -07003276#ifdef SISUSB_OLD_CONFIG_COMPAT
3277 if (sisusb->ioctl32registered) {
3278 int ret;
3279 sisusb->ioctl32registered = 0;
3280 ret = unregister_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE);
3281 ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG);
3282 ret |= unregister_ioctl32_conversion(SISUSB_COMMAND);
3283 if (ret) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003284 dev_err(&sisusb->sisusb_dev->dev, "Error unregistering ioctl32 translations\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003285 }
3286 }
3287#endif
3288
3289 sisusb->present = 0;
3290 sisusb->ready = 0;
3291
Arjan van de Ven2682d272006-03-28 01:00:21 -08003292 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293
3294 /* decrement our usage count */
3295 kref_put(&sisusb->kref, sisusb_delete);
3296
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003297 dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003298}
3299
3300static struct usb_device_id sisusb_table [] = {
samson yeungca9024e2007-08-31 16:40:40 -04003301 { USB_DEVICE(0x0711, 0x0550) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003302 { USB_DEVICE(0x0711, 0x0900) },
Nobuhiro Iwamatsu3003b9f2006-09-01 11:32:28 +09003303 { USB_DEVICE(0x0711, 0x0901) },
3304 { USB_DEVICE(0x0711, 0x0902) },
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003305 { USB_DEVICE(0x182d, 0x021c) },
Thomas Winischhofercef11122005-04-22 15:06:59 -07003306 { USB_DEVICE(0x182d, 0x0269) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307 { }
3308};
3309
3310MODULE_DEVICE_TABLE (usb, sisusb_table);
3311
3312static struct usb_driver sisusb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313 .name = "sisusb",
3314 .probe = sisusb_probe,
3315 .disconnect = sisusb_disconnect,
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003316 .id_table = sisusb_table,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317};
3318
3319static int __init usb_sisusb_init(void)
3320{
3321 int retval;
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003322 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003324#ifdef INCL_SISUSB_CON
3325 sisusb_init_concode();
3326#endif
3327
Linus Torvalds1da177e2005-04-16 15:20:36 -07003328 if (!(retval = usb_register(&sisusb_driver))) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003329
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003330 dev_info(&sisusb->sisusb_dev->dev, "Driver version %d.%d.%d\n", SISUSB_VERSION,
3331 SISUSB_REVISION, SISUSB_PATCHLEVEL);
3332 dev_info(&sisusb->sisusb_dev->dev, "sisusb: Copyright (C) 2005 Thomas Winischhofer\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003333
Linus Torvalds1da177e2005-04-16 15:20:36 -07003334 }
3335
3336 return retval;
3337}
3338
3339static void __exit usb_sisusb_exit(void)
3340{
3341 usb_deregister(&sisusb_driver);
3342}
3343
3344module_init(usb_sisusb_init);
3345module_exit(usb_sisusb_exit);
3346
3347MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003348MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003349MODULE_LICENSE("GPL");
3350