blob: 3991655f8f09bf5612a2ca93fb11d73274a96d86 [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]) {
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -060082 kfree(sisusb->obuf[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 sisusb->obuf[i] = NULL;
84 }
85 }
86 if (sisusb->ibuf) {
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -060087 kfree(sisusb->ibuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 sisusb->ibuf = NULL;
89 }
90}
91
92static void
93sisusb_free_urbs(struct sisusb_usb_data *sisusb)
94{
95 int i;
96
97 for (i = 0; i < NUMOBUFS; i++) {
98 usb_free_urb(sisusb->sisurbout[i]);
99 sisusb->sisurbout[i] = NULL;
100 }
101 usb_free_urb(sisusb->sisurbin);
102 sisusb->sisurbin = NULL;
103}
104
105/* Level 0: USB transport layer */
106
107/* 1. out-bulks */
108
109/* out-urb management */
110
111/* Return 1 if all free, 0 otherwise */
112static int
113sisusb_all_free(struct sisusb_usb_data *sisusb)
114{
115 int i;
116
117 for (i = 0; i < sisusb->numobufs; i++) {
118
119 if (sisusb->urbstatus[i] & SU_URB_BUSY)
120 return 0;
121
122 }
123
124 return 1;
125}
126
127/* Kill all busy URBs */
128static void
129sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
130{
131 int i;
132
133 if (sisusb_all_free(sisusb))
134 return;
135
136 for (i = 0; i < sisusb->numobufs; i++) {
137
138 if (sisusb->urbstatus[i] & SU_URB_BUSY)
139 usb_kill_urb(sisusb->sisurbout[i]);
140
141 }
142}
143
144/* Return 1 if ok, 0 if error (not all complete within timeout) */
145static int
146sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
147{
148 int timeout = 5 * HZ, i = 1;
149
150 wait_event_timeout(sisusb->wait_q,
151 (i = sisusb_all_free(sisusb)),
152 timeout);
153
154 return i;
155}
156
157static int
158sisusb_outurb_available(struct sisusb_usb_data *sisusb)
159{
160 int i;
161
162 for (i = 0; i < sisusb->numobufs; i++) {
163
164 if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
165 return i;
166
167 }
168
169 return -1;
170}
171
172static int
173sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
174{
175 int i, timeout = 5 * HZ;
176
177 wait_event_timeout(sisusb->wait_q,
178 ((i = sisusb_outurb_available(sisusb)) >= 0),
179 timeout);
180
181 return i;
182}
183
184static int
185sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
186{
187 int i;
188
189 i = sisusb_outurb_available(sisusb);
190
191 if (i >= 0)
192 sisusb->urbstatus[i] |= SU_URB_ALLOC;
193
194 return i;
195}
196
197static void
198sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
199{
200 if ((index >= 0) && (index < sisusb->numobufs))
201 sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
202}
203
204/* completion callback */
205
206static void
David Howells7d12e782006-10-05 14:55:46 +0100207sisusb_bulk_completeout(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
209 struct sisusb_urb_context *context = urb->context;
210 struct sisusb_usb_data *sisusb;
211
212 if (!context)
213 return;
214
215 sisusb = context->sisusb;
216
217 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
218 return;
219
220#ifndef SISUSB_DONTSYNC
221 if (context->actual_length)
222 *(context->actual_length) += urb->actual_length;
223#endif
224
225 sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
226 wake_up(&sisusb->wait_q);
227}
228
229static int
230sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600231 int len, int *actual_length, int timeout, unsigned int tflags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232{
233 struct urb *urb = sisusb->sisurbout[index];
234 int retval, byteswritten = 0;
235
236 /* Set up URB */
237 urb->transfer_flags = 0;
238
239 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
240 sisusb_bulk_completeout, &sisusb->urbout_context[index]);
241
Alan Sternb375a042005-07-29 16:11:07 -0400242 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 urb->actual_length = 0;
244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 /* Set up context */
246 sisusb->urbout_context[index].actual_length = (timeout) ?
247 NULL : actual_length;
248
249 /* Declare this urb/buffer in use */
250 sisusb->urbstatus[index] |= SU_URB_BUSY;
251
252 /* Submit URB */
253 retval = usb_submit_urb(urb, GFP_ATOMIC);
254
255 /* If OK, and if timeout > 0, wait for completion */
256 if ((retval == 0) && timeout) {
257 wait_event_timeout(sisusb->wait_q,
258 (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
259 timeout);
260 if (sisusb->urbstatus[index] & SU_URB_BUSY) {
261 /* URB timed out... kill it and report error */
262 usb_kill_urb(urb);
263 retval = -ETIMEDOUT;
264 } else {
265 /* Otherwise, report urb status */
266 retval = urb->status;
267 byteswritten = urb->actual_length;
268 }
269 }
270
271 if (actual_length)
272 *actual_length = byteswritten;
273
274 return retval;
275}
276
277/* 2. in-bulks */
278
279/* completion callback */
280
281static void
David Howells7d12e782006-10-05 14:55:46 +0100282sisusb_bulk_completein(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 struct sisusb_usb_data *sisusb = urb->context;
285
286 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
287 return;
288
289 sisusb->completein = 1;
290 wake_up(&sisusb->wait_q);
291}
292
293static int
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600294sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data,
295 int len, int *actual_length, int timeout, unsigned int tflags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 struct urb *urb = sisusb->sisurbin;
298 int retval, readbytes = 0;
299
300 urb->transfer_flags = 0;
301
302 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
303 sisusb_bulk_completein, sisusb);
304
Alan Sternb375a042005-07-29 16:11:07 -0400305 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 urb->actual_length = 0;
307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 sisusb->completein = 0;
309 retval = usb_submit_urb(urb, GFP_ATOMIC);
310 if (retval == 0) {
311 wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
312 if (!sisusb->completein) {
313 /* URB timed out... kill it and report error */
314 usb_kill_urb(urb);
315 retval = -ETIMEDOUT;
316 } else {
Joe Perchesdc0d5c12007-12-17 11:40:18 -0800317 /* URB completed within timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 retval = urb->status;
319 readbytes = urb->actual_length;
320 }
321 }
322
323 if (actual_length)
324 *actual_length = readbytes;
325
326 return retval;
327}
328
329
330/* Level 1: */
331
332/* Send a bulk message of variable size
333 *
334 * To copy the data from userspace, give pointer to "userbuffer",
335 * to copy from (non-DMA) kernel memory, give "kernbuffer". If
336 * both of these are NULL, it is assumed, that the transfer
337 * buffer "sisusb->obuf[index]" is set up with the data to send.
338 * Index is ignored if either kernbuffer or userbuffer is set.
339 * If async is nonzero, URBs will be sent without waiting for
340 * completion of the previous URB.
341 *
342 * (return 0 on success)
343 */
344
345static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
346 char *kernbuffer, const char __user *userbuffer, int index,
347 ssize_t *bytes_written, unsigned int tflags, int async)
348{
349 int result = 0, retry, count = len;
350 int passsize, thispass, transferred_len = 0;
351 int fromuser = (userbuffer != NULL) ? 1 : 0;
352 int fromkern = (kernbuffer != NULL) ? 1 : 0;
353 unsigned int pipe;
354 char *buffer;
355
356 (*bytes_written) = 0;
357
358 /* Sanity check */
359 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
360 return -ENODEV;
361
362 /* If we copy data from kernel or userspace, force the
363 * allocation of a buffer/urb. If we have the data in
364 * the transfer buffer[index] already, reuse the buffer/URB
365 * if the length is > buffer size. (So, transmitting
366 * large data amounts directly from the transfer buffer
367 * treats the buffer as a ring buffer. However, we need
368 * to sync in this case.)
369 */
370 if (fromuser || fromkern)
371 index = -1;
372 else if (len > sisusb->obufsize)
373 async = 0;
374
375 pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
376
377 do {
378 passsize = thispass = (sisusb->obufsize < count) ?
379 sisusb->obufsize : count;
380
381 if (index < 0)
382 index = sisusb_get_free_outbuf(sisusb);
383
384 if (index < 0)
385 return -EIO;
386
387 buffer = sisusb->obuf[index];
388
389 if (fromuser) {
390
391 if (copy_from_user(buffer, userbuffer, passsize))
392 return -EFAULT;
393
394 userbuffer += passsize;
395
396 } else if (fromkern) {
397
398 memcpy(buffer, kernbuffer, passsize);
399 kernbuffer += passsize;
400
401 }
402
403 retry = 5;
404 while (thispass) {
405
406 if (!sisusb->sisusb_dev)
407 return -ENODEV;
408
409 result = sisusb_bulkout_msg(sisusb,
410 index,
411 pipe,
412 buffer,
413 thispass,
414 &transferred_len,
415 async ? 0 : 5 * HZ,
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600416 tflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 if (result == -ETIMEDOUT) {
419
420 /* Will not happen if async */
421 if (!retry--)
422 return -ETIME;
423
424 continue;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600427 if ((result == 0) && !async && transferred_len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
429 thispass -= transferred_len;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600430 buffer += transferred_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
432 } else
433 break;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600434 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 if (result)
437 return result;
438
439 (*bytes_written) += passsize;
440 count -= passsize;
441
442 /* Force new allocation in next iteration */
443 if (fromuser || fromkern)
444 index = -1;
445
446 } while (count > 0);
447
448 if (async) {
449#ifdef SISUSB_DONTSYNC
450 (*bytes_written) = len;
451 /* Some URBs/buffers might be busy */
452#else
453 sisusb_wait_all_out_complete(sisusb);
454 (*bytes_written) = transferred_len;
455 /* All URBs and all buffers are available */
456#endif
457 }
458
459 return ((*bytes_written) == len) ? 0 : -EIO;
460}
461
462/* Receive a bulk message of variable size
463 *
464 * To copy the data to userspace, give pointer to "userbuffer",
465 * to copy to kernel memory, give "kernbuffer". One of them
466 * MUST be set. (There is no technique for letting the caller
467 * read directly from the ibuf.)
468 *
469 */
470
471static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
472 void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
473 unsigned int tflags)
474{
475 int result = 0, retry, count = len;
476 int bufsize, thispass, transferred_len;
477 unsigned int pipe;
478 char *buffer;
479
480 (*bytes_read) = 0;
481
482 /* Sanity check */
483 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
484 return -ENODEV;
485
486 pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
487 buffer = sisusb->ibuf;
488 bufsize = sisusb->ibufsize;
489
490 retry = 5;
491
492#ifdef SISUSB_DONTSYNC
493 if (!(sisusb_wait_all_out_complete(sisusb)))
494 return -EIO;
495#endif
496
497 while (count > 0) {
498
499 if (!sisusb->sisusb_dev)
500 return -ENODEV;
501
502 thispass = (bufsize < count) ? bufsize : count;
503
504 result = sisusb_bulkin_msg(sisusb,
505 pipe,
506 buffer,
507 thispass,
508 &transferred_len,
509 5 * HZ,
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600510 tflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 if (transferred_len)
513 thispass = transferred_len;
514
515 else if (result == -ETIMEDOUT) {
516
517 if (!retry--)
518 return -ETIME;
519
520 continue;
521
522 } else
523 return -EIO;
524
525
526 if (thispass) {
527
528 (*bytes_read) += thispass;
529 count -= thispass;
530
531 if (userbuffer) {
532
533 if (copy_to_user(userbuffer, buffer, thispass))
534 return -EFAULT;
535
536 userbuffer += thispass;
537
538 } else {
539
540 memcpy(kernbuffer, buffer, thispass);
541 kernbuffer += thispass;
542
543 }
544
545 }
546
547 }
548
549 return ((*bytes_read) == len) ? 0 : -EIO;
550}
551
552static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
553 struct sisusb_packet *packet)
554{
555 int ret;
556 ssize_t bytes_transferred = 0;
557 __le32 tmp;
558
559 if (len == 6)
560 packet->data = 0;
561
562#ifdef SISUSB_DONTSYNC
563 if (!(sisusb_wait_all_out_complete(sisusb)))
564 return 1;
565#endif
566
567 /* Eventually correct endianness */
568 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
569
570 /* 1. send the packet */
571 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
572 (char *)packet, NULL, 0, &bytes_transferred, 0, 0);
573
574 if ((ret == 0) && (len == 6)) {
575
576 /* 2. if packet len == 6, it means we read, so wait for 32bit
577 * return value and write it to packet->data
578 */
579 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
580 (char *)&tmp, NULL, &bytes_transferred, 0);
581
582 packet->data = le32_to_cpu(tmp);
583 }
584
585 return ret;
586}
587
588static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
589 struct sisusb_packet *packet,
590 unsigned int tflags)
591{
592 int ret;
593 ssize_t bytes_transferred = 0;
594 __le32 tmp;
595
596 if (len == 6)
597 packet->data = 0;
598
599#ifdef SISUSB_DONTSYNC
600 if (!(sisusb_wait_all_out_complete(sisusb)))
601 return 1;
602#endif
603
604 /* Eventually correct endianness */
605 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
606
607 /* 1. send the packet */
608 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
609 (char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
610
611 if ((ret == 0) && (len == 6)) {
612
613 /* 2. if packet len == 6, it means we read, so wait for 32bit
614 * return value and write it to packet->data
615 */
616 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
617 (char *)&tmp, NULL, &bytes_transferred, 0);
618
619 packet->data = le32_to_cpu(tmp);
620 }
621
622 return ret;
623}
624
625/* access video memory and mmio (return 0 on success) */
626
627/* Low level */
628
629/* The following routines assume being used to transfer byte, word,
630 * long etc.
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200631 * This means that
632 * - the write routines expect "data" in machine endianness format.
633 * The data will be converted to leXX in sisusb_xxx_packet.
634 * - the read routines can expect read data in machine-endianess.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 */
636
637static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
638 u32 addr, u8 data)
639{
640 struct sisusb_packet packet;
641 int ret;
642
643 packet.header = (1 << (addr & 3)) | (type << 6);
644 packet.address = addr & ~3;
645 packet.data = data << ((addr & 3) << 3);
646 ret = sisusb_send_packet(sisusb, 10, &packet);
647 return ret;
648}
649
650static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
651 u32 addr, u16 data)
652{
653 struct sisusb_packet packet;
654 int ret = 0;
655
656 packet.address = addr & ~3;
657
658 switch (addr & 3) {
659 case 0:
660 packet.header = (type << 6) | 0x0003;
661 packet.data = (u32)data;
662 ret = sisusb_send_packet(sisusb, 10, &packet);
663 break;
664 case 1:
665 packet.header = (type << 6) | 0x0006;
666 packet.data = (u32)data << 8;
667 ret = sisusb_send_packet(sisusb, 10, &packet);
668 break;
669 case 2:
670 packet.header = (type << 6) | 0x000c;
671 packet.data = (u32)data << 16;
672 ret = sisusb_send_packet(sisusb, 10, &packet);
673 break;
674 case 3:
675 packet.header = (type << 6) | 0x0008;
676 packet.data = (u32)data << 24;
677 ret = sisusb_send_packet(sisusb, 10, &packet);
678 packet.header = (type << 6) | 0x0001;
679 packet.address = (addr & ~3) + 4;
680 packet.data = (u32)data >> 8;
681 ret |= sisusb_send_packet(sisusb, 10, &packet);
682 }
683
684 return ret;
685}
686
687static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
688 u32 addr, u32 data)
689{
690 struct sisusb_packet packet;
691 int ret = 0;
692
693 packet.address = addr & ~3;
694
695 switch (addr & 3) {
696 case 0:
697 packet.header = (type << 6) | 0x0007;
698 packet.data = data & 0x00ffffff;
699 ret = sisusb_send_packet(sisusb, 10, &packet);
700 break;
701 case 1:
702 packet.header = (type << 6) | 0x000e;
703 packet.data = data << 8;
704 ret = sisusb_send_packet(sisusb, 10, &packet);
705 break;
706 case 2:
707 packet.header = (type << 6) | 0x000c;
708 packet.data = data << 16;
709 ret = sisusb_send_packet(sisusb, 10, &packet);
710 packet.header = (type << 6) | 0x0001;
711 packet.address = (addr & ~3) + 4;
712 packet.data = (data >> 16) & 0x00ff;
713 ret |= sisusb_send_packet(sisusb, 10, &packet);
714 break;
715 case 3:
716 packet.header = (type << 6) | 0x0008;
717 packet.data = data << 24;
718 ret = sisusb_send_packet(sisusb, 10, &packet);
719 packet.header = (type << 6) | 0x0003;
720 packet.address = (addr & ~3) + 4;
721 packet.data = (data >> 8) & 0xffff;
722 ret |= sisusb_send_packet(sisusb, 10, &packet);
723 }
724
725 return ret;
726}
727
728static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
729 u32 addr, u32 data)
730{
731 struct sisusb_packet packet;
732 int ret = 0;
733
734 packet.address = addr & ~3;
735
736 switch (addr & 3) {
737 case 0:
738 packet.header = (type << 6) | 0x000f;
739 packet.data = data;
740 ret = sisusb_send_packet(sisusb, 10, &packet);
741 break;
742 case 1:
743 packet.header = (type << 6) | 0x000e;
744 packet.data = data << 8;
745 ret = sisusb_send_packet(sisusb, 10, &packet);
746 packet.header = (type << 6) | 0x0001;
747 packet.address = (addr & ~3) + 4;
748 packet.data = data >> 24;
749 ret |= sisusb_send_packet(sisusb, 10, &packet);
750 break;
751 case 2:
752 packet.header = (type << 6) | 0x000c;
753 packet.data = data << 16;
754 ret = sisusb_send_packet(sisusb, 10, &packet);
755 packet.header = (type << 6) | 0x0003;
756 packet.address = (addr & ~3) + 4;
757 packet.data = data >> 16;
758 ret |= sisusb_send_packet(sisusb, 10, &packet);
759 break;
760 case 3:
761 packet.header = (type << 6) | 0x0008;
762 packet.data = data << 24;
763 ret = sisusb_send_packet(sisusb, 10, &packet);
764 packet.header = (type << 6) | 0x0007;
765 packet.address = (addr & ~3) + 4;
766 packet.data = data >> 8;
767 ret |= sisusb_send_packet(sisusb, 10, &packet);
768 }
769
770 return ret;
771}
772
773/* The xxx_bulk routines copy a buffer of variable size. They treat the
774 * buffer as chars, therefore lsb/msb has to be corrected if using the
775 * byte/word/long/etc routines for speed-up
776 *
777 * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
778 * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
779 * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
780 * that the data already is in the transfer buffer "sisusb->obuf[index]".
781 */
782
783static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
784 char *kernbuffer, int length,
785 const char __user *userbuffer, int index,
786 ssize_t *bytes_written)
787{
788 struct sisusb_packet packet;
789 int ret = 0;
790 static int msgcount = 0;
791 u8 swap8, fromkern = kernbuffer ? 1 : 0;
792 u16 swap16;
793 u32 swap32, flag = (length >> 28) & 1;
794 char buf[4];
795
796 /* if neither kernbuffer not userbuffer are given, assume
797 * data in obuf
798 */
799 if (!fromkern && !userbuffer)
800 kernbuffer = sisusb->obuf[index];
801
802 (*bytes_written = 0);
803
804 length &= 0x00ffffff;
805
806 while (length) {
807
808 switch (length) {
809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 case 1:
811 if (userbuffer) {
812 if (get_user(swap8, (u8 __user *)userbuffer))
813 return -EFAULT;
814 } else
815 swap8 = kernbuffer[0];
816
817 ret = sisusb_write_memio_byte(sisusb,
818 SISUSB_TYPE_MEM,
819 addr, swap8);
820
821 if (!ret)
822 (*bytes_written)++;
823
824 return ret;
825
826 case 2:
827 if (userbuffer) {
828 if (get_user(swap16, (u16 __user *)userbuffer))
829 return -EFAULT;
830 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200831 swap16 = *((u16 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
833 ret = sisusb_write_memio_word(sisusb,
834 SISUSB_TYPE_MEM,
835 addr,
836 swap16);
837
838 if (!ret)
839 (*bytes_written) += 2;
840
841 return ret;
842
843 case 3:
844 if (userbuffer) {
845 if (copy_from_user(&buf, userbuffer, 3))
846 return -EFAULT;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200847#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 swap32 = (buf[0] << 16) |
849 (buf[1] << 8) |
850 buf[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200851#else
852 swap32 = (buf[2] << 16) |
853 (buf[1] << 8) |
854 buf[0];
855#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200857#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 swap32 = (kernbuffer[0] << 16) |
859 (kernbuffer[1] << 8) |
860 kernbuffer[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200861#else
862 swap32 = (kernbuffer[2] << 16) |
863 (kernbuffer[1] << 8) |
864 kernbuffer[0];
865#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867 ret = sisusb_write_memio_24bit(sisusb,
868 SISUSB_TYPE_MEM,
869 addr,
870 swap32);
871
872 if (!ret)
873 (*bytes_written) += 3;
874
875 return ret;
876
877 case 4:
878 if (userbuffer) {
879 if (get_user(swap32, (u32 __user *)userbuffer))
880 return -EFAULT;
881 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200882 swap32 = *((u32 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 ret = sisusb_write_memio_long(sisusb,
885 SISUSB_TYPE_MEM,
886 addr,
887 swap32);
888 if (!ret)
889 (*bytes_written) += 4;
890
891 return ret;
892
893 default:
894 if ((length & ~3) > 0x10000) {
895
896 packet.header = 0x001f;
897 packet.address = 0x000001d4;
898 packet.data = addr;
899 ret = sisusb_send_bridge_packet(sisusb, 10,
900 &packet, 0);
901 packet.header = 0x001f;
902 packet.address = 0x000001d0;
903 packet.data = (length & ~3);
904 ret |= sisusb_send_bridge_packet(sisusb, 10,
905 &packet, 0);
906 packet.header = 0x001f;
907 packet.address = 0x000001c0;
908 packet.data = flag | 0x16;
909 ret |= sisusb_send_bridge_packet(sisusb, 10,
910 &packet, 0);
911 if (userbuffer) {
912 ret |= sisusb_send_bulk_msg(sisusb,
913 SISUSB_EP_GFX_LBULK_OUT,
914 (length & ~3),
915 NULL, userbuffer, 0,
916 bytes_written, 0, 1);
917 userbuffer += (*bytes_written);
918 } else if (fromkern) {
919 ret |= sisusb_send_bulk_msg(sisusb,
920 SISUSB_EP_GFX_LBULK_OUT,
921 (length & ~3),
922 kernbuffer, NULL, 0,
923 bytes_written, 0, 1);
924 kernbuffer += (*bytes_written);
925 } else {
926 ret |= sisusb_send_bulk_msg(sisusb,
927 SISUSB_EP_GFX_LBULK_OUT,
928 (length & ~3),
929 NULL, NULL, index,
930 bytes_written, 0, 1);
931 kernbuffer += ((*bytes_written) &
932 (sisusb->obufsize-1));
933 }
934
935 } else {
936
937 packet.header = 0x001f;
938 packet.address = 0x00000194;
939 packet.data = addr;
940 ret = sisusb_send_bridge_packet(sisusb, 10,
Felipe Balbied86d972007-08-10 09:34:24 -0400941 &packet, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 packet.header = 0x001f;
943 packet.address = 0x00000190;
944 packet.data = (length & ~3);
945 ret |= sisusb_send_bridge_packet(sisusb, 10,
Felipe Balbied86d972007-08-10 09:34:24 -0400946 &packet, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 if (sisusb->flagb0 != 0x16) {
948 packet.header = 0x001f;
949 packet.address = 0x00000180;
950 packet.data = flag | 0x16;
951 ret |= sisusb_send_bridge_packet(sisusb, 10,
952 &packet, 0);
953 sisusb->flagb0 = 0x16;
954 }
955 if (userbuffer) {
956 ret |= sisusb_send_bulk_msg(sisusb,
957 SISUSB_EP_GFX_BULK_OUT,
958 (length & ~3),
959 NULL, userbuffer, 0,
960 bytes_written, 0, 1);
961 userbuffer += (*bytes_written);
962 } else if (fromkern) {
963 ret |= sisusb_send_bulk_msg(sisusb,
964 SISUSB_EP_GFX_BULK_OUT,
965 (length & ~3),
966 kernbuffer, NULL, 0,
967 bytes_written, 0, 1);
968 kernbuffer += (*bytes_written);
969 } else {
970 ret |= sisusb_send_bulk_msg(sisusb,
971 SISUSB_EP_GFX_BULK_OUT,
972 (length & ~3),
973 NULL, NULL, index,
974 bytes_written, 0, 1);
975 kernbuffer += ((*bytes_written) &
976 (sisusb->obufsize-1));
977 }
978 }
979 if (ret) {
980 msgcount++;
981 if (msgcount < 500)
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -0400982 dev_err(&sisusb->sisusb_dev->dev, "Wrote %zd of %d bytes, error %d\n",
983 *bytes_written, length, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 else if (msgcount == 500)
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -0400985 dev_err(&sisusb->sisusb_dev->dev, "Too many errors, logging stopped\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 }
987 addr += (*bytes_written);
988 length -= (*bytes_written);
989 }
990
991 if (ret)
Felipe Balbied86d972007-08-10 09:34:24 -0400992 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993
994 }
995
996 return ret ? -EIO : 0;
997}
998
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200999/* Remember: Read data in packet is in machine-endianess! So for
1000 * byte, word, 24bit, long no endian correction is necessary.
1001 */
1002
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
1004 u32 addr, u8 *data)
1005{
1006 struct sisusb_packet packet;
1007 int ret;
1008
1009 CLEARPACKET(&packet);
1010 packet.header = (1 << (addr & 3)) | (type << 6);
1011 packet.address = addr & ~3;
1012 ret = sisusb_send_packet(sisusb, 6, &packet);
1013 *data = (u8)(packet.data >> ((addr & 3) << 3));
1014 return ret;
1015}
1016
1017static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
1018 u32 addr, u16 *data)
1019{
1020 struct sisusb_packet packet;
1021 int ret = 0;
1022
1023 CLEARPACKET(&packet);
1024
1025 packet.address = addr & ~3;
1026
1027 switch (addr & 3) {
1028 case 0:
1029 packet.header = (type << 6) | 0x0003;
1030 ret = sisusb_send_packet(sisusb, 6, &packet);
1031 *data = (u16)(packet.data);
1032 break;
1033 case 1:
1034 packet.header = (type << 6) | 0x0006;
1035 ret = sisusb_send_packet(sisusb, 6, &packet);
1036 *data = (u16)(packet.data >> 8);
1037 break;
1038 case 2:
1039 packet.header = (type << 6) | 0x000c;
1040 ret = sisusb_send_packet(sisusb, 6, &packet);
1041 *data = (u16)(packet.data >> 16);
1042 break;
1043 case 3:
1044 packet.header = (type << 6) | 0x0008;
1045 ret = sisusb_send_packet(sisusb, 6, &packet);
1046 *data = (u16)(packet.data >> 24);
1047 packet.header = (type << 6) | 0x0001;
1048 packet.address = (addr & ~3) + 4;
1049 ret |= sisusb_send_packet(sisusb, 6, &packet);
1050 *data |= (u16)(packet.data << 8);
1051 }
1052
1053 return ret;
1054}
1055
1056static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
1057 u32 addr, u32 *data)
1058{
1059 struct sisusb_packet packet;
1060 int ret = 0;
1061
1062 packet.address = addr & ~3;
1063
1064 switch (addr & 3) {
1065 case 0:
1066 packet.header = (type << 6) | 0x0007;
1067 ret = sisusb_send_packet(sisusb, 6, &packet);
1068 *data = packet.data & 0x00ffffff;
1069 break;
1070 case 1:
1071 packet.header = (type << 6) | 0x000e;
1072 ret = sisusb_send_packet(sisusb, 6, &packet);
1073 *data = packet.data >> 8;
1074 break;
1075 case 2:
1076 packet.header = (type << 6) | 0x000c;
1077 ret = sisusb_send_packet(sisusb, 6, &packet);
1078 *data = packet.data >> 16;
1079 packet.header = (type << 6) | 0x0001;
1080 packet.address = (addr & ~3) + 4;
1081 ret |= sisusb_send_packet(sisusb, 6, &packet);
1082 *data |= ((packet.data & 0xff) << 16);
1083 break;
1084 case 3:
1085 packet.header = (type << 6) | 0x0008;
1086 ret = sisusb_send_packet(sisusb, 6, &packet);
1087 *data = packet.data >> 24;
1088 packet.header = (type << 6) | 0x0003;
1089 packet.address = (addr & ~3) + 4;
1090 ret |= sisusb_send_packet(sisusb, 6, &packet);
1091 *data |= ((packet.data & 0xffff) << 8);
1092 }
1093
1094 return ret;
1095}
1096
1097static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
1098 u32 addr, u32 *data)
1099{
1100 struct sisusb_packet packet;
1101 int ret = 0;
1102
1103 packet.address = addr & ~3;
1104
1105 switch (addr & 3) {
1106 case 0:
1107 packet.header = (type << 6) | 0x000f;
1108 ret = sisusb_send_packet(sisusb, 6, &packet);
1109 *data = packet.data;
1110 break;
1111 case 1:
1112 packet.header = (type << 6) | 0x000e;
1113 ret = sisusb_send_packet(sisusb, 6, &packet);
1114 *data = packet.data >> 8;
1115 packet.header = (type << 6) | 0x0001;
1116 packet.address = (addr & ~3) + 4;
1117 ret |= sisusb_send_packet(sisusb, 6, &packet);
1118 *data |= (packet.data << 24);
1119 break;
1120 case 2:
1121 packet.header = (type << 6) | 0x000c;
1122 ret = sisusb_send_packet(sisusb, 6, &packet);
1123 *data = packet.data >> 16;
1124 packet.header = (type << 6) | 0x0003;
1125 packet.address = (addr & ~3) + 4;
1126 ret |= sisusb_send_packet(sisusb, 6, &packet);
1127 *data |= (packet.data << 16);
1128 break;
1129 case 3:
1130 packet.header = (type << 6) | 0x0008;
1131 ret = sisusb_send_packet(sisusb, 6, &packet);
1132 *data = packet.data >> 24;
1133 packet.header = (type << 6) | 0x0007;
1134 packet.address = (addr & ~3) + 4;
1135 ret |= sisusb_send_packet(sisusb, 6, &packet);
1136 *data |= (packet.data << 8);
1137 }
1138
1139 return ret;
1140}
1141
1142static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
1143 char *kernbuffer, int length,
1144 char __user *userbuffer, ssize_t *bytes_read)
1145{
1146 int ret = 0;
1147 char buf[4];
1148 u16 swap16;
1149 u32 swap32;
1150
1151 (*bytes_read = 0);
1152
1153 length &= 0x00ffffff;
1154
1155 while (length) {
1156
1157 switch (length) {
1158
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 case 1:
1160
1161 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
1162 addr, &buf[0]);
1163 if (!ret) {
1164 (*bytes_read)++;
1165 if (userbuffer) {
1166 if (put_user(buf[0],
1167 (u8 __user *)userbuffer)) {
1168 return -EFAULT;
1169 }
1170 } else {
1171 kernbuffer[0] = buf[0];
1172 }
1173 }
1174 return ret;
1175
1176 case 2:
1177 ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
1178 addr, &swap16);
1179 if (!ret) {
1180 (*bytes_read) += 2;
1181 if (userbuffer) {
1182 if (put_user(swap16,
1183 (u16 __user *)userbuffer))
1184 return -EFAULT;
1185 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001186 *((u16 *)kernbuffer) = swap16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
1188 }
1189 return ret;
1190
1191 case 3:
1192 ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
1193 addr, &swap32);
1194 if (!ret) {
1195 (*bytes_read) += 3;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001196#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 buf[0] = (swap32 >> 16) & 0xff;
1198 buf[1] = (swap32 >> 8) & 0xff;
1199 buf[2] = swap32 & 0xff;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001200#else
1201 buf[2] = (swap32 >> 16) & 0xff;
1202 buf[1] = (swap32 >> 8) & 0xff;
1203 buf[0] = swap32 & 0xff;
1204#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 if (userbuffer) {
1206 if (copy_to_user(userbuffer, &buf[0], 3))
1207 return -EFAULT;
1208 } else {
1209 kernbuffer[0] = buf[0];
1210 kernbuffer[1] = buf[1];
1211 kernbuffer[2] = buf[2];
1212 }
1213 }
1214 return ret;
1215
1216 default:
1217 ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
1218 addr, &swap32);
1219 if (!ret) {
1220 (*bytes_read) += 4;
1221 if (userbuffer) {
1222 if (put_user(swap32,
1223 (u32 __user *)userbuffer))
1224 return -EFAULT;
1225
1226 userbuffer += 4;
1227 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001228 *((u32 *)kernbuffer) = swap32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 kernbuffer += 4;
1230 }
1231 addr += 4;
1232 length -= 4;
1233 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 }
1235
1236 if (ret)
Felipe Balbied86d972007-08-10 09:34:24 -04001237 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 }
1239
1240 return ret;
1241}
1242
1243/* High level: Gfx (indexed) register access */
1244
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001245#ifdef INCL_SISUSB_CON
1246int
1247sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
1248{
1249 return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1250}
1251
1252int
1253sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
1254{
1255 return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1256}
1257#endif
1258
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001259int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
1261{
1262 int ret;
1263 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1264 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1265 return ret;
1266}
1267
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001268int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
1270{
1271 int ret;
1272 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1273 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1274 return ret;
1275}
1276
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001277int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
1279 u8 myand, u8 myor)
1280{
1281 int ret;
1282 u8 tmp;
1283
1284 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1285 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1286 tmp &= myand;
1287 tmp |= myor;
1288 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1289 return ret;
1290}
1291
1292static int
1293sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx,
1294 u8 data, u8 mask)
1295{
1296 int ret;
1297 u8 tmp;
1298 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1299 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1300 tmp &= ~(mask);
1301 tmp |= (data & mask);
1302 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1303 return ret;
1304}
1305
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001306int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
1308{
1309 return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
1310}
1311
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001312int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
1314{
1315 return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00));
1316}
1317
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001318/* Write/read video ram */
1319
1320#ifdef INCL_SISUSB_CON
1321int
1322sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data)
1323{
1324 return(sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
1325}
1326
1327int
1328sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data)
1329{
1330 return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
1331}
1332
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001333int
1334sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
1335 u32 dest, int length, size_t *bytes_written)
1336{
1337 return(sisusb_write_mem_bulk(sisusb, dest, src, length, NULL, 0, bytes_written));
1338}
1339
1340#ifdef SISUSBENDIANTEST
1341int
1342sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest,
1343 u32 src, int length, size_t *bytes_written)
1344{
1345 return(sisusb_read_mem_bulk(sisusb, src, dest, length, NULL, bytes_written));
1346}
1347#endif
1348#endif
1349
1350#ifdef SISUSBENDIANTEST
1351static void
1352sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
1353{
1354 static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
1355 char destbuffer[10];
1356 size_t dummy;
1357 int i,j;
1358
1359 sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy);
1360
1361 for(i = 1; i <= 7; i++) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04001362 dev_dbg(&sisusb->sisusb_dev->dev, "sisusb: rwtest %d bytes\n", i);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001363 sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy);
1364 for(j = 0; j < i; j++) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04001365 dev_dbg(&sisusb->sisusb_dev->dev, "rwtest read[%d] = %x\n", j, destbuffer[j]);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001366 }
1367 }
1368}
1369#endif
1370
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371/* access pci config registers (reg numbers 0, 4, 8, etc) */
1372
1373static int
1374sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data)
1375{
1376 struct sisusb_packet packet;
1377 int ret;
1378
1379 packet.header = 0x008f;
1380 packet.address = regnum | 0x10000;
1381 packet.data = data;
1382 ret = sisusb_send_packet(sisusb, 10, &packet);
1383 return ret;
1384}
1385
1386static int
1387sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data)
1388{
1389 struct sisusb_packet packet;
1390 int ret;
1391
1392 packet.header = 0x008f;
1393 packet.address = (u32)regnum | 0x10000;
1394 ret = sisusb_send_packet(sisusb, 6, &packet);
1395 *data = packet.data;
1396 return ret;
1397}
1398
1399/* Clear video RAM */
1400
1401static int
1402sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
1403{
1404 int ret, i;
1405 ssize_t j;
1406
1407 if (address < sisusb->vrambase)
1408 return 1;
1409
1410 if (address >= sisusb->vrambase + sisusb->vramsize)
1411 return 1;
1412
1413 if (address + length > sisusb->vrambase + sisusb->vramsize)
1414 length = sisusb->vrambase + sisusb->vramsize - address;
1415
1416 if (length <= 0)
1417 return 0;
1418
1419 /* allocate free buffer/urb and clear the buffer */
1420 if ((i = sisusb_alloc_outbuf(sisusb)) < 0)
1421 return -EBUSY;
1422
1423 memset(sisusb->obuf[i], 0, sisusb->obufsize);
1424
1425 /* We can write a length > buffer size here. The buffer
1426 * data will simply be re-used (like a ring-buffer).
1427 */
1428 ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
1429
1430 /* Free the buffer/urb */
1431 sisusb_free_outbuf(sisusb, i);
1432
1433 return ret;
1434}
1435
1436/* Initialize the graphics core (return 0 on success)
1437 * This resets the graphics hardware and puts it into
1438 * a defined mode (640x480@60Hz)
1439 */
1440
1441#define GETREG(r,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1442#define SETREG(r,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1443#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d)
1444#define GETIREG(r,i,d) sisusb_getidxreg(sisusb, r, i, d)
1445#define SETIREGOR(r,i,o) sisusb_setidxregor(sisusb, r, i, o)
1446#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a)
1447#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o)
1448#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
Felipe Balbied86d972007-08-10 09:34:24 -04001449#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
Felipe Balbied86d972007-08-10 09:34:24 -04001451#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
1453static int
1454sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
1455{
1456 int ret;
1457 u8 tmp8;
1458
1459 ret = GETIREG(SISSR, 0x16, &tmp8);
1460 if (ramtype <= 1) {
1461 tmp8 &= 0x3f;
1462 ret |= SETIREG(SISSR, 0x16, tmp8);
1463 tmp8 |= 0x80;
1464 ret |= SETIREG(SISSR, 0x16, tmp8);
1465 } else {
1466 tmp8 |= 0xc0;
1467 ret |= SETIREG(SISSR, 0x16, tmp8);
1468 tmp8 &= 0x0f;
1469 ret |= SETIREG(SISSR, 0x16, tmp8);
1470 tmp8 |= 0x80;
1471 ret |= SETIREG(SISSR, 0x16, tmp8);
1472 tmp8 &= 0x0f;
1473 ret |= SETIREG(SISSR, 0x16, tmp8);
1474 tmp8 |= 0xd0;
1475 ret |= SETIREG(SISSR, 0x16, tmp8);
1476 tmp8 &= 0x0f;
1477 ret |= SETIREG(SISSR, 0x16, tmp8);
1478 tmp8 |= 0xa0;
1479 ret |= SETIREG(SISSR, 0x16, tmp8);
1480 }
1481 return ret;
1482}
1483
1484static int
1485sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab)
1486{
1487 int ret;
1488 u8 ramtype, done = 0;
1489 u32 t0, t1, t2, t3;
1490 u32 ramptr = SISUSB_PCI_MEMBASE;
1491
1492 ret = GETIREG(SISSR, 0x3a, &ramtype);
1493 ramtype &= 3;
1494
1495 ret |= SETIREG(SISSR, 0x13, 0x00);
1496
1497 if (ramtype <= 1) {
1498 ret |= SETIREG(SISSR, 0x14, 0x12);
1499 ret |= SETIREGAND(SISSR, 0x15, 0xef);
1500 } else {
1501 ret |= SETIREG(SISSR, 0x14, 0x02);
1502 }
1503
1504 ret |= sisusb_triggersr16(sisusb, ramtype);
1505 ret |= WRITEL(ramptr + 0, 0x01234567);
1506 ret |= WRITEL(ramptr + 4, 0x456789ab);
1507 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1508 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1509 ret |= WRITEL(ramptr + 16, 0x55555555);
1510 ret |= WRITEL(ramptr + 20, 0x55555555);
1511 ret |= WRITEL(ramptr + 24, 0xffffffff);
1512 ret |= WRITEL(ramptr + 28, 0xffffffff);
1513 ret |= READL(ramptr + 0, &t0);
1514 ret |= READL(ramptr + 4, &t1);
1515 ret |= READL(ramptr + 8, &t2);
1516 ret |= READL(ramptr + 12, &t3);
1517
1518 if (ramtype <= 1) {
1519
1520 *chab = 0; *bw = 64;
1521
1522 if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
1523 if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
1524 *chab = 0; *bw = 64;
1525 ret |= SETIREGAND(SISSR, 0x14, 0xfd);
1526 }
1527 }
1528 if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
1529 *chab = 1; *bw = 64;
1530 ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01);
1531
1532 ret |= sisusb_triggersr16(sisusb, ramtype);
1533 ret |= WRITEL(ramptr + 0, 0x89abcdef);
1534 ret |= WRITEL(ramptr + 4, 0xcdef0123);
1535 ret |= WRITEL(ramptr + 8, 0x55555555);
1536 ret |= WRITEL(ramptr + 12, 0x55555555);
1537 ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
1538 ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
1539 ret |= READL(ramptr + 4, &t1);
1540
1541 if (t1 != 0xcdef0123) {
1542 *bw = 32;
1543 ret |= SETIREGOR(SISSR, 0x15, 0x10);
1544 }
1545 }
1546
1547 } else {
1548
1549 *chab = 0; *bw = 64; /* default: cha, bw = 64 */
1550
1551 done = 0;
1552
1553 if (t1 == 0x456789ab) {
1554 if (t0 == 0x01234567) {
1555 *chab = 0; *bw = 64;
1556 done = 1;
1557 }
1558 } else {
1559 if (t0 == 0x01234567) {
1560 *chab = 0; *bw = 32;
1561 ret |= SETIREG(SISSR, 0x14, 0x00);
1562 done = 1;
1563 }
1564 }
1565
1566 if (!done) {
1567 ret |= SETIREG(SISSR, 0x14, 0x03);
1568 ret |= sisusb_triggersr16(sisusb, ramtype);
1569
1570 ret |= WRITEL(ramptr + 0, 0x01234567);
1571 ret |= WRITEL(ramptr + 4, 0x456789ab);
1572 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1573 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1574 ret |= WRITEL(ramptr + 16, 0x55555555);
1575 ret |= WRITEL(ramptr + 20, 0x55555555);
1576 ret |= WRITEL(ramptr + 24, 0xffffffff);
1577 ret |= WRITEL(ramptr + 28, 0xffffffff);
1578 ret |= READL(ramptr + 0, &t0);
1579 ret |= READL(ramptr + 4, &t1);
1580
1581 if (t1 == 0x456789ab) {
1582 if (t0 == 0x01234567) {
1583 *chab = 1; *bw = 64;
1584 return ret;
1585 } /* else error */
1586 } else {
1587 if (t0 == 0x01234567) {
1588 *chab = 1; *bw = 32;
1589 ret |= SETIREG(SISSR, 0x14, 0x01);
1590 } /* else error */
1591 }
1592 }
1593 }
1594 return ret;
1595}
1596
1597static int
1598sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
1599{
1600 int ret = 0;
1601 u32 ramptr = SISUSB_PCI_MEMBASE;
1602 u8 tmp1, tmp2, i, j;
1603
1604 ret |= WRITEB(ramptr, 0xaa);
1605 ret |= WRITEB(ramptr + 16, 0x55);
1606 ret |= READB(ramptr, &tmp1);
1607 ret |= READB(ramptr + 16, &tmp2);
1608 if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
1609 for (i = 0, j = 16; i < 2; i++, j += 16) {
1610 ret |= GETIREG(SISSR, 0x21, &tmp1);
1611 ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
1612 ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */
1613 ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
1614 ret |= SETIREG(SISSR, 0x21, tmp1);
1615 ret |= WRITEB(ramptr + 16 + j, j);
1616 ret |= READB(ramptr + 16 + j, &tmp1);
1617 if (tmp1 == j) {
1618 ret |= WRITEB(ramptr + j, j);
1619 break;
1620 }
1621 }
1622 }
1623 return ret;
1624}
1625
1626static int
1627sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index,
1628 u8 rankno, u8 chab, const u8 dramtype[][5],
1629 int bw)
1630{
1631 int ret = 0, ranksize;
1632 u8 tmp;
1633
1634 *iret = 0;
1635
1636 if ((rankno == 2) && (dramtype[index][0] == 2))
1637 return ret;
1638
1639 ranksize = dramtype[index][3] / 2 * bw / 32;
1640
1641 if ((ranksize * rankno) > 128)
1642 return ret;
1643
1644 tmp = 0;
1645 while ((ranksize >>= 1) > 0) tmp += 0x10;
1646 tmp |= ((rankno - 1) << 2);
1647 tmp |= ((bw / 64) & 0x02);
1648 tmp |= (chab & 0x01);
1649
1650 ret = SETIREG(SISSR, 0x14, tmp);
1651 ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
1652
1653 *iret = 1;
1654
1655 return ret;
1656}
1657
1658static int
1659sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn)
1660{
1661 int ret = 0, i;
1662 u32 j, tmp;
1663
1664 *iret = 0;
1665
1666 for (i = 0, j = 0; i < testn; i++) {
1667 ret |= WRITEL(sisusb->vrambase + j, j);
1668 j += inc;
1669 }
1670
1671 for (i = 0, j = 0; i < testn; i++) {
1672 ret |= READL(sisusb->vrambase + j, &tmp);
1673 if (tmp != j) return ret;
1674 j += inc;
1675 }
1676
1677 *iret = 1;
1678 return ret;
1679}
1680
1681static int
1682sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno,
1683 int idx, int bw, const u8 rtype[][5])
1684{
1685 int ret = 0, i, i2ret;
1686 u32 inc;
1687
1688 *iret = 0;
1689
1690 for (i = rankno; i >= 1; i--) {
1691 inc = 1 << (rtype[idx][2] +
1692 rtype[idx][1] +
1693 rtype[idx][0] +
1694 bw / 64 + i);
1695 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1696 if (!i2ret)
1697 return ret;
1698 }
1699
1700 inc = 1 << (rtype[idx][2] + bw / 64 + 2);
1701 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
1702 if (!i2ret)
1703 return ret;
1704
1705 inc = 1 << (10 + bw / 64);
1706 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1707 if (!i2ret)
1708 return ret;
1709
1710 *iret = 1;
1711 return ret;
1712}
1713
1714static int
1715sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw,
1716 int chab)
1717{
1718 int ret = 0, i2ret = 0, i, j;
1719 static const u8 sdramtype[13][5] = {
1720 { 2, 12, 9, 64, 0x35 },
1721 { 1, 13, 9, 64, 0x44 },
1722 { 2, 12, 8, 32, 0x31 },
1723 { 2, 11, 9, 32, 0x25 },
1724 { 1, 12, 9, 32, 0x34 },
1725 { 1, 13, 8, 32, 0x40 },
1726 { 2, 11, 8, 16, 0x21 },
1727 { 1, 12, 8, 16, 0x30 },
1728 { 1, 11, 9, 16, 0x24 },
1729 { 1, 11, 8, 8, 0x20 },
1730 { 2, 9, 8, 4, 0x01 },
1731 { 1, 10, 8, 4, 0x10 },
1732 { 1, 9, 8, 2, 0x00 }
1733 };
1734
1735 *iret = 1; /* error */
1736
1737 for (i = 0; i < 13; i++) {
1738 ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
1739 for (j = 2; j > 0; j--) {
1740 ret |= sisusb_set_rank(sisusb, &i2ret, i, j,
1741 chab, sdramtype, bw);
1742 if (!i2ret)
1743 continue;
1744
1745 ret |= sisusb_check_ranks(sisusb, &i2ret, j, i,
1746 bw, sdramtype);
1747 if (i2ret) {
1748 *iret = 0; /* ram size found */
1749 return ret;
1750 }
1751 }
1752 }
1753
1754 return ret;
1755}
1756
1757static int
1758sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
1759{
1760 int ret = 0;
1761 u32 address;
1762 int i, length, modex, modey, bpp;
1763
1764 modex = 640; modey = 480; bpp = 2;
1765
1766 address = sisusb->vrambase; /* Clear video ram */
1767
1768 if (clrall)
1769 length = sisusb->vramsize;
1770 else
1771 length = modex * bpp * modey;
1772
1773 ret = sisusb_clear_vram(sisusb, address, length);
1774
1775 if (!ret && drwfr) {
1776 for (i = 0; i < modex; i++) {
1777 address = sisusb->vrambase + (i * bpp);
1778 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1779 address, 0xf100);
1780 address += (modex * (modey-1) * bpp);
1781 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1782 address, 0xf100);
1783 }
1784 for (i = 0; i < modey; i++) {
1785 address = sisusb->vrambase + ((i * modex) * bpp);
1786 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1787 address, 0xf100);
1788 address += ((modex - 1) * bpp);
1789 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
1790 address, 0xf100);
1791 }
1792 }
1793
1794 return ret;
1795}
1796
1797static int
1798sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
1799{
1800 int ret = 0, i, j, modex, modey, bpp, du;
1801 u8 sr31, cr63, tmp8;
1802 static const char attrdata[] = {
1803 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
1804 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
1805 0x01,0x00,0x00,0x00
1806 };
1807 static const char crtcrdata[] = {
1808 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
1809 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
1810 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
1811 0xff
1812 };
1813 static const char grcdata[] = {
1814 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
1815 0xff
1816 };
1817 static const char crtcdata[] = {
1818 0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e,
1819 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05,
1820 0x00
1821 };
1822
1823 modex = 640; modey = 480; bpp = 2;
1824
1825 GETIREG(SISSR, 0x31, &sr31);
1826 GETIREG(SISCR, 0x63, &cr63);
1827 SETIREGOR(SISSR, 0x01, 0x20);
1828 SETIREG(SISCR, 0x63, cr63 & 0xbf);
1829 SETIREGOR(SISCR, 0x17, 0x80);
1830 SETIREGOR(SISSR, 0x1f, 0x04);
1831 SETIREGAND(SISSR, 0x07, 0xfb);
1832 SETIREG(SISSR, 0x00, 0x03); /* seq */
1833 SETIREG(SISSR, 0x01, 0x21);
1834 SETIREG(SISSR, 0x02, 0x0f);
1835 SETIREG(SISSR, 0x03, 0x00);
1836 SETIREG(SISSR, 0x04, 0x0e);
1837 SETREG(SISMISCW, 0x23); /* misc */
1838 for (i = 0; i <= 0x18; i++) { /* crtc */
1839 SETIREG(SISCR, i, crtcrdata[i]);
1840 }
1841 for (i = 0; i <= 0x13; i++) { /* att */
1842 GETREG(SISINPSTAT, &tmp8);
1843 SETREG(SISAR, i);
1844 SETREG(SISAR, attrdata[i]);
1845 }
1846 GETREG(SISINPSTAT, &tmp8);
1847 SETREG(SISAR, 0x14);
1848 SETREG(SISAR, 0x00);
1849 GETREG(SISINPSTAT, &tmp8);
1850 SETREG(SISAR, 0x20);
1851 GETREG(SISINPSTAT, &tmp8);
1852 for (i = 0; i <= 0x08; i++) { /* grc */
1853 SETIREG(SISGR, i, grcdata[i]);
1854 }
1855 SETIREGAND(SISGR, 0x05, 0xbf);
1856 for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */
1857 SETIREG(SISSR, i, 0x00);
1858 }
1859 SETIREGAND(SISSR, 0x37, 0xfe);
1860 SETREG(SISMISCW, 0xef); /* sync */
1861 SETIREG(SISCR, 0x11, 0x00); /* crtc */
1862 for (j = 0x00, i = 0; i <= 7; i++, j++) {
1863 SETIREG(SISCR, j, crtcdata[i]);
1864 }
1865 for (j = 0x10; i <= 10; i++, j++) {
1866 SETIREG(SISCR, j, crtcdata[i]);
1867 }
1868 for (j = 0x15; i <= 12; i++, j++) {
1869 SETIREG(SISCR, j, crtcdata[i]);
1870 }
1871 for (j = 0x0A; i <= 15; i++, j++) {
1872 SETIREG(SISSR, j, crtcdata[i]);
1873 }
1874 SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
1875 SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
1876 SETIREG(SISCR, 0x14, 0x4f);
1877 du = (modex / 16) * (bpp * 2); /* offset/pitch */
1878 if (modex % 16) du += bpp;
1879 SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
1880 SETIREG(SISCR, 0x13, (du & 0xff));
1881 du <<= 5;
1882 tmp8 = du >> 8;
1883 if (du & 0xff) tmp8++;
1884 SETIREG(SISSR, 0x10, tmp8);
1885 SETIREG(SISSR, 0x31, 0x00); /* VCLK */
1886 SETIREG(SISSR, 0x2b, 0x1b);
1887 SETIREG(SISSR, 0x2c, 0xe1);
1888 SETIREG(SISSR, 0x2d, 0x01);
1889 SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */
1890 SETIREG(SISSR, 0x08, 0xae);
1891 SETIREGAND(SISSR, 0x09, 0xf0);
1892 SETIREG(SISSR, 0x08, 0x34);
1893 SETIREGOR(SISSR, 0x3d, 0x01);
1894 SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */
1895 SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
1896 SETIREG(SISCR, 0x19, 0x00);
1897 SETIREGAND(SISCR, 0x1a, 0xfc);
1898 SETIREGAND(SISSR, 0x0f, 0xb7);
1899 SETIREGAND(SISSR, 0x31, 0xfb);
1900 SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
1901 SETIREGAND(SISSR, 0x32, 0xf3);
1902 SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
1903 SETIREG(SISCR, 0x52, 0x6c);
1904
1905 SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */
1906 SETIREG(SISCR, 0x0c, 0x00);
1907 SETIREG(SISSR, 0x0d, 0x00);
1908 SETIREGAND(SISSR, 0x37, 0xfe);
1909
1910 SETIREG(SISCR, 0x32, 0x20);
1911 SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */
1912 SETIREG(SISCR, 0x63, (cr63 & 0xbf));
1913 SETIREG(SISSR, 0x31, (sr31 & 0xfb));
1914
1915 if (touchengines) {
1916 SETIREG(SISSR, 0x20, 0xa1); /* enable engines */
1917 SETIREGOR(SISSR, 0x1e, 0x5a);
1918
1919 SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */
1920 SETIREG(SISSR, 0x27, 0x1f);
1921 SETIREG(SISSR, 0x26, 0x00);
1922 }
1923
Felipe Balbied86d972007-08-10 09:34:24 -04001924 SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925
1926 return ret;
1927}
1928
1929static int
1930sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
1931{
1932 int ret = 0, i, j, bw, chab, iret, retry = 3;
1933 u8 tmp8, ramtype;
1934 u32 tmp32;
1935 static const char mclktable[] = {
1936 0x3b, 0x22, 0x01, 143,
1937 0x3b, 0x22, 0x01, 143,
1938 0x3b, 0x22, 0x01, 143,
1939 0x3b, 0x22, 0x01, 143
1940 };
1941 static const char eclktable[] = {
1942 0x3b, 0x22, 0x01, 143,
1943 0x3b, 0x22, 0x01, 143,
1944 0x3b, 0x22, 0x01, 143,
1945 0x3b, 0x22, 0x01, 143
1946 };
1947 static const char ramtypetable1[] = {
1948 0x00, 0x04, 0x60, 0x60,
1949 0x0f, 0x0f, 0x1f, 0x1f,
1950 0xba, 0xba, 0xba, 0xba,
1951 0xa9, 0xa9, 0xac, 0xac,
1952 0xa0, 0xa0, 0xa0, 0xa8,
1953 0x00, 0x00, 0x02, 0x02,
1954 0x30, 0x30, 0x40, 0x40
1955 };
1956 static const char ramtypetable2[] = {
1957 0x77, 0x77, 0x44, 0x44,
1958 0x77, 0x77, 0x44, 0x44,
1959 0x00, 0x00, 0x00, 0x00,
1960 0x5b, 0x5b, 0xab, 0xab,
1961 0x00, 0x00, 0xf0, 0xf8
1962 };
1963
1964 while (retry--) {
1965
1966 /* Enable VGA */
1967 ret = GETREG(SISVGAEN, &tmp8);
1968 ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
1969
1970 /* Enable GPU access to VRAM */
1971 ret |= GETREG(SISMISCR, &tmp8);
1972 ret |= SETREG(SISMISCW, (tmp8 | 0x01));
1973
1974 if (ret) continue;
1975
1976 /* Reset registers */
1977 ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
1978 ret |= SETIREG(SISSR, 0x05, 0x86);
1979 ret |= SETIREGOR(SISSR, 0x20, 0x01);
1980
1981 ret |= SETREG(SISMISCW, 0x67);
1982
1983 for (i = 0x06; i <= 0x1f; i++) {
1984 ret |= SETIREG(SISSR, i, 0x00);
1985 }
1986 for (i = 0x21; i <= 0x27; i++) {
1987 ret |= SETIREG(SISSR, i, 0x00);
1988 }
1989 for (i = 0x31; i <= 0x3d; i++) {
1990 ret |= SETIREG(SISSR, i, 0x00);
1991 }
1992 for (i = 0x12; i <= 0x1b; i++) {
1993 ret |= SETIREG(SISSR, i, 0x00);
1994 }
1995 for (i = 0x79; i <= 0x7c; i++) {
1996 ret |= SETIREG(SISCR, i, 0x00);
1997 }
1998
1999 if (ret) continue;
2000
2001 ret |= SETIREG(SISCR, 0x63, 0x80);
2002
2003 ret |= GETIREG(SISSR, 0x3a, &ramtype);
2004 ramtype &= 0x03;
2005
2006 ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
2007 ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
2008 ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
2009
2010 ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
2011 ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
2012 ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
2013
2014 ret |= SETIREG(SISSR, 0x07, 0x18);
2015 ret |= SETIREG(SISSR, 0x11, 0x0f);
2016
2017 if (ret) continue;
2018
2019 for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
2020 ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]);
2021 }
2022 for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
2023 ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]);
2024 }
2025
2026 ret |= SETIREG(SISCR, 0x49, 0xaa);
2027
2028 ret |= SETIREG(SISSR, 0x1f, 0x00);
2029 ret |= SETIREG(SISSR, 0x20, 0xa0);
2030 ret |= SETIREG(SISSR, 0x23, 0xf6);
2031 ret |= SETIREG(SISSR, 0x24, 0x0d);
2032 ret |= SETIREG(SISSR, 0x25, 0x33);
2033
2034 ret |= SETIREG(SISSR, 0x11, 0x0f);
2035
2036 ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
2037
2038 ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
2039
2040 if (ret) continue;
2041
2042 ret |= SETIREG(SISPART1, 0x00, 0x00);
2043
2044 ret |= GETIREG(SISSR, 0x13, &tmp8);
2045 tmp8 >>= 4;
2046
2047 ret |= SETIREG(SISPART1, 0x02, 0x00);
2048 ret |= SETIREG(SISPART1, 0x2e, 0x08);
2049
2050 ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
2051 tmp32 &= 0x00f00000;
2052 tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
2053 ret |= SETIREG(SISSR, 0x25, tmp8);
2054 tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
2055 ret |= SETIREG(SISCR, 0x49, tmp8);
2056
2057 ret |= SETIREG(SISSR, 0x27, 0x1f);
2058 ret |= SETIREG(SISSR, 0x31, 0x00);
2059 ret |= SETIREG(SISSR, 0x32, 0x11);
2060 ret |= SETIREG(SISSR, 0x33, 0x00);
2061
2062 if (ret) continue;
2063
2064 ret |= SETIREG(SISCR, 0x83, 0x00);
2065
2066 ret |= sisusb_set_default_mode(sisusb, 0);
2067
2068 ret |= SETIREGAND(SISSR, 0x21, 0xdf);
2069 ret |= SETIREGOR(SISSR, 0x01, 0x20);
2070 ret |= SETIREGOR(SISSR, 0x16, 0x0f);
2071
2072 ret |= sisusb_triggersr16(sisusb, ramtype);
2073
2074 /* Disable refresh */
2075 ret |= SETIREGAND(SISSR, 0x17, 0xf8);
2076 ret |= SETIREGOR(SISSR, 0x19, 0x03);
2077
2078 ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
2079 ret |= sisusb_verify_mclk(sisusb);
2080
2081 if (ramtype <= 1) {
2082 ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
2083 if (iret) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002084 dev_err(&sisusb->sisusb_dev->dev,"RAM size detection failed, assuming 8MB video RAM\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 ret |= SETIREG(SISSR,0x14,0x31);
2086 /* TODO */
2087 }
2088 } else {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002089 dev_err(&sisusb->sisusb_dev->dev, "DDR RAM device found, assuming 8MB video RAM\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 ret |= SETIREG(SISSR,0x14,0x31);
2091 /* *** TODO *** */
2092 }
2093
2094 /* Enable refresh */
2095 ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
2096 ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
2097 ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
2098
2099 ret |= SETIREGOR(SISSR, 0x21, 0x20);
2100
2101 ret |= SETIREG(SISSR, 0x22, 0xfb);
2102 ret |= SETIREG(SISSR, 0x21, 0xa5);
2103
2104 if (ret == 0)
2105 break;
2106 }
2107
2108 return ret;
2109}
2110
2111#undef SETREG
2112#undef GETREG
2113#undef SETIREG
2114#undef GETIREG
2115#undef SETIREGOR
2116#undef SETIREGAND
2117#undef SETIREGANDOR
2118#undef READL
2119#undef WRITEL
2120
2121static void
2122sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
2123{
2124 u8 tmp8, tmp82, ramtype;
2125 int bw = 0;
2126 char *ramtypetext1 = NULL;
2127 const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM",
2128 "DDR SDRAM", "DDR SGRAM" };
2129 static const int busSDR[4] = {64, 64, 128, 128};
2130 static const int busDDR[4] = {32, 32, 64, 64};
2131 static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
2132
2133 sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
2134 sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
2135 sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
2136 sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
2137 ramtype &= 0x03;
2138 switch ((tmp8 >> 2) & 0x03) {
2139 case 0: ramtypetext1 = "1 ch/1 r";
2140 if (tmp82 & 0x10) {
2141 bw = 32;
2142 } else {
2143 bw = busSDR[(tmp8 & 0x03)];
2144 }
2145 break;
2146 case 1: ramtypetext1 = "1 ch/2 r";
2147 sisusb->vramsize <<= 1;
2148 bw = busSDR[(tmp8 & 0x03)];
2149 break;
2150 case 2: ramtypetext1 = "asymmeric";
2151 sisusb->vramsize += sisusb->vramsize/2;
2152 bw = busDDRA[(tmp8 & 0x03)];
2153 break;
2154 case 3: ramtypetext1 = "2 channel";
2155 sisusb->vramsize <<= 1;
2156 bw = busDDR[(tmp8 & 0x03)];
2157 break;
2158 }
2159
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002160 dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 ramtypetext2[ramtype], bw);
2162}
2163
2164static int
2165sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
2166{
2167 struct sisusb_packet packet;
2168 int ret;
2169 u32 tmp32;
2170
2171 /* Do some magic */
2172 packet.header = 0x001f;
2173 packet.address = 0x00000324;
2174 packet.data = 0x00000004;
2175 ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2176
2177 packet.header = 0x001f;
2178 packet.address = 0x00000364;
2179 packet.data = 0x00000004;
2180 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2181
2182 packet.header = 0x001f;
2183 packet.address = 0x00000384;
2184 packet.data = 0x00000004;
2185 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2186
2187 packet.header = 0x001f;
2188 packet.address = 0x00000100;
2189 packet.data = 0x00000700;
2190 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2191
2192 packet.header = 0x000f;
2193 packet.address = 0x00000004;
2194 ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
2195 packet.data |= 0x17;
2196 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2197
2198 /* Init BAR 0 (VRAM) */
2199 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2200 ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
2201 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2202 tmp32 &= 0x0f;
2203 tmp32 |= SISUSB_PCI_MEMBASE;
2204 ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
2205
2206 /* Init BAR 1 (MMIO) */
2207 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2208 ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
2209 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2210 tmp32 &= 0x0f;
2211 tmp32 |= SISUSB_PCI_MMIOBASE;
2212 ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
2213
2214 /* Init BAR 2 (i/o ports) */
2215 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2216 ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
2217 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2218 tmp32 &= 0x0f;
2219 tmp32 |= SISUSB_PCI_IOPORTBASE;
2220 ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
2221
2222 /* Enable memory and i/o access */
2223 ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
2224 tmp32 |= 0x3;
2225 ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
2226
2227 if (ret == 0) {
2228 /* Some further magic */
2229 packet.header = 0x001f;
2230 packet.address = 0x00000050;
2231 packet.data = 0x000000ff;
2232 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2233 }
2234
2235 return ret;
2236}
2237
2238/* Initialize the graphics device (return 0 on success)
2239 * This initializes the net2280 as well as the PCI registers
2240 * of the graphics board.
2241 */
2242
2243static int
2244sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
2245{
2246 int ret = 0, test = 0;
2247 u32 tmp32;
2248
2249 if (sisusb->devinit == 1) {
2250 /* Read PCI BARs and see if they have been set up */
2251 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2252 if (ret) return ret;
2253 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++;
2254
2255 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2256 if (ret) return ret;
2257 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++;
2258
2259 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2260 if (ret) return ret;
2261 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++;
2262 }
2263
2264 /* No? So reset the device */
2265 if ((sisusb->devinit == 0) || (test != 3)) {
2266
2267 ret |= sisusb_do_init_gfxdevice(sisusb);
2268
2269 if (ret == 0)
2270 sisusb->devinit = 1;
2271
2272 }
2273
2274 if (sisusb->devinit) {
2275 /* Initialize the graphics core */
2276 if (sisusb_init_gfxcore(sisusb) == 0) {
2277 sisusb->gfxinit = 1;
2278 sisusb_get_ramconfig(sisusb);
2279 ret |= sisusb_set_default_mode(sisusb, 1);
2280 ret |= sisusb_setup_screen(sisusb, 1, initscreen);
2281 }
2282 }
2283
2284 return ret;
2285}
2286
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002287
2288#ifdef INCL_SISUSB_CON
2289
2290/* Set up default text mode:
2291 - Set text mode (0x03)
2292 - Upload default font
2293 - Upload user font (if available)
2294*/
2295
2296int
2297sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
2298{
2299 int ret = 0, slot = sisusb->font_slot, i;
Andrew Mortondabb5922005-09-22 23:55:22 -07002300 const struct font_desc *myfont;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002301 u8 *tempbuf;
2302 u16 *tempbufb;
2303 size_t written;
Arjan van de Ven4c4c9432005-11-29 09:43:42 +01002304 static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
2305 static const char bootlogo[] = "(o_ //\\ V_/_";
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002306
2307 /* sisusb->lock is down */
2308
2309 if (!sisusb->SiS_Pr)
2310 return 1;
2311
2312 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2313 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2314
2315 /* Set mode 0x03 */
2316 SiSUSBSetMode(sisusb->SiS_Pr, 0x03);
2317
2318 if (!(myfont = find_font("VGA8x16")))
2319 return 1;
2320
2321 if (!(tempbuf = vmalloc(8192)))
2322 return 1;
2323
2324 for (i = 0; i < 256; i++)
2325 memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16);
2326
2327 /* Upload default font */
2328 ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, 0, 1, NULL, 16, 0);
2329
2330 vfree(tempbuf);
2331
2332 /* Upload user font (and reset current slot) */
2333 if (sisusb->font_backup) {
2334 ret |= sisusbcon_do_font_op(sisusb, 1, 2, sisusb->font_backup,
2335 8192, sisusb->font_backup_512, 1, NULL,
2336 sisusb->font_backup_height, 0);
2337 if (slot != 2)
2338 sisusbcon_do_font_op(sisusb, 1, 0, NULL, 0, 0, 1,
2339 NULL, 16, 0);
2340 }
2341
2342 if (init && !sisusb->scrbuf) {
2343
2344 if ((tempbuf = vmalloc(8192))) {
2345
2346 i = 4096;
2347 tempbufb = (u16 *)tempbuf;
2348 while (i--)
2349 *(tempbufb++) = 0x0720;
2350
2351 i = 0;
2352 tempbufb = (u16 *)tempbuf;
2353 while (bootlogo[i]) {
2354 *(tempbufb++) = 0x0700 | bootlogo[i++];
2355 if (!(i % 4))
2356 tempbufb += 76;
2357 }
2358
2359 i = 0;
2360 tempbufb = (u16 *)tempbuf + 6;
2361 while (bootstring[i])
2362 *(tempbufb++) = 0x0700 | bootstring[i++];
2363
2364 ret |= sisusb_copy_memory(sisusb, tempbuf,
2365 sisusb->vrambase, 8192, &written);
2366
2367 vfree(tempbuf);
2368
2369 }
2370
2371 } else if (sisusb->scrbuf) {
2372
2373 ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf,
2374 sisusb->vrambase, sisusb->scrbuf_size, &written);
2375
2376 }
2377
2378 if (sisusb->sisusb_cursor_size_from >= 0 &&
2379 sisusb->sisusb_cursor_size_to >= 0) {
2380 sisusb_setidxreg(sisusb, SISCR, 0x0a,
2381 sisusb->sisusb_cursor_size_from);
2382 sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0,
2383 sisusb->sisusb_cursor_size_to);
2384 } else {
2385 sisusb_setidxreg(sisusb, SISCR, 0x0a, 0x2d);
2386 sisusb_setidxreg(sisusb, SISCR, 0x0b, 0x0e);
2387 sisusb->sisusb_cursor_size_to = -1;
2388 }
2389
2390 slot = sisusb->sisusb_cursor_loc;
2391 if(slot < 0) slot = 0;
2392
2393 sisusb->sisusb_cursor_loc = -1;
2394 sisusb->bad_cursor_pos = 1;
2395
2396 sisusb_set_cursor(sisusb, slot);
2397
2398 sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
2399 sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
2400
2401 sisusb->textmodedestroyed = 0;
2402
2403 /* sisusb->lock is down */
2404
2405 return ret;
2406}
2407
2408#endif
2409
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410/* fops */
2411
2412static int
2413sisusb_open(struct inode *inode, struct file *file)
2414{
2415 struct sisusb_usb_data *sisusb;
2416 struct usb_interface *interface;
2417 int subminor = iminor(inode);
2418
Oliver Neukum86266452010-01-13 15:33:15 +01002419 lock_kernel();
2420 if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
2421 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422 return -ENODEV;
Oliver Neukum86266452010-01-13 15:33:15 +01002423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424
Oliver Neukum86266452010-01-13 15:33:15 +01002425 if (!(sisusb = usb_get_intfdata(interface))) {
2426 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 return -ENODEV;
Oliver Neukum86266452010-01-13 15:33:15 +01002428 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429
Arjan van de Ven2682d272006-03-28 01:00:21 -08002430 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431
2432 if (!sisusb->present || !sisusb->ready) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002433 mutex_unlock(&sisusb->lock);
Oliver Neukum86266452010-01-13 15:33:15 +01002434 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 return -ENODEV;
2436 }
2437
2438 if (sisusb->isopen) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002439 mutex_unlock(&sisusb->lock);
Oliver Neukum86266452010-01-13 15:33:15 +01002440 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441 return -EBUSY;
2442 }
2443
2444 if (!sisusb->devinit) {
2445 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
2446 if (sisusb_init_gfxdevice(sisusb, 0)) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002447 mutex_unlock(&sisusb->lock);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002448 dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n");
Oliver Neukum86266452010-01-13 15:33:15 +01002449 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 return -EIO;
2451 }
2452 } else {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002453 mutex_unlock(&sisusb->lock);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04002454 dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n");
Oliver Neukum86266452010-01-13 15:33:15 +01002455 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 return -EIO;
2457 }
2458 }
2459
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002460 /* Increment usage count for our sisusb */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 kref_get(&sisusb->kref);
2462
2463 sisusb->isopen = 1;
2464
2465 file->private_data = sisusb;
2466
Arjan van de Ven2682d272006-03-28 01:00:21 -08002467 mutex_unlock(&sisusb->lock);
Oliver Neukum86266452010-01-13 15:33:15 +01002468 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 return 0;
2471}
2472
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002473void
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474sisusb_delete(struct kref *kref)
2475{
2476 struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
2477
2478 if (!sisusb)
2479 return;
2480
2481 if (sisusb->sisusb_dev)
2482 usb_put_dev(sisusb->sisusb_dev);
2483
2484 sisusb->sisusb_dev = NULL;
2485 sisusb_free_buffers(sisusb);
2486 sisusb_free_urbs(sisusb);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002487#ifdef INCL_SISUSB_CON
2488 kfree(sisusb->SiS_Pr);
2489#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 kfree(sisusb);
2491}
2492
2493static int
2494sisusb_release(struct inode *inode, struct file *file)
2495{
2496 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497
Alan Sternd4ead162007-05-22 11:46:41 -04002498 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500
Arjan van de Ven2682d272006-03-28 01:00:21 -08002501 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502
2503 if (sisusb->present) {
2504 /* Wait for all URBs to finish if device still present */
2505 if (!sisusb_wait_all_out_complete(sisusb))
2506 sisusb_kill_all_busy(sisusb);
2507 }
2508
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509 sisusb->isopen = 0;
2510 file->private_data = NULL;
2511
Arjan van de Ven2682d272006-03-28 01:00:21 -08002512 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513
2514 /* decrement the usage count on our device */
2515 kref_put(&sisusb->kref, sisusb_delete);
2516
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517 return 0;
2518}
2519
2520static ssize_t
2521sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
2522{
2523 struct sisusb_usb_data *sisusb;
2524 ssize_t bytes_read = 0;
2525 int errno = 0;
2526 u8 buf8;
2527 u16 buf16;
2528 u32 buf32, address;
2529
2530 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2531 return -ENODEV;
2532
Arjan van de Ven2682d272006-03-28 01:00:21 -08002533 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
2535 /* Sanity check */
2536 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002537 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538 return -ENODEV;
2539 }
2540
2541 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2542 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2543
2544 address = (*ppos) -
2545 SISUSB_PCI_PSEUDO_IOPORTBASE +
2546 SISUSB_PCI_IOPORTBASE;
2547
2548 /* Read i/o ports
2549 * Byte, word and long(32) can be read. As this
2550 * emulates inX instructions, the data returned is
2551 * in machine-endianness.
2552 */
2553 switch (count) {
2554
2555 case 1:
2556 if (sisusb_read_memio_byte(sisusb,
2557 SISUSB_TYPE_IO,
2558 address, &buf8))
2559 errno = -EIO;
2560 else if (put_user(buf8, (u8 __user *)buffer))
2561 errno = -EFAULT;
2562 else
2563 bytes_read = 1;
2564
2565 break;
2566
2567 case 2:
2568 if (sisusb_read_memio_word(sisusb,
2569 SISUSB_TYPE_IO,
2570 address, &buf16))
2571 errno = -EIO;
2572 else if (put_user(buf16, (u16 __user *)buffer))
2573 errno = -EFAULT;
2574 else
2575 bytes_read = 2;
2576
2577 break;
2578
2579 case 4:
2580 if (sisusb_read_memio_long(sisusb,
2581 SISUSB_TYPE_IO,
2582 address, &buf32))
2583 errno = -EIO;
2584 else if (put_user(buf32, (u32 __user *)buffer))
2585 errno = -EFAULT;
2586 else
2587 bytes_read = 4;
2588
2589 break;
2590
2591 default:
2592 errno = -EIO;
2593
2594 }
2595
2596 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2597 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2598
2599 address = (*ppos) -
2600 SISUSB_PCI_PSEUDO_MEMBASE +
2601 SISUSB_PCI_MEMBASE;
2602
2603 /* Read video ram
2604 * Remember: Data delivered is never endian-corrected
2605 */
2606 errno = sisusb_read_mem_bulk(sisusb, address,
2607 NULL, count, buffer, &bytes_read);
2608
2609 if (bytes_read)
2610 errno = bytes_read;
2611
2612 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2613 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2614
2615 address = (*ppos) -
2616 SISUSB_PCI_PSEUDO_MMIOBASE +
2617 SISUSB_PCI_MMIOBASE;
2618
2619 /* Read MMIO
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_PCIBASE &&
2629 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
2630
2631 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002632 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 return -EINVAL;
2634 }
2635
2636 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2637
2638 /* Read PCI config register
2639 * Return value delivered in machine endianness.
2640 */
2641 if (sisusb_read_pci_config(sisusb, address, &buf32))
2642 errno = -EIO;
2643 else if (put_user(buf32, (u32 __user *)buffer))
2644 errno = -EFAULT;
2645 else
2646 bytes_read = 4;
2647
2648 } else {
2649
2650 errno = -EBADFD;
2651
2652 }
2653
2654 (*ppos) += bytes_read;
2655
Arjan van de Ven2682d272006-03-28 01:00:21 -08002656 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657
2658 return errno ? errno : bytes_read;
2659}
2660
2661static ssize_t
2662sisusb_write(struct file *file, const char __user *buffer, size_t count,
2663 loff_t *ppos)
2664{
2665 struct sisusb_usb_data *sisusb;
2666 int errno = 0;
2667 ssize_t bytes_written = 0;
2668 u8 buf8;
2669 u16 buf16;
2670 u32 buf32, address;
2671
2672 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2673 return -ENODEV;
2674
Arjan van de Ven2682d272006-03-28 01:00:21 -08002675 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676
2677 /* Sanity check */
2678 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002679 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 return -ENODEV;
2681 }
2682
2683 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
2684 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
2685
2686 address = (*ppos) -
2687 SISUSB_PCI_PSEUDO_IOPORTBASE +
2688 SISUSB_PCI_IOPORTBASE;
2689
2690 /* Write i/o ports
2691 * Byte, word and long(32) can be written. As this
2692 * emulates outX instructions, the data is expected
2693 * in machine-endianness.
2694 */
2695 switch (count) {
2696
2697 case 1:
2698 if (get_user(buf8, (u8 __user *)buffer))
2699 errno = -EFAULT;
2700 else if (sisusb_write_memio_byte(sisusb,
2701 SISUSB_TYPE_IO,
2702 address, buf8))
2703 errno = -EIO;
2704 else
2705 bytes_written = 1;
2706
2707 break;
2708
2709 case 2:
2710 if (get_user(buf16, (u16 __user *)buffer))
2711 errno = -EFAULT;
2712 else if (sisusb_write_memio_word(sisusb,
2713 SISUSB_TYPE_IO,
2714 address, buf16))
2715 errno = -EIO;
2716 else
2717 bytes_written = 2;
2718
2719 break;
2720
2721 case 4:
2722 if (get_user(buf32, (u32 __user *)buffer))
2723 errno = -EFAULT;
2724 else if (sisusb_write_memio_long(sisusb,
2725 SISUSB_TYPE_IO,
2726 address, buf32))
2727 errno = -EIO;
2728 else
2729 bytes_written = 4;
2730
2731 break;
2732
2733 default:
2734 errno = -EIO;
2735 }
2736
2737 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
2738 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
2739
2740 address = (*ppos) -
2741 SISUSB_PCI_PSEUDO_MEMBASE +
2742 SISUSB_PCI_MEMBASE;
2743
2744 /* Write video ram.
2745 * Buffer is copied 1:1, therefore, on big-endian
2746 * machines, the data must be swapped by userland
2747 * in advance (if applicable; no swapping in 8bpp
2748 * mode or if YUV data is being transferred).
2749 */
2750 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2751 count, buffer, 0, &bytes_written);
2752
2753 if (bytes_written)
2754 errno = bytes_written;
2755
2756 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
2757 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
2758
2759 address = (*ppos) -
2760 SISUSB_PCI_PSEUDO_MMIOBASE +
2761 SISUSB_PCI_MMIOBASE;
2762
2763 /* Write MMIO.
2764 * Buffer is copied 1:1, therefore, on big-endian
2765 * machines, the data must be swapped by userland
2766 * in advance.
2767 */
2768 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
2769 count, buffer, 0, &bytes_written);
2770
2771 if (bytes_written)
2772 errno = bytes_written;
2773
2774 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
2775 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
2776
2777 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002778 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 return -EINVAL;
2780 }
2781
2782 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2783
2784 /* Write PCI config register.
2785 * Given value expected in machine endianness.
2786 */
2787 if (get_user(buf32, (u32 __user *)buffer))
2788 errno = -EFAULT;
2789 else if (sisusb_write_pci_config(sisusb, address, buf32))
2790 errno = -EIO;
2791 else
2792 bytes_written = 4;
2793
2794
2795 } else {
2796
2797 /* Error */
2798 errno = -EBADFD;
2799
2800 }
2801
2802 (*ppos) += bytes_written;
2803
Arjan van de Ven2682d272006-03-28 01:00:21 -08002804 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805
2806 return errno ? errno : bytes_written;
2807}
2808
2809static loff_t
2810sisusb_lseek(struct file *file, loff_t offset, int orig)
2811{
2812 struct sisusb_usb_data *sisusb;
2813 loff_t ret;
2814
2815 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2816 return -ENODEV;
2817
Arjan van de Ven2682d272006-03-28 01:00:21 -08002818 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819
2820 /* Sanity check */
2821 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002822 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 return -ENODEV;
2824 }
2825
2826 switch (orig) {
2827 case 0:
2828 file->f_pos = offset;
2829 ret = file->f_pos;
2830 /* never negative, no force_successful_syscall needed */
2831 break;
2832 case 1:
2833 file->f_pos += offset;
2834 ret = file->f_pos;
2835 /* never negative, no force_successful_syscall needed */
2836 break;
2837 default:
2838 /* seeking relative to "end of file" is not supported */
2839 ret = -EINVAL;
2840 }
2841
Arjan van de Ven2682d272006-03-28 01:00:21 -08002842 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 return ret;
2844}
2845
2846static int
2847sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
2848 unsigned long arg)
2849{
Felipe Balbied86d972007-08-10 09:34:24 -04002850 int retval, port, length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 u32 address;
2852
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002853 /* All our commands require the device
2854 * to be initialized.
2855 */
2856 if (!sisusb->devinit)
2857 return -ENODEV;
2858
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 port = y->data3 -
2860 SISUSB_PCI_PSEUDO_IOPORTBASE +
2861 SISUSB_PCI_IOPORTBASE;
2862
2863 switch (y->operation) {
2864 case SUCMD_GET:
2865 retval = sisusb_getidxreg(sisusb, port,
2866 y->data0, &y->data1);
2867 if (!retval) {
2868 if (copy_to_user((void __user *)arg, y,
2869 sizeof(*y)))
2870 retval = -EFAULT;
2871 }
2872 break;
2873
2874 case SUCMD_SET:
2875 retval = sisusb_setidxreg(sisusb, port,
2876 y->data0, y->data1);
2877 break;
2878
2879 case SUCMD_SETOR:
2880 retval = sisusb_setidxregor(sisusb, port,
2881 y->data0, y->data1);
2882 break;
2883
2884 case SUCMD_SETAND:
2885 retval = sisusb_setidxregand(sisusb, port,
2886 y->data0, y->data1);
2887 break;
2888
2889 case SUCMD_SETANDOR:
2890 retval = sisusb_setidxregandor(sisusb, port,
2891 y->data0, y->data1, y->data2);
2892 break;
2893
2894 case SUCMD_SETMASK:
2895 retval = sisusb_setidxregmask(sisusb, port,
2896 y->data0, y->data1, y->data2);
2897 break;
2898
2899 case SUCMD_CLRSCR:
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002900 /* Gfx core must be initialized */
2901 if (!sisusb->gfxinit)
2902 return -ENODEV;
2903
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904 length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
2905 address = y->data3 -
2906 SISUSB_PCI_PSEUDO_MEMBASE +
2907 SISUSB_PCI_MEMBASE;
2908 retval = sisusb_clear_vram(sisusb, address, length);
2909 break;
2910
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002911 case SUCMD_HANDLETEXTMODE:
2912 retval = 0;
2913#ifdef INCL_SISUSB_CON
2914 /* Gfx core must be initialized, SiS_Pr must exist */
2915 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2916 return -ENODEV;
2917
2918 switch (y->data0) {
2919 case 0:
2920 retval = sisusb_reset_text_mode(sisusb, 0);
2921 break;
2922 case 1:
2923 sisusb->textmodedestroyed = 1;
2924 break;
2925 }
2926#endif
2927 break;
2928
2929#ifdef INCL_SISUSB_CON
2930 case SUCMD_SETMODE:
2931 /* Gfx core must be initialized, SiS_Pr must exist */
2932 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2933 return -ENODEV;
2934
2935 retval = 0;
2936
2937 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2938 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2939
2940 if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3))
2941 retval = -EINVAL;
2942
2943 break;
2944
2945 case SUCMD_SETVESAMODE:
2946 /* Gfx core must be initialized, SiS_Pr must exist */
2947 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2948 return -ENODEV;
2949
2950 retval = 0;
2951
2952 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2953 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2954
2955 if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3))
2956 retval = -EINVAL;
2957
2958 break;
2959#endif
2960
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 default:
2962 retval = -EINVAL;
2963 }
2964
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002965 if (retval > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966 retval = -EIO;
2967
2968 return retval;
2969}
2970
Alan Cox49f15252008-05-22 22:48:48 +01002971static long
2972sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973{
2974 struct sisusb_usb_data *sisusb;
2975 struct sisusb_info x;
2976 struct sisusb_command y;
Felipe Balbied86d972007-08-10 09:34:24 -04002977 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002978 u32 __user *argp = (u32 __user *)arg;
2979
2980 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2981 return -ENODEV;
2982
Alan Cox49f15252008-05-22 22:48:48 +01002983 lock_kernel();
Arjan van de Ven2682d272006-03-28 01:00:21 -08002984 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985
2986 /* Sanity check */
2987 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
2988 retval = -ENODEV;
2989 goto err_out;
2990 }
2991
2992 switch (cmd) {
2993
2994 case SISUSB_GET_CONFIG_SIZE:
2995
2996 if (put_user(sizeof(x), argp))
2997 retval = -EFAULT;
2998
2999 break;
3000
3001 case SISUSB_GET_CONFIG:
3002
Felipe Balbied86d972007-08-10 09:34:24 -04003003 x.sisusb_id = SISUSB_ID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003004 x.sisusb_version = SISUSB_VERSION;
3005 x.sisusb_revision = SISUSB_REVISION;
3006 x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
3007 x.sisusb_gfxinit = sisusb->gfxinit;
3008 x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE;
3009 x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE;
3010 x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE;
3011 x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE;
3012 x.sisusb_vramsize = sisusb->vramsize;
3013 x.sisusb_minor = sisusb->minor;
3014 x.sisusb_fbdevactive= 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003015#ifdef INCL_SISUSB_CON
3016 x.sisusb_conactive = sisusb->haveconsole ? 1 : 0;
3017#else
3018 x.sisusb_conactive = 0;
3019#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020
3021 if (copy_to_user((void __user *)arg, &x, sizeof(x)))
3022 retval = -EFAULT;
3023
3024 break;
3025
3026 case SISUSB_COMMAND:
3027
3028 if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
3029 retval = -EFAULT;
3030 else
3031 retval = sisusb_handle_command(sisusb, &y, arg);
3032
3033 break;
3034
3035 default:
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003036 retval = -ENOTTY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003037 break;
3038 }
3039
3040err_out:
Arjan van de Ven2682d272006-03-28 01:00:21 -08003041 mutex_unlock(&sisusb->lock);
Alan Cox49f15252008-05-22 22:48:48 +01003042 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043 return retval;
3044}
3045
3046#ifdef SISUSB_NEW_CONFIG_COMPAT
3047static long
3048sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
3049{
3050 long retval;
3051
3052 switch (cmd) {
3053 case SISUSB_GET_CONFIG_SIZE:
3054 case SISUSB_GET_CONFIG:
3055 case SISUSB_COMMAND:
Alan Cox49f15252008-05-22 22:48:48 +01003056 retval = sisusb_ioctl(f, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 return retval;
3058
3059 default:
3060 return -ENOIOCTLCMD;
3061 }
3062}
3063#endif
3064
Luiz Fernando N. Capitulino066202d2006-08-05 20:37:11 -03003065static const struct file_operations usb_sisusb_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066 .owner = THIS_MODULE,
3067 .open = sisusb_open,
3068 .release = sisusb_release,
3069 .read = sisusb_read,
3070 .write = sisusb_write,
Felipe Balbied86d972007-08-10 09:34:24 -04003071 .llseek = sisusb_lseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072#ifdef SISUSB_NEW_CONFIG_COMPAT
3073 .compat_ioctl = sisusb_compat_ioctl,
3074#endif
Alan Cox49f15252008-05-22 22:48:48 +01003075 .unlocked_ioctl = sisusb_ioctl
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076};
3077
3078static struct usb_class_driver usb_sisusb_class = {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003079 .name = "sisusbvga%d",
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003080 .fops = &usb_sisusb_fops,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003081 .minor_base = SISUSB_MINOR
3082};
3083
3084static int sisusb_probe(struct usb_interface *intf,
3085 const struct usb_device_id *id)
3086{
3087 struct usb_device *dev = interface_to_usbdev(intf);
3088 struct sisusb_usb_data *sisusb;
3089 int retval = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003091 dev_info(&dev->dev, "USB2VGA dongle found at address %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092 dev->devnum);
3093
3094 /* Allocate memory for our private */
Oliver Neukum9ee884c2006-01-06 23:27:17 +01003095 if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003096 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for private data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 return -ENOMEM;
3098 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099 kref_init(&sisusb->kref);
3100
Arjan van de Ven2682d272006-03-28 01:00:21 -08003101 mutex_init(&(sisusb->lock));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003102
3103 /* Register device */
3104 if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003105 dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106 dev->devnum);
3107 retval = -ENODEV;
3108 goto error_1;
3109 }
3110
3111 sisusb->sisusb_dev = dev;
3112 sisusb->minor = intf->minor;
3113 sisusb->vrambase = SISUSB_PCI_MEMBASE;
3114 sisusb->mmiobase = SISUSB_PCI_MMIOBASE;
3115 sisusb->mmiosize = SISUSB_PCI_MMIOSIZE;
3116 sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
3117 /* Everything else is zero */
3118
3119 /* Allocate buffers */
3120 sisusb->ibufsize = SISUSB_IBUF_SIZE;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -06003121 if (!(sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003122 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003123 retval = -ENOMEM;
3124 goto error_2;
3125 }
3126
3127 sisusb->numobufs = 0;
3128 sisusb->obufsize = SISUSB_OBUF_SIZE;
3129 for (i = 0; i < NUMOBUFS; i++) {
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -06003130 if (!(sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003131 if (i == 0) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003132 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003133 retval = -ENOMEM;
3134 goto error_3;
3135 }
3136 break;
3137 } else
3138 sisusb->numobufs++;
3139
3140 }
3141
3142 /* Allocate URBs */
3143 if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003144 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 retval = -ENOMEM;
3146 goto error_3;
3147 }
3148 sisusb->completein = 1;
3149
3150 for (i = 0; i < sisusb->numobufs; i++) {
3151 if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003152 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003153 retval = -ENOMEM;
3154 goto error_4;
3155 }
3156 sisusb->urbout_context[i].sisusb = (void *)sisusb;
3157 sisusb->urbout_context[i].urbindex = i;
3158 sisusb->urbstatus[i] = 0;
3159 }
3160
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003161 dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", sisusb->numobufs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003162
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003163#ifdef INCL_SISUSB_CON
3164 /* Allocate our SiS_Pr */
3165 if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003166 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate SiS_Pr\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003167 }
3168#endif
3169
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170 /* Do remaining init stuff */
3171
3172 init_waitqueue_head(&sisusb->wait_q);
3173
3174 usb_set_intfdata(intf, sisusb);
3175
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003176 usb_get_dev(sisusb->sisusb_dev);
3177
3178 sisusb->present = 1;
3179
Linus Torvalds1da177e2005-04-16 15:20:36 -07003180 if (dev->speed == USB_SPEED_HIGH) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003181 int initscreen = 1;
3182#ifdef INCL_SISUSB_CON
3183 if (sisusb_first_vc > 0 &&
3184 sisusb_last_vc > 0 &&
3185 sisusb_first_vc <= sisusb_last_vc &&
3186 sisusb_last_vc <= MAX_NR_CONSOLES)
3187 initscreen = 0;
3188#endif
3189 if (sisusb_init_gfxdevice(sisusb, initscreen))
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003190 dev_err(&sisusb->sisusb_dev->dev, "Failed to early initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191
3192 } else
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003193 dev_info(&sisusb->sisusb_dev->dev, "Not attached to USB 2.0 hub, deferring init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194
3195 sisusb->ready = 1;
3196
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003197#ifdef SISUSBENDIANTEST
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003198 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003199 sisusb_testreadwrite(sisusb);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003200 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003201#endif
3202
3203#ifdef INCL_SISUSB_CON
3204 sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
3205#endif
3206
Linus Torvalds1da177e2005-04-16 15:20:36 -07003207 return 0;
3208
3209error_4:
3210 sisusb_free_urbs(sisusb);
3211error_3:
3212 sisusb_free_buffers(sisusb);
3213error_2:
3214 usb_deregister_dev(intf, &usb_sisusb_class);
3215error_1:
3216 kfree(sisusb);
3217 return retval;
3218}
3219
3220static void sisusb_disconnect(struct usb_interface *intf)
3221{
3222 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224 /* This should *not* happen */
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003225 if (!(sisusb = usb_get_intfdata(intf)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 return;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003227
3228#ifdef INCL_SISUSB_CON
3229 sisusb_console_exit(sisusb);
3230#endif
3231
Alan Sternd4ead162007-05-22 11:46:41 -04003232 usb_deregister_dev(intf, &usb_sisusb_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003233
Arjan van de Ven2682d272006-03-28 01:00:21 -08003234 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003235
3236 /* Wait for all URBs to complete and kill them in case (MUST do) */
3237 if (!sisusb_wait_all_out_complete(sisusb))
3238 sisusb_kill_all_busy(sisusb);
3239
Linus Torvalds1da177e2005-04-16 15:20:36 -07003240 usb_set_intfdata(intf, NULL);
3241
Linus Torvalds1da177e2005-04-16 15:20:36 -07003242 sisusb->present = 0;
3243 sisusb->ready = 0;
3244
Arjan van de Ven2682d272006-03-28 01:00:21 -08003245 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246
3247 /* decrement our usage count */
3248 kref_put(&sisusb->kref, sisusb_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003249}
3250
Németh Márton33b9e162010-01-10 15:34:45 +01003251static const struct usb_device_id sisusb_table[] = {
samson yeungca9024e2007-08-31 16:40:40 -04003252 { USB_DEVICE(0x0711, 0x0550) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003253 { USB_DEVICE(0x0711, 0x0900) },
Nobuhiro Iwamatsu3003b9f2006-09-01 11:32:28 +09003254 { USB_DEVICE(0x0711, 0x0901) },
3255 { USB_DEVICE(0x0711, 0x0902) },
Albert Comerma859ff402008-11-04 10:44:01 -08003256 { USB_DEVICE(0x0711, 0x0903) },
Stefan Lippers-Hollmanneaea0432008-08-21 13:46:11 +02003257 { USB_DEVICE(0x0711, 0x0918) },
Tanaka Akirabbcb8bb2010-01-21 02:31:09 +09003258 { USB_DEVICE(0x0711, 0x0920) },
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003259 { USB_DEVICE(0x182d, 0x021c) },
Thomas Winischhofercef11122005-04-22 15:06:59 -07003260 { USB_DEVICE(0x182d, 0x0269) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003261 { }
3262};
3263
3264MODULE_DEVICE_TABLE (usb, sisusb_table);
3265
3266static struct usb_driver sisusb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003267 .name = "sisusb",
3268 .probe = sisusb_probe,
3269 .disconnect = sisusb_disconnect,
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003270 .id_table = sisusb_table,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271};
3272
3273static int __init usb_sisusb_init(void)
3274{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003275
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003276#ifdef INCL_SISUSB_CON
3277 sisusb_init_concode();
3278#endif
3279
Andrew Morton9dcfbd92007-10-02 14:40:46 -07003280 return usb_register(&sisusb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003281}
3282
3283static void __exit usb_sisusb_exit(void)
3284{
3285 usb_deregister(&sisusb_driver);
3286}
3287
3288module_init(usb_sisusb_init);
3289module_exit(usb_sisusb_exit);
3290
3291MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003292MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293MODULE_LICENSE("GPL");
3294