blob: 9560fde621ee5a8116912f9ee201b25db4c526ee [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
4 *
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02005 * Main part
6 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
8 *
9 * If distributed as part of the Linux kernel, this code is licensed under the
10 * terms of the GPL v2.
11 *
12 * Otherwise, the following license terms apply:
13 *
14 * * Redistribution and use in source and binary forms, with or without
15 * * modification, are permitted provided that the following conditions
16 * * are met:
17 * * 1) Redistributions of source code must retain the above copyright
18 * * notice, this list of conditions and the following disclaimer.
19 * * 2) Redistributions in binary form must reproduce the above copyright
20 * * notice, this list of conditions and the following disclaimer in the
21 * * documentation and/or other materials provided with the distribution.
22 * * 3) The name of the author may not be used to endorse or promote products
23 * * derived from this software without specific psisusbr written permission.
24 * *
25 * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
26 * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
Felipe Balbied86d972007-08-10 09:34:24 -040036 * Author: Thomas Winischhofer <thomas@winischhofer.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 *
38 */
39
Arjan van de Ven2682d272006-03-28 01:00:21 -080040#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/module.h>
42#include <linux/kernel.h>
43#include <linux/signal.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/errno.h>
45#include <linux/poll.h>
46#include <linux/init.h>
47#include <linux/slab.h>
48#include <linux/spinlock.h>
49#include <linux/kref.h>
50#include <linux/usb.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 Bunkdf47e5332006-04-15 11:17:27 +020054#include "sisusb_init.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Jiri Slaby022e4682019-01-22 16:12:00 +010056#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +020057#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
Jiri Slaby022e4682019-01-22 16:12:00 +010064#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudinf996c492016-01-15 18:41:30 +010065static int sisusb_first_vc;
66static int sisusb_last_vc;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +020067module_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
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +010075static void sisusb_free_buffers(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
77 int i;
78
79 for (i = 0; i < NUMOBUFS; i++) {
Peter Senna Tschudin8a102fd2016-01-15 18:41:32 +010080 kfree(sisusb->obuf[i]);
81 sisusb->obuf[i] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 }
Peter Senna Tschudin8a102fd2016-01-15 18:41:32 +010083 kfree(sisusb->ibuf);
84 sisusb->ibuf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085}
86
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +010087static void sisusb_free_urbs(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
89 int i;
90
91 for (i = 0; i < NUMOBUFS; i++) {
92 usb_free_urb(sisusb->sisurbout[i]);
93 sisusb->sisurbout[i] = NULL;
94 }
95 usb_free_urb(sisusb->sisurbin);
96 sisusb->sisurbin = NULL;
97}
98
99/* Level 0: USB transport layer */
100
101/* 1. out-bulks */
102
103/* out-urb management */
104
105/* Return 1 if all free, 0 otherwise */
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100106static int sisusb_all_free(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107{
108 int i;
109
110 for (i = 0; i < sisusb->numobufs; i++) {
111
112 if (sisusb->urbstatus[i] & SU_URB_BUSY)
113 return 0;
114
115 }
116
117 return 1;
118}
119
120/* Kill all busy URBs */
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100121static void sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
123 int i;
124
125 if (sisusb_all_free(sisusb))
126 return;
127
128 for (i = 0; i < sisusb->numobufs; i++) {
129
130 if (sisusb->urbstatus[i] & SU_URB_BUSY)
131 usb_kill_urb(sisusb->sisurbout[i]);
132
133 }
134}
135
136/* Return 1 if ok, 0 if error (not all complete within timeout) */
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100137static int sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
139 int timeout = 5 * HZ, i = 1;
140
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100141 wait_event_timeout(sisusb->wait_q, (i = sisusb_all_free(sisusb)),
142 timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 return i;
145}
146
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100147static int sisusb_outurb_available(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
149 int i;
150
151 for (i = 0; i < sisusb->numobufs; i++) {
152
153 if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
154 return i;
155
156 }
157
158 return -1;
159}
160
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100161static int sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162{
163 int i, timeout = 5 * HZ;
164
165 wait_event_timeout(sisusb->wait_q,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100166 ((i = sisusb_outurb_available(sisusb)) >= 0), timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
168 return i;
169}
170
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100171static int sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
173 int i;
174
175 i = sisusb_outurb_available(sisusb);
176
177 if (i >= 0)
178 sisusb->urbstatus[i] |= SU_URB_ALLOC;
179
180 return i;
181}
182
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100183static void sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
185 if ((index >= 0) && (index < sisusb->numobufs))
186 sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
187}
188
189/* completion callback */
190
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100191static void sisusb_bulk_completeout(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
193 struct sisusb_urb_context *context = urb->context;
194 struct sisusb_usb_data *sisusb;
195
196 if (!context)
197 return;
198
199 sisusb = context->sisusb;
200
201 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
202 return;
203
204#ifndef SISUSB_DONTSYNC
205 if (context->actual_length)
206 *(context->actual_length) += urb->actual_length;
207#endif
208
209 sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
210 wake_up(&sisusb->wait_q);
211}
212
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100213static int sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index,
214 unsigned int pipe, void *data, int len, int *actual_length,
215 int timeout, unsigned int tflags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
217 struct urb *urb = sisusb->sisurbout[index];
218 int retval, byteswritten = 0;
219
220 /* Set up URB */
221 urb->transfer_flags = 0;
222
223 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100224 sisusb_bulk_completeout,
225 &sisusb->urbout_context[index]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
Alan Sternb375a042005-07-29 16:11:07 -0400227 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 urb->actual_length = 0;
229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 /* Set up context */
231 sisusb->urbout_context[index].actual_length = (timeout) ?
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100232 NULL : actual_length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
234 /* Declare this urb/buffer in use */
235 sisusb->urbstatus[index] |= SU_URB_BUSY;
236
237 /* Submit URB */
Oliver Neukum444dc542010-02-20 01:40:54 +0100238 retval = usb_submit_urb(urb, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
240 /* If OK, and if timeout > 0, wait for completion */
241 if ((retval == 0) && timeout) {
242 wait_event_timeout(sisusb->wait_q,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100243 (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
244 timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 if (sisusb->urbstatus[index] & SU_URB_BUSY) {
246 /* URB timed out... kill it and report error */
247 usb_kill_urb(urb);
248 retval = -ETIMEDOUT;
249 } else {
250 /* Otherwise, report urb status */
251 retval = urb->status;
252 byteswritten = urb->actual_length;
253 }
254 }
255
256 if (actual_length)
257 *actual_length = byteswritten;
258
259 return retval;
260}
261
262/* 2. in-bulks */
263
264/* completion callback */
265
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100266static void sisusb_bulk_completein(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
268 struct sisusb_usb_data *sisusb = urb->context;
269
270 if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
271 return;
272
273 sisusb->completein = 1;
274 wake_up(&sisusb->wait_q);
275}
276
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100277static int sisusb_bulkin_msg(struct sisusb_usb_data *sisusb,
278 unsigned int pipe, void *data, int len,
279 int *actual_length, int timeout, unsigned int tflags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
281 struct urb *urb = sisusb->sisurbin;
282 int retval, readbytes = 0;
283
284 urb->transfer_flags = 0;
285
286 usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
287 sisusb_bulk_completein, sisusb);
288
Alan Sternb375a042005-07-29 16:11:07 -0400289 urb->transfer_flags |= tflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 urb->actual_length = 0;
291
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 sisusb->completein = 0;
Oliver Neukum444dc542010-02-20 01:40:54 +0100293 retval = usb_submit_urb(urb, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 if (retval == 0) {
295 wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
296 if (!sisusb->completein) {
297 /* URB timed out... kill it and report error */
298 usb_kill_urb(urb);
299 retval = -ETIMEDOUT;
300 } else {
Joe Perchesdc0d5c12007-12-17 11:40:18 -0800301 /* URB completed within timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 retval = urb->status;
303 readbytes = urb->actual_length;
304 }
305 }
306
307 if (actual_length)
308 *actual_length = readbytes;
309
310 return retval;
311}
312
313
314/* Level 1: */
315
316/* Send a bulk message of variable size
317 *
318 * To copy the data from userspace, give pointer to "userbuffer",
319 * to copy from (non-DMA) kernel memory, give "kernbuffer". If
320 * both of these are NULL, it is assumed, that the transfer
321 * buffer "sisusb->obuf[index]" is set up with the data to send.
322 * Index is ignored if either kernbuffer or userbuffer is set.
323 * If async is nonzero, URBs will be sent without waiting for
324 * completion of the previous URB.
325 *
326 * (return 0 on success)
327 */
328
329static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
330 char *kernbuffer, const char __user *userbuffer, int index,
331 ssize_t *bytes_written, unsigned int tflags, int async)
332{
333 int result = 0, retry, count = len;
334 int passsize, thispass, transferred_len = 0;
335 int fromuser = (userbuffer != NULL) ? 1 : 0;
336 int fromkern = (kernbuffer != NULL) ? 1 : 0;
337 unsigned int pipe;
338 char *buffer;
339
340 (*bytes_written) = 0;
341
342 /* Sanity check */
343 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
344 return -ENODEV;
345
346 /* If we copy data from kernel or userspace, force the
347 * allocation of a buffer/urb. If we have the data in
348 * the transfer buffer[index] already, reuse the buffer/URB
349 * if the length is > buffer size. (So, transmitting
350 * large data amounts directly from the transfer buffer
351 * treats the buffer as a ring buffer. However, we need
352 * to sync in this case.)
353 */
354 if (fromuser || fromkern)
355 index = -1;
356 else if (len > sisusb->obufsize)
357 async = 0;
358
359 pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
360
361 do {
362 passsize = thispass = (sisusb->obufsize < count) ?
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100363 sisusb->obufsize : count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
365 if (index < 0)
366 index = sisusb_get_free_outbuf(sisusb);
367
368 if (index < 0)
369 return -EIO;
370
371 buffer = sisusb->obuf[index];
372
373 if (fromuser) {
374
375 if (copy_from_user(buffer, userbuffer, passsize))
376 return -EFAULT;
377
378 userbuffer += passsize;
379
380 } else if (fromkern) {
381
382 memcpy(buffer, kernbuffer, passsize);
383 kernbuffer += passsize;
384
385 }
386
387 retry = 5;
388 while (thispass) {
389
390 if (!sisusb->sisusb_dev)
391 return -ENODEV;
392
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100393 result = sisusb_bulkout_msg(sisusb, index, pipe,
394 buffer, thispass, &transferred_len,
395 async ? 0 : 5 * HZ, tflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
397 if (result == -ETIMEDOUT) {
398
399 /* Will not happen if async */
400 if (!retry--)
401 return -ETIME;
402
403 continue;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600404 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600406 if ((result == 0) && !async && transferred_len) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 thispass -= transferred_len;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600409 buffer += transferred_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411 } else
412 break;
Pete Zaitcevd2fb1bb2009-06-10 14:15:52 -0600413 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
415 if (result)
416 return result;
417
418 (*bytes_written) += passsize;
419 count -= passsize;
420
421 /* Force new allocation in next iteration */
422 if (fromuser || fromkern)
423 index = -1;
424
425 } while (count > 0);
426
427 if (async) {
428#ifdef SISUSB_DONTSYNC
429 (*bytes_written) = len;
430 /* Some URBs/buffers might be busy */
431#else
432 sisusb_wait_all_out_complete(sisusb);
433 (*bytes_written) = transferred_len;
434 /* All URBs and all buffers are available */
435#endif
436 }
437
438 return ((*bytes_written) == len) ? 0 : -EIO;
439}
440
441/* Receive a bulk message of variable size
442 *
443 * To copy the data to userspace, give pointer to "userbuffer",
444 * to copy to kernel memory, give "kernbuffer". One of them
445 * MUST be set. (There is no technique for letting the caller
446 * read directly from the ibuf.)
447 *
448 */
449
450static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
451 void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
452 unsigned int tflags)
453{
454 int result = 0, retry, count = len;
455 int bufsize, thispass, transferred_len;
456 unsigned int pipe;
457 char *buffer;
458
459 (*bytes_read) = 0;
460
461 /* Sanity check */
462 if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
463 return -ENODEV;
464
465 pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
466 buffer = sisusb->ibuf;
467 bufsize = sisusb->ibufsize;
468
469 retry = 5;
470
471#ifdef SISUSB_DONTSYNC
472 if (!(sisusb_wait_all_out_complete(sisusb)))
473 return -EIO;
474#endif
475
476 while (count > 0) {
477
478 if (!sisusb->sisusb_dev)
479 return -ENODEV;
480
481 thispass = (bufsize < count) ? bufsize : count;
482
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100483 result = sisusb_bulkin_msg(sisusb, pipe, buffer, thispass,
484 &transferred_len, 5 * HZ, tflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 if (transferred_len)
487 thispass = transferred_len;
488
489 else if (result == -ETIMEDOUT) {
490
491 if (!retry--)
492 return -ETIME;
493
494 continue;
495
496 } else
497 return -EIO;
498
499
500 if (thispass) {
501
502 (*bytes_read) += thispass;
503 count -= thispass;
504
505 if (userbuffer) {
506
507 if (copy_to_user(userbuffer, buffer, thispass))
508 return -EFAULT;
509
510 userbuffer += thispass;
511
512 } else {
513
514 memcpy(kernbuffer, buffer, thispass);
515 kernbuffer += thispass;
516
517 }
518
519 }
520
521 }
522
523 return ((*bytes_read) == len) ? 0 : -EIO;
524}
525
526static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100527 struct sisusb_packet *packet)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528{
529 int ret;
530 ssize_t bytes_transferred = 0;
531 __le32 tmp;
532
533 if (len == 6)
534 packet->data = 0;
535
536#ifdef SISUSB_DONTSYNC
537 if (!(sisusb_wait_all_out_complete(sisusb)))
538 return 1;
539#endif
540
541 /* Eventually correct endianness */
542 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
543
544 /* 1. send the packet */
545 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
546 (char *)packet, NULL, 0, &bytes_transferred, 0, 0);
547
548 if ((ret == 0) && (len == 6)) {
549
550 /* 2. if packet len == 6, it means we read, so wait for 32bit
551 * return value and write it to packet->data
552 */
553 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
554 (char *)&tmp, NULL, &bytes_transferred, 0);
555
556 packet->data = le32_to_cpu(tmp);
557 }
558
559 return ret;
560}
561
562static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100563 struct sisusb_packet *packet, unsigned int tflags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
565 int ret;
566 ssize_t bytes_transferred = 0;
567 __le32 tmp;
568
569 if (len == 6)
570 packet->data = 0;
571
572#ifdef SISUSB_DONTSYNC
573 if (!(sisusb_wait_all_out_complete(sisusb)))
574 return 1;
575#endif
576
577 /* Eventually correct endianness */
578 SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
579
580 /* 1. send the packet */
581 ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
582 (char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
583
584 if ((ret == 0) && (len == 6)) {
585
586 /* 2. if packet len == 6, it means we read, so wait for 32bit
587 * return value and write it to packet->data
588 */
589 ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
590 (char *)&tmp, NULL, &bytes_transferred, 0);
591
592 packet->data = le32_to_cpu(tmp);
593 }
594
595 return ret;
596}
597
598/* access video memory and mmio (return 0 on success) */
599
600/* Low level */
601
602/* The following routines assume being used to transfer byte, word,
603 * long etc.
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200604 * This means that
605 * - the write routines expect "data" in machine endianness format.
606 * The data will be converted to leXX in sisusb_xxx_packet.
607 * - the read routines can expect read data in machine-endianess.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 */
609
610static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100611 u32 addr, u8 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
613 struct sisusb_packet packet;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 packet.header = (1 << (addr & 3)) | (type << 6);
616 packet.address = addr & ~3;
617 packet.data = data << ((addr & 3) << 3);
Gustavo A. R. Silvae6f9e132017-07-09 22:31:31 -0500618 return sisusb_send_packet(sisusb, 10, &packet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619}
620
621static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100622 u32 addr, u16 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623{
624 struct sisusb_packet packet;
625 int ret = 0;
626
627 packet.address = addr & ~3;
628
629 switch (addr & 3) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100630 case 0:
631 packet.header = (type << 6) | 0x0003;
632 packet.data = (u32)data;
633 ret = sisusb_send_packet(sisusb, 10, &packet);
634 break;
635 case 1:
636 packet.header = (type << 6) | 0x0006;
637 packet.data = (u32)data << 8;
638 ret = sisusb_send_packet(sisusb, 10, &packet);
639 break;
640 case 2:
641 packet.header = (type << 6) | 0x000c;
642 packet.data = (u32)data << 16;
643 ret = sisusb_send_packet(sisusb, 10, &packet);
644 break;
645 case 3:
646 packet.header = (type << 6) | 0x0008;
647 packet.data = (u32)data << 24;
648 ret = sisusb_send_packet(sisusb, 10, &packet);
649 packet.header = (type << 6) | 0x0001;
650 packet.address = (addr & ~3) + 4;
651 packet.data = (u32)data >> 8;
652 ret |= sisusb_send_packet(sisusb, 10, &packet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 }
654
655 return ret;
656}
657
658static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100659 u32 addr, u32 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 struct sisusb_packet packet;
662 int ret = 0;
663
664 packet.address = addr & ~3;
665
666 switch (addr & 3) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100667 case 0:
668 packet.header = (type << 6) | 0x0007;
669 packet.data = data & 0x00ffffff;
670 ret = sisusb_send_packet(sisusb, 10, &packet);
671 break;
672 case 1:
673 packet.header = (type << 6) | 0x000e;
674 packet.data = data << 8;
675 ret = sisusb_send_packet(sisusb, 10, &packet);
676 break;
677 case 2:
678 packet.header = (type << 6) | 0x000c;
679 packet.data = data << 16;
680 ret = sisusb_send_packet(sisusb, 10, &packet);
681 packet.header = (type << 6) | 0x0001;
682 packet.address = (addr & ~3) + 4;
683 packet.data = (data >> 16) & 0x00ff;
684 ret |= sisusb_send_packet(sisusb, 10, &packet);
685 break;
686 case 3:
687 packet.header = (type << 6) | 0x0008;
688 packet.data = data << 24;
689 ret = sisusb_send_packet(sisusb, 10, &packet);
690 packet.header = (type << 6) | 0x0003;
691 packet.address = (addr & ~3) + 4;
692 packet.data = (data >> 8) & 0xffff;
693 ret |= sisusb_send_packet(sisusb, 10, &packet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 }
695
696 return ret;
697}
698
699static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100700 u32 addr, u32 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701{
702 struct sisusb_packet packet;
703 int ret = 0;
704
705 packet.address = addr & ~3;
706
707 switch (addr & 3) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100708 case 0:
709 packet.header = (type << 6) | 0x000f;
710 packet.data = data;
711 ret = sisusb_send_packet(sisusb, 10, &packet);
712 break;
713 case 1:
714 packet.header = (type << 6) | 0x000e;
715 packet.data = data << 8;
716 ret = sisusb_send_packet(sisusb, 10, &packet);
717 packet.header = (type << 6) | 0x0001;
718 packet.address = (addr & ~3) + 4;
719 packet.data = data >> 24;
720 ret |= sisusb_send_packet(sisusb, 10, &packet);
721 break;
722 case 2:
723 packet.header = (type << 6) | 0x000c;
724 packet.data = data << 16;
725 ret = sisusb_send_packet(sisusb, 10, &packet);
726 packet.header = (type << 6) | 0x0003;
727 packet.address = (addr & ~3) + 4;
728 packet.data = data >> 16;
729 ret |= sisusb_send_packet(sisusb, 10, &packet);
730 break;
731 case 3:
732 packet.header = (type << 6) | 0x0008;
733 packet.data = data << 24;
734 ret = sisusb_send_packet(sisusb, 10, &packet);
735 packet.header = (type << 6) | 0x0007;
736 packet.address = (addr & ~3) + 4;
737 packet.data = data >> 8;
738 ret |= sisusb_send_packet(sisusb, 10, &packet);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 }
740
741 return ret;
742}
743
744/* The xxx_bulk routines copy a buffer of variable size. They treat the
745 * buffer as chars, therefore lsb/msb has to be corrected if using the
746 * byte/word/long/etc routines for speed-up
747 *
748 * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
749 * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
750 * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
751 * that the data already is in the transfer buffer "sisusb->obuf[index]".
752 */
753
754static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100755 char *kernbuffer, int length, const char __user *userbuffer,
756 int index, ssize_t *bytes_written)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
758 struct sisusb_packet packet;
759 int ret = 0;
Peter Senna Tschudinf996c492016-01-15 18:41:30 +0100760 static int msgcount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 u8 swap8, fromkern = kernbuffer ? 1 : 0;
762 u16 swap16;
763 u32 swap32, flag = (length >> 28) & 1;
764 char buf[4];
765
766 /* if neither kernbuffer not userbuffer are given, assume
767 * data in obuf
768 */
769 if (!fromkern && !userbuffer)
770 kernbuffer = sisusb->obuf[index];
771
772 (*bytes_written = 0);
773
774 length &= 0x00ffffff;
775
776 while (length) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100777 switch (length) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 case 1:
779 if (userbuffer) {
780 if (get_user(swap8, (u8 __user *)userbuffer))
781 return -EFAULT;
782 } else
783 swap8 = kernbuffer[0];
784
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100785 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM,
786 addr, swap8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788 if (!ret)
789 (*bytes_written)++;
790
791 return ret;
792
793 case 2:
794 if (userbuffer) {
795 if (get_user(swap16, (u16 __user *)userbuffer))
796 return -EFAULT;
797 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200798 swap16 = *((u16 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100800 ret = sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
801 addr, swap16);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
803 if (!ret)
804 (*bytes_written) += 2;
805
806 return ret;
807
808 case 3:
809 if (userbuffer) {
810 if (copy_from_user(&buf, userbuffer, 3))
811 return -EFAULT;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200812#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 swap32 = (buf[0] << 16) |
814 (buf[1] << 8) |
815 buf[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200816#else
817 swap32 = (buf[2] << 16) |
818 (buf[1] << 8) |
819 buf[0];
820#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200822#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 swap32 = (kernbuffer[0] << 16) |
824 (kernbuffer[1] << 8) |
825 kernbuffer[2];
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200826#else
827 swap32 = (kernbuffer[2] << 16) |
828 (kernbuffer[1] << 8) |
829 kernbuffer[0];
830#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100832 ret = sisusb_write_memio_24bit(sisusb, SISUSB_TYPE_MEM,
833 addr, swap32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
835 if (!ret)
836 (*bytes_written) += 3;
837
838 return ret;
839
840 case 4:
841 if (userbuffer) {
842 if (get_user(swap32, (u32 __user *)userbuffer))
843 return -EFAULT;
844 } else
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200845 swap32 = *((u32 *)kernbuffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100847 ret = sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM,
848 addr, swap32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 if (!ret)
850 (*bytes_written) += 4;
851
852 return ret;
853
854 default:
855 if ((length & ~3) > 0x10000) {
856
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100857 packet.header = 0x001f;
858 packet.address = 0x000001d4;
859 packet.data = addr;
860 ret = sisusb_send_bridge_packet(sisusb, 10,
861 &packet, 0);
862 packet.header = 0x001f;
863 packet.address = 0x000001d0;
864 packet.data = (length & ~3);
865 ret |= sisusb_send_bridge_packet(sisusb, 10,
866 &packet, 0);
867 packet.header = 0x001f;
868 packet.address = 0x000001c0;
869 packet.data = flag | 0x16;
870 ret |= sisusb_send_bridge_packet(sisusb, 10,
871 &packet, 0);
872 if (userbuffer) {
873 ret |= sisusb_send_bulk_msg(sisusb,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 SISUSB_EP_GFX_LBULK_OUT,
875 (length & ~3),
876 NULL, userbuffer, 0,
877 bytes_written, 0, 1);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100878 userbuffer += (*bytes_written);
879 } else if (fromkern) {
880 ret |= sisusb_send_bulk_msg(sisusb,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 SISUSB_EP_GFX_LBULK_OUT,
882 (length & ~3),
883 kernbuffer, NULL, 0,
884 bytes_written, 0, 1);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100885 kernbuffer += (*bytes_written);
886 } else {
887 ret |= sisusb_send_bulk_msg(sisusb,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 SISUSB_EP_GFX_LBULK_OUT,
889 (length & ~3),
890 NULL, NULL, index,
891 bytes_written, 0, 1);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100892 kernbuffer += ((*bytes_written) &
893 (sisusb->obufsize-1));
894 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
896 } else {
897
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 packet.header = 0x001f;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100899 packet.address = 0x00000194;
900 packet.data = addr;
901 ret = sisusb_send_bridge_packet(sisusb, 10,
902 &packet, 0);
903 packet.header = 0x001f;
904 packet.address = 0x00000190;
905 packet.data = (length & ~3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 ret |= sisusb_send_bridge_packet(sisusb, 10,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100907 &packet, 0);
908 if (sisusb->flagb0 != 0x16) {
909 packet.header = 0x001f;
910 packet.address = 0x00000180;
911 packet.data = flag | 0x16;
912 ret |= sisusb_send_bridge_packet(sisusb,
913 10, &packet, 0);
914 sisusb->flagb0 = 0x16;
915 }
916 if (userbuffer) {
917 ret |= sisusb_send_bulk_msg(sisusb,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 SISUSB_EP_GFX_BULK_OUT,
919 (length & ~3),
920 NULL, userbuffer, 0,
921 bytes_written, 0, 1);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100922 userbuffer += (*bytes_written);
923 } else if (fromkern) {
924 ret |= sisusb_send_bulk_msg(sisusb,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 SISUSB_EP_GFX_BULK_OUT,
926 (length & ~3),
927 kernbuffer, NULL, 0,
928 bytes_written, 0, 1);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100929 kernbuffer += (*bytes_written);
930 } else {
931 ret |= sisusb_send_bulk_msg(sisusb,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 SISUSB_EP_GFX_BULK_OUT,
933 (length & ~3),
934 NULL, NULL, index,
935 bytes_written, 0, 1);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100936 kernbuffer += ((*bytes_written) &
937 (sisusb->obufsize-1));
938 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 }
940 if (ret) {
941 msgcount++;
942 if (msgcount < 500)
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100943 dev_err(&sisusb->sisusb_dev->dev,
944 "Wrote %zd of %d bytes, error %d\n",
945 *bytes_written, length,
946 ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 else if (msgcount == 500)
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +0100948 dev_err(&sisusb->sisusb_dev->dev,
949 "Too many errors, logging stopped\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 }
951 addr += (*bytes_written);
952 length -= (*bytes_written);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100953 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100955 if (ret)
956 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
958 }
959
960 return ret ? -EIO : 0;
961}
962
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +0200963/* Remember: Read data in packet is in machine-endianess! So for
964 * byte, word, 24bit, long no endian correction is necessary.
965 */
966
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100968 u32 addr, u8 *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969{
970 struct sisusb_packet packet;
971 int ret;
972
973 CLEARPACKET(&packet);
974 packet.header = (1 << (addr & 3)) | (type << 6);
975 packet.address = addr & ~3;
976 ret = sisusb_send_packet(sisusb, 6, &packet);
977 *data = (u8)(packet.data >> ((addr & 3) << 3));
978 return ret;
979}
980
981static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100982 u32 addr, u16 *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983{
984 struct sisusb_packet packet;
985 int ret = 0;
986
987 CLEARPACKET(&packet);
988
989 packet.address = addr & ~3;
990
991 switch (addr & 3) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +0100992 case 0:
993 packet.header = (type << 6) | 0x0003;
994 ret = sisusb_send_packet(sisusb, 6, &packet);
995 *data = (u16)(packet.data);
996 break;
997 case 1:
998 packet.header = (type << 6) | 0x0006;
999 ret = sisusb_send_packet(sisusb, 6, &packet);
1000 *data = (u16)(packet.data >> 8);
1001 break;
1002 case 2:
1003 packet.header = (type << 6) | 0x000c;
1004 ret = sisusb_send_packet(sisusb, 6, &packet);
1005 *data = (u16)(packet.data >> 16);
1006 break;
1007 case 3:
1008 packet.header = (type << 6) | 0x0008;
1009 ret = sisusb_send_packet(sisusb, 6, &packet);
1010 *data = (u16)(packet.data >> 24);
1011 packet.header = (type << 6) | 0x0001;
1012 packet.address = (addr & ~3) + 4;
1013 ret |= sisusb_send_packet(sisusb, 6, &packet);
1014 *data |= (u16)(packet.data << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 }
1016
1017 return ret;
1018}
1019
1020static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001021 u32 addr, u32 *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
1023 struct sisusb_packet packet;
1024 int ret = 0;
1025
1026 packet.address = addr & ~3;
1027
1028 switch (addr & 3) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001029 case 0:
1030 packet.header = (type << 6) | 0x0007;
1031 ret = sisusb_send_packet(sisusb, 6, &packet);
1032 *data = packet.data & 0x00ffffff;
1033 break;
1034 case 1:
1035 packet.header = (type << 6) | 0x000e;
1036 ret = sisusb_send_packet(sisusb, 6, &packet);
1037 *data = packet.data >> 8;
1038 break;
1039 case 2:
1040 packet.header = (type << 6) | 0x000c;
1041 ret = sisusb_send_packet(sisusb, 6, &packet);
1042 *data = packet.data >> 16;
1043 packet.header = (type << 6) | 0x0001;
1044 packet.address = (addr & ~3) + 4;
1045 ret |= sisusb_send_packet(sisusb, 6, &packet);
1046 *data |= ((packet.data & 0xff) << 16);
1047 break;
1048 case 3:
1049 packet.header = (type << 6) | 0x0008;
1050 ret = sisusb_send_packet(sisusb, 6, &packet);
1051 *data = packet.data >> 24;
1052 packet.header = (type << 6) | 0x0003;
1053 packet.address = (addr & ~3) + 4;
1054 ret |= sisusb_send_packet(sisusb, 6, &packet);
1055 *data |= ((packet.data & 0xffff) << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 }
1057
1058 return ret;
1059}
1060
1061static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001062 u32 addr, u32 *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063{
1064 struct sisusb_packet packet;
1065 int ret = 0;
1066
1067 packet.address = addr & ~3;
1068
1069 switch (addr & 3) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001070 case 0:
1071 packet.header = (type << 6) | 0x000f;
1072 ret = sisusb_send_packet(sisusb, 6, &packet);
1073 *data = packet.data;
1074 break;
1075 case 1:
1076 packet.header = (type << 6) | 0x000e;
1077 ret = sisusb_send_packet(sisusb, 6, &packet);
1078 *data = packet.data >> 8;
1079 packet.header = (type << 6) | 0x0001;
1080 packet.address = (addr & ~3) + 4;
1081 ret |= sisusb_send_packet(sisusb, 6, &packet);
1082 *data |= (packet.data << 24);
1083 break;
1084 case 2:
1085 packet.header = (type << 6) | 0x000c;
1086 ret = sisusb_send_packet(sisusb, 6, &packet);
1087 *data = packet.data >> 16;
1088 packet.header = (type << 6) | 0x0003;
1089 packet.address = (addr & ~3) + 4;
1090 ret |= sisusb_send_packet(sisusb, 6, &packet);
1091 *data |= (packet.data << 16);
1092 break;
1093 case 3:
1094 packet.header = (type << 6) | 0x0008;
1095 ret = sisusb_send_packet(sisusb, 6, &packet);
1096 *data = packet.data >> 24;
1097 packet.header = (type << 6) | 0x0007;
1098 packet.address = (addr & ~3) + 4;
1099 ret |= sisusb_send_packet(sisusb, 6, &packet);
1100 *data |= (packet.data << 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 }
1102
1103 return ret;
1104}
1105
1106static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001107 char *kernbuffer, int length, char __user *userbuffer,
1108 ssize_t *bytes_read)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109{
1110 int ret = 0;
1111 char buf[4];
1112 u16 swap16;
1113 u32 swap32;
1114
1115 (*bytes_read = 0);
1116
1117 length &= 0x00ffffff;
1118
1119 while (length) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001120 switch (length) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001123 addr, &buf[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 if (!ret) {
1125 (*bytes_read)++;
1126 if (userbuffer) {
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001127 if (put_user(buf[0], (u8 __user *)userbuffer))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 return -EFAULT;
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001129 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 kernbuffer[0] = buf[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 }
1132 return ret;
1133
1134 case 2:
1135 ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001136 addr, &swap16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (!ret) {
1138 (*bytes_read) += 2;
1139 if (userbuffer) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001140 if (put_user(swap16, (u16 __user *)userbuffer))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 return -EFAULT;
1142 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001143 *((u16 *)kernbuffer) = swap16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 }
1145 }
1146 return ret;
1147
1148 case 3:
1149 ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001150 addr, &swap32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 if (!ret) {
1152 (*bytes_read) += 3;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001153#ifdef __BIG_ENDIAN
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 buf[0] = (swap32 >> 16) & 0xff;
1155 buf[1] = (swap32 >> 8) & 0xff;
1156 buf[2] = swap32 & 0xff;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001157#else
1158 buf[2] = (swap32 >> 16) & 0xff;
1159 buf[1] = (swap32 >> 8) & 0xff;
1160 buf[0] = swap32 & 0xff;
1161#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (userbuffer) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001163 if (copy_to_user(userbuffer,
1164 &buf[0], 3))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 return -EFAULT;
1166 } else {
1167 kernbuffer[0] = buf[0];
1168 kernbuffer[1] = buf[1];
1169 kernbuffer[2] = buf[2];
1170 }
1171 }
1172 return ret;
1173
1174 default:
1175 ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001176 addr, &swap32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 if (!ret) {
1178 (*bytes_read) += 4;
1179 if (userbuffer) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001180 if (put_user(swap32, (u32 __user *)userbuffer))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 return -EFAULT;
1182
1183 userbuffer += 4;
1184 } else {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001185 *((u32 *)kernbuffer) = swap32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 kernbuffer += 4;
1187 }
1188 addr += 4;
1189 length -= 4;
1190 }
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001191 }
1192 if (ret)
1193 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 }
1195
1196 return ret;
1197}
1198
1199/* High level: Gfx (indexed) register access */
1200
Jiri Slaby022e4682019-01-22 16:12:00 +01001201#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001202int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001203{
1204 return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1205}
1206
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001207int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001208{
1209 return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
1210}
1211#endif
1212
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001213int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
1214 u8 index, u8 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215{
1216 int ret;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001217
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1219 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1220 return ret;
1221}
1222
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001223int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
1224 u8 index, u8 *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225{
1226 int ret;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001227
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
1229 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
1230 return ret;
1231}
1232
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001233int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
1234 u8 myand, u8 myor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235{
1236 int ret;
1237 u8 tmp;
1238
1239 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1240 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1241 tmp &= myand;
1242 tmp |= myor;
1243 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1244 return ret;
1245}
1246
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001247static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb,
1248 int port, u8 idx, u8 data, u8 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249{
1250 int ret;
1251 u8 tmp;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001252
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
1254 ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
1255 tmp &= ~(mask);
1256 tmp |= (data & mask);
1257 ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
1258 return ret;
1259}
1260
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001261int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
1262 u8 index, u8 myor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263{
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001264 return sisusb_setidxregandor(sisusb, port, index, 0xff, myor);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265}
1266
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001267int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
1268 u8 idx, u8 myand)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269{
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001270 return sisusb_setidxregandor(sisusb, port, idx, myand, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271}
1272
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001273/* Write/read video ram */
1274
Jiri Slaby022e4682019-01-22 16:12:00 +01001275#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001276int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001277{
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001278 return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001279}
1280
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001281int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001282{
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001283 return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001284}
1285
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001286int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001287 u32 dest, int length)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001288{
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001289 size_t dummy;
1290
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001291 return sisusb_write_mem_bulk(sisusb, dest, src, length,
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001292 NULL, 0, &dummy);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001293}
1294
1295#ifdef SISUSBENDIANTEST
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001296static int sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest,
1297 u32 src, int length)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001298{
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001299 size_t dummy;
1300
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001301 return sisusb_read_mem_bulk(sisusb, src, dest, length,
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001302 NULL, &dummy);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001303}
1304#endif
1305#endif
1306
1307#ifdef SISUSBENDIANTEST
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001308static void sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001309{
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001310 static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
1311 char destbuffer[10];
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001312 int i, j;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001313
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001314 sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001315
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001316 for (i = 1; i <= 7; i++) {
1317 dev_dbg(&sisusb->sisusb_dev->dev,
1318 "sisusb: rwtest %d bytes\n", i);
Jiri Slaby3cc5be72016-06-23 13:34:25 +02001319 sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i);
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001320 for (j = 0; j < i; j++) {
1321 dev_dbg(&sisusb->sisusb_dev->dev,
1322 "rwtest read[%d] = %x\n",
1323 j, destbuffer[j]);
1324 }
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001325 }
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02001326}
1327#endif
1328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329/* access pci config registers (reg numbers 0, 4, 8, etc) */
1330
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001331static int sisusb_write_pci_config(struct sisusb_usb_data *sisusb,
1332 int regnum, u32 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333{
1334 struct sisusb_packet packet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
1336 packet.header = 0x008f;
1337 packet.address = regnum | 0x10000;
1338 packet.data = data;
Gustavo A. R. Silvae6f9e132017-07-09 22:31:31 -05001339 return sisusb_send_packet(sisusb, 10, &packet);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340}
1341
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001342static int sisusb_read_pci_config(struct sisusb_usb_data *sisusb,
1343 int regnum, u32 *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344{
1345 struct sisusb_packet packet;
1346 int ret;
1347
1348 packet.header = 0x008f;
1349 packet.address = (u32)regnum | 0x10000;
1350 ret = sisusb_send_packet(sisusb, 6, &packet);
1351 *data = packet.data;
1352 return ret;
1353}
1354
1355/* Clear video RAM */
1356
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001357static int sisusb_clear_vram(struct sisusb_usb_data *sisusb,
1358 u32 address, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359{
1360 int ret, i;
1361 ssize_t j;
1362
1363 if (address < sisusb->vrambase)
1364 return 1;
1365
1366 if (address >= sisusb->vrambase + sisusb->vramsize)
1367 return 1;
1368
1369 if (address + length > sisusb->vrambase + sisusb->vramsize)
1370 length = sisusb->vrambase + sisusb->vramsize - address;
1371
1372 if (length <= 0)
1373 return 0;
1374
1375 /* allocate free buffer/urb and clear the buffer */
Peter Senna Tschudin662bfe72016-01-15 18:41:31 +01001376 i = sisusb_alloc_outbuf(sisusb);
1377 if (i < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 return -EBUSY;
1379
1380 memset(sisusb->obuf[i], 0, sisusb->obufsize);
1381
1382 /* We can write a length > buffer size here. The buffer
1383 * data will simply be re-used (like a ring-buffer).
1384 */
1385 ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
1386
1387 /* Free the buffer/urb */
1388 sisusb_free_outbuf(sisusb, i);
1389
1390 return ret;
1391}
1392
1393/* Initialize the graphics core (return 0 on success)
1394 * This resets the graphics hardware and puts it into
1395 * a defined mode (640x480@60Hz)
1396 */
1397
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001398#define GETREG(r, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1399#define SETREG(r, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
1400#define SETIREG(r, i, d) sisusb_setidxreg(sisusb, r, i, d)
1401#define GETIREG(r, i, d) sisusb_getidxreg(sisusb, r, i, d)
1402#define SETIREGOR(r, i, o) sisusb_setidxregor(sisusb, r, i, o)
1403#define SETIREGAND(r, i, a) sisusb_setidxregand(sisusb, r, i, a)
1404#define SETIREGANDOR(r, i, a, o) sisusb_setidxregandor(sisusb, r, i, a, o)
1405#define READL(a, d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
1406#define WRITEL(a, d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
1407#define READB(a, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
1408#define WRITEB(a, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001410static int sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411{
1412 int ret;
1413 u8 tmp8;
1414
1415 ret = GETIREG(SISSR, 0x16, &tmp8);
1416 if (ramtype <= 1) {
1417 tmp8 &= 0x3f;
1418 ret |= SETIREG(SISSR, 0x16, tmp8);
1419 tmp8 |= 0x80;
1420 ret |= SETIREG(SISSR, 0x16, tmp8);
1421 } else {
1422 tmp8 |= 0xc0;
1423 ret |= SETIREG(SISSR, 0x16, tmp8);
1424 tmp8 &= 0x0f;
1425 ret |= SETIREG(SISSR, 0x16, tmp8);
1426 tmp8 |= 0x80;
1427 ret |= SETIREG(SISSR, 0x16, tmp8);
1428 tmp8 &= 0x0f;
1429 ret |= SETIREG(SISSR, 0x16, tmp8);
1430 tmp8 |= 0xd0;
1431 ret |= SETIREG(SISSR, 0x16, tmp8);
1432 tmp8 &= 0x0f;
1433 ret |= SETIREG(SISSR, 0x16, tmp8);
1434 tmp8 |= 0xa0;
1435 ret |= SETIREG(SISSR, 0x16, tmp8);
1436 }
1437 return ret;
1438}
1439
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001440static int sisusb_getbuswidth(struct sisusb_usb_data *sisusb,
1441 int *bw, int *chab)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442{
1443 int ret;
1444 u8 ramtype, done = 0;
1445 u32 t0, t1, t2, t3;
1446 u32 ramptr = SISUSB_PCI_MEMBASE;
1447
1448 ret = GETIREG(SISSR, 0x3a, &ramtype);
1449 ramtype &= 3;
1450
1451 ret |= SETIREG(SISSR, 0x13, 0x00);
1452
1453 if (ramtype <= 1) {
1454 ret |= SETIREG(SISSR, 0x14, 0x12);
1455 ret |= SETIREGAND(SISSR, 0x15, 0xef);
1456 } else {
1457 ret |= SETIREG(SISSR, 0x14, 0x02);
1458 }
1459
1460 ret |= sisusb_triggersr16(sisusb, ramtype);
1461 ret |= WRITEL(ramptr + 0, 0x01234567);
1462 ret |= WRITEL(ramptr + 4, 0x456789ab);
1463 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1464 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1465 ret |= WRITEL(ramptr + 16, 0x55555555);
1466 ret |= WRITEL(ramptr + 20, 0x55555555);
1467 ret |= WRITEL(ramptr + 24, 0xffffffff);
1468 ret |= WRITEL(ramptr + 28, 0xffffffff);
1469 ret |= READL(ramptr + 0, &t0);
1470 ret |= READL(ramptr + 4, &t1);
1471 ret |= READL(ramptr + 8, &t2);
1472 ret |= READL(ramptr + 12, &t3);
1473
1474 if (ramtype <= 1) {
1475
1476 *chab = 0; *bw = 64;
1477
1478 if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
1479 if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
1480 *chab = 0; *bw = 64;
1481 ret |= SETIREGAND(SISSR, 0x14, 0xfd);
1482 }
1483 }
1484 if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
1485 *chab = 1; *bw = 64;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001486 ret |= SETIREGANDOR(SISSR, 0x14, 0xfc, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
1488 ret |= sisusb_triggersr16(sisusb, ramtype);
1489 ret |= WRITEL(ramptr + 0, 0x89abcdef);
1490 ret |= WRITEL(ramptr + 4, 0xcdef0123);
1491 ret |= WRITEL(ramptr + 8, 0x55555555);
1492 ret |= WRITEL(ramptr + 12, 0x55555555);
1493 ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
1494 ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
1495 ret |= READL(ramptr + 4, &t1);
1496
1497 if (t1 != 0xcdef0123) {
1498 *bw = 32;
1499 ret |= SETIREGOR(SISSR, 0x15, 0x10);
1500 }
1501 }
1502
1503 } else {
1504
1505 *chab = 0; *bw = 64; /* default: cha, bw = 64 */
1506
1507 done = 0;
1508
1509 if (t1 == 0x456789ab) {
1510 if (t0 == 0x01234567) {
1511 *chab = 0; *bw = 64;
1512 done = 1;
1513 }
1514 } else {
1515 if (t0 == 0x01234567) {
1516 *chab = 0; *bw = 32;
1517 ret |= SETIREG(SISSR, 0x14, 0x00);
1518 done = 1;
1519 }
1520 }
1521
1522 if (!done) {
1523 ret |= SETIREG(SISSR, 0x14, 0x03);
1524 ret |= sisusb_triggersr16(sisusb, ramtype);
1525
1526 ret |= WRITEL(ramptr + 0, 0x01234567);
1527 ret |= WRITEL(ramptr + 4, 0x456789ab);
1528 ret |= WRITEL(ramptr + 8, 0x89abcdef);
1529 ret |= WRITEL(ramptr + 12, 0xcdef0123);
1530 ret |= WRITEL(ramptr + 16, 0x55555555);
1531 ret |= WRITEL(ramptr + 20, 0x55555555);
1532 ret |= WRITEL(ramptr + 24, 0xffffffff);
1533 ret |= WRITEL(ramptr + 28, 0xffffffff);
1534 ret |= READL(ramptr + 0, &t0);
1535 ret |= READL(ramptr + 4, &t1);
1536
1537 if (t1 == 0x456789ab) {
1538 if (t0 == 0x01234567) {
1539 *chab = 1; *bw = 64;
1540 return ret;
1541 } /* else error */
1542 } else {
1543 if (t0 == 0x01234567) {
1544 *chab = 1; *bw = 32;
1545 ret |= SETIREG(SISSR, 0x14, 0x01);
1546 } /* else error */
1547 }
1548 }
1549 }
1550 return ret;
1551}
1552
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001553static int sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554{
1555 int ret = 0;
1556 u32 ramptr = SISUSB_PCI_MEMBASE;
1557 u8 tmp1, tmp2, i, j;
1558
1559 ret |= WRITEB(ramptr, 0xaa);
1560 ret |= WRITEB(ramptr + 16, 0x55);
1561 ret |= READB(ramptr, &tmp1);
1562 ret |= READB(ramptr + 16, &tmp2);
1563 if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
1564 for (i = 0, j = 16; i < 2; i++, j += 16) {
1565 ret |= GETIREG(SISSR, 0x21, &tmp1);
1566 ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
1567 ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */
1568 ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
1569 ret |= SETIREG(SISSR, 0x21, tmp1);
1570 ret |= WRITEB(ramptr + 16 + j, j);
1571 ret |= READB(ramptr + 16 + j, &tmp1);
1572 if (tmp1 == j) {
1573 ret |= WRITEB(ramptr + j, j);
1574 break;
1575 }
1576 }
1577 }
1578 return ret;
1579}
1580
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001581static int sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret,
1582 int index, u8 rankno, u8 chab, const u8 dramtype[][5], int bw)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583{
1584 int ret = 0, ranksize;
1585 u8 tmp;
1586
1587 *iret = 0;
1588
1589 if ((rankno == 2) && (dramtype[index][0] == 2))
1590 return ret;
1591
1592 ranksize = dramtype[index][3] / 2 * bw / 32;
1593
1594 if ((ranksize * rankno) > 128)
1595 return ret;
1596
1597 tmp = 0;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001598 while ((ranksize >>= 1) > 0)
1599 tmp += 0x10;
1600
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 tmp |= ((rankno - 1) << 2);
1602 tmp |= ((bw / 64) & 0x02);
1603 tmp |= (chab & 0x01);
1604
1605 ret = SETIREG(SISSR, 0x14, tmp);
1606 ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
1607
1608 *iret = 1;
1609
1610 return ret;
1611}
1612
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001613static int sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret,
1614 u32 inc, int testn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615{
1616 int ret = 0, i;
1617 u32 j, tmp;
1618
1619 *iret = 0;
1620
1621 for (i = 0, j = 0; i < testn; i++) {
1622 ret |= WRITEL(sisusb->vrambase + j, j);
1623 j += inc;
1624 }
1625
1626 for (i = 0, j = 0; i < testn; i++) {
1627 ret |= READL(sisusb->vrambase + j, &tmp);
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001628 if (tmp != j)
1629 return ret;
1630
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 j += inc;
1632 }
1633
1634 *iret = 1;
1635 return ret;
1636}
1637
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001638static int sisusb_check_ranks(struct sisusb_usb_data *sisusb,
1639 int *iret, int rankno, int idx, int bw, const u8 rtype[][5])
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640{
1641 int ret = 0, i, i2ret;
1642 u32 inc;
1643
1644 *iret = 0;
1645
1646 for (i = rankno; i >= 1; i--) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001647 inc = 1 << (rtype[idx][2] + rtype[idx][1] + rtype[idx][0] +
1648 bw / 64 + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1650 if (!i2ret)
1651 return ret;
1652 }
1653
1654 inc = 1 << (rtype[idx][2] + bw / 64 + 2);
1655 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
1656 if (!i2ret)
1657 return ret;
1658
1659 inc = 1 << (10 + bw / 64);
1660 ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
1661 if (!i2ret)
1662 return ret;
1663
1664 *iret = 1;
1665 return ret;
1666}
1667
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001668static int sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret,
1669 int bw, int chab)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
1671 int ret = 0, i2ret = 0, i, j;
1672 static const u8 sdramtype[13][5] = {
1673 { 2, 12, 9, 64, 0x35 },
1674 { 1, 13, 9, 64, 0x44 },
1675 { 2, 12, 8, 32, 0x31 },
1676 { 2, 11, 9, 32, 0x25 },
1677 { 1, 12, 9, 32, 0x34 },
1678 { 1, 13, 8, 32, 0x40 },
1679 { 2, 11, 8, 16, 0x21 },
1680 { 1, 12, 8, 16, 0x30 },
1681 { 1, 11, 9, 16, 0x24 },
1682 { 1, 11, 8, 8, 0x20 },
1683 { 2, 9, 8, 4, 0x01 },
1684 { 1, 10, 8, 4, 0x10 },
1685 { 1, 9, 8, 2, 0x00 }
1686 };
1687
1688 *iret = 1; /* error */
1689
1690 for (i = 0; i < 13; i++) {
1691 ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
1692 for (j = 2; j > 0; j--) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001693 ret |= sisusb_set_rank(sisusb, &i2ret, i, j, chab,
1694 sdramtype, bw);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 if (!i2ret)
1696 continue;
1697
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001698 ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, bw,
1699 sdramtype);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 if (i2ret) {
1701 *iret = 0; /* ram size found */
1702 return ret;
1703 }
1704 }
1705 }
1706
1707 return ret;
1708}
1709
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001710static int sisusb_setup_screen(struct sisusb_usb_data *sisusb,
1711 int clrall, int drwfr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
1713 int ret = 0;
1714 u32 address;
1715 int i, length, modex, modey, bpp;
1716
1717 modex = 640; modey = 480; bpp = 2;
1718
1719 address = sisusb->vrambase; /* Clear video ram */
1720
1721 if (clrall)
1722 length = sisusb->vramsize;
1723 else
1724 length = modex * bpp * modey;
1725
1726 ret = sisusb_clear_vram(sisusb, address, length);
1727
1728 if (!ret && drwfr) {
1729 for (i = 0; i < modex; i++) {
1730 address = sisusb->vrambase + (i * bpp);
1731 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001732 address, 0xf100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 address += (modex * (modey-1) * bpp);
1734 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001735 address, 0xf100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 }
1737 for (i = 0; i < modey; i++) {
1738 address = sisusb->vrambase + ((i * modex) * bpp);
1739 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001740 address, 0xf100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 address += ((modex - 1) * bpp);
1742 ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001743 address, 0xf100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 }
1745 }
1746
1747 return ret;
1748}
1749
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001750static int sisusb_set_default_mode(struct sisusb_usb_data *sisusb,
1751 int touchengines)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752{
Colin Ian King795a8072018-07-13 10:55:30 +01001753 int ret = 0, i, j, modex, bpp, du;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 u8 sr31, cr63, tmp8;
1755 static const char attrdata[] = {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001756 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1757 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
1758 0x01, 0x00, 0x00, 0x00
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759 };
1760 static const char crtcrdata[] = {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001761 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
1762 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1763 0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 0xff
1765 };
1766 static const char grcdata[] = {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001767 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 0xff
1769 };
1770 static const char crtcdata[] = {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01001771 0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e,
1772 0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 0x00
1774 };
1775
Colin Ian King795a8072018-07-13 10:55:30 +01001776 modex = 640; bpp = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
1778 GETIREG(SISSR, 0x31, &sr31);
1779 GETIREG(SISCR, 0x63, &cr63);
1780 SETIREGOR(SISSR, 0x01, 0x20);
1781 SETIREG(SISCR, 0x63, cr63 & 0xbf);
1782 SETIREGOR(SISCR, 0x17, 0x80);
1783 SETIREGOR(SISSR, 0x1f, 0x04);
1784 SETIREGAND(SISSR, 0x07, 0xfb);
1785 SETIREG(SISSR, 0x00, 0x03); /* seq */
1786 SETIREG(SISSR, 0x01, 0x21);
1787 SETIREG(SISSR, 0x02, 0x0f);
1788 SETIREG(SISSR, 0x03, 0x00);
1789 SETIREG(SISSR, 0x04, 0x0e);
1790 SETREG(SISMISCW, 0x23); /* misc */
1791 for (i = 0; i <= 0x18; i++) { /* crtc */
1792 SETIREG(SISCR, i, crtcrdata[i]);
1793 }
1794 for (i = 0; i <= 0x13; i++) { /* att */
1795 GETREG(SISINPSTAT, &tmp8);
1796 SETREG(SISAR, i);
1797 SETREG(SISAR, attrdata[i]);
1798 }
1799 GETREG(SISINPSTAT, &tmp8);
1800 SETREG(SISAR, 0x14);
1801 SETREG(SISAR, 0x00);
1802 GETREG(SISINPSTAT, &tmp8);
1803 SETREG(SISAR, 0x20);
1804 GETREG(SISINPSTAT, &tmp8);
1805 for (i = 0; i <= 0x08; i++) { /* grc */
1806 SETIREG(SISGR, i, grcdata[i]);
1807 }
1808 SETIREGAND(SISGR, 0x05, 0xbf);
1809 for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */
1810 SETIREG(SISSR, i, 0x00);
1811 }
1812 SETIREGAND(SISSR, 0x37, 0xfe);
1813 SETREG(SISMISCW, 0xef); /* sync */
1814 SETIREG(SISCR, 0x11, 0x00); /* crtc */
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001815 for (j = 0x00, i = 0; i <= 7; i++, j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 SETIREG(SISCR, j, crtcdata[i]);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001817
1818 for (j = 0x10; i <= 10; i++, j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 SETIREG(SISCR, j, crtcdata[i]);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001820
1821 for (j = 0x15; i <= 12; i++, j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 SETIREG(SISCR, j, crtcdata[i]);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001823
1824 for (j = 0x0A; i <= 15; i++, j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 SETIREG(SISSR, j, crtcdata[i]);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001826
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
1828 SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
1829 SETIREG(SISCR, 0x14, 0x4f);
1830 du = (modex / 16) * (bpp * 2); /* offset/pitch */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
1832 SETIREG(SISCR, 0x13, (du & 0xff));
1833 du <<= 5;
1834 tmp8 = du >> 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 SETIREG(SISSR, 0x10, tmp8);
1836 SETIREG(SISSR, 0x31, 0x00); /* VCLK */
1837 SETIREG(SISSR, 0x2b, 0x1b);
1838 SETIREG(SISSR, 0x2c, 0xe1);
1839 SETIREG(SISSR, 0x2d, 0x01);
1840 SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */
1841 SETIREG(SISSR, 0x08, 0xae);
1842 SETIREGAND(SISSR, 0x09, 0xf0);
1843 SETIREG(SISSR, 0x08, 0x34);
1844 SETIREGOR(SISSR, 0x3d, 0x01);
1845 SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */
1846 SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
1847 SETIREG(SISCR, 0x19, 0x00);
1848 SETIREGAND(SISCR, 0x1a, 0xfc);
1849 SETIREGAND(SISSR, 0x0f, 0xb7);
1850 SETIREGAND(SISSR, 0x31, 0xfb);
1851 SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
1852 SETIREGAND(SISSR, 0x32, 0xf3);
1853 SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
1854 SETIREG(SISCR, 0x52, 0x6c);
1855
1856 SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */
1857 SETIREG(SISCR, 0x0c, 0x00);
1858 SETIREG(SISSR, 0x0d, 0x00);
1859 SETIREGAND(SISSR, 0x37, 0xfe);
1860
1861 SETIREG(SISCR, 0x32, 0x20);
1862 SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */
1863 SETIREG(SISCR, 0x63, (cr63 & 0xbf));
1864 SETIREG(SISSR, 0x31, (sr31 & 0xfb));
1865
1866 if (touchengines) {
1867 SETIREG(SISSR, 0x20, 0xa1); /* enable engines */
1868 SETIREGOR(SISSR, 0x1e, 0x5a);
1869
1870 SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */
1871 SETIREG(SISSR, 0x27, 0x1f);
1872 SETIREG(SISSR, 0x26, 0x00);
1873 }
1874
Felipe Balbied86d972007-08-10 09:34:24 -04001875 SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876
1877 return ret;
1878}
1879
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001880static int sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881{
1882 int ret = 0, i, j, bw, chab, iret, retry = 3;
1883 u8 tmp8, ramtype;
1884 u32 tmp32;
1885 static const char mclktable[] = {
1886 0x3b, 0x22, 0x01, 143,
1887 0x3b, 0x22, 0x01, 143,
1888 0x3b, 0x22, 0x01, 143,
1889 0x3b, 0x22, 0x01, 143
1890 };
1891 static const char eclktable[] = {
1892 0x3b, 0x22, 0x01, 143,
1893 0x3b, 0x22, 0x01, 143,
1894 0x3b, 0x22, 0x01, 143,
1895 0x3b, 0x22, 0x01, 143
1896 };
1897 static const char ramtypetable1[] = {
1898 0x00, 0x04, 0x60, 0x60,
1899 0x0f, 0x0f, 0x1f, 0x1f,
1900 0xba, 0xba, 0xba, 0xba,
1901 0xa9, 0xa9, 0xac, 0xac,
1902 0xa0, 0xa0, 0xa0, 0xa8,
1903 0x00, 0x00, 0x02, 0x02,
1904 0x30, 0x30, 0x40, 0x40
1905 };
1906 static const char ramtypetable2[] = {
1907 0x77, 0x77, 0x44, 0x44,
1908 0x77, 0x77, 0x44, 0x44,
1909 0x00, 0x00, 0x00, 0x00,
1910 0x5b, 0x5b, 0xab, 0xab,
1911 0x00, 0x00, 0xf0, 0xf8
1912 };
1913
1914 while (retry--) {
1915
1916 /* Enable VGA */
1917 ret = GETREG(SISVGAEN, &tmp8);
1918 ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
1919
1920 /* Enable GPU access to VRAM */
1921 ret |= GETREG(SISMISCR, &tmp8);
1922 ret |= SETREG(SISMISCW, (tmp8 | 0x01));
1923
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001924 if (ret)
1925 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
1927 /* Reset registers */
1928 ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
1929 ret |= SETIREG(SISSR, 0x05, 0x86);
1930 ret |= SETIREGOR(SISSR, 0x20, 0x01);
1931
1932 ret |= SETREG(SISMISCW, 0x67);
1933
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001934 for (i = 0x06; i <= 0x1f; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 ret |= SETIREG(SISSR, i, 0x00);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001936
1937 for (i = 0x21; i <= 0x27; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 ret |= SETIREG(SISSR, i, 0x00);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001939
1940 for (i = 0x31; i <= 0x3d; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 ret |= SETIREG(SISSR, i, 0x00);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001942
1943 for (i = 0x12; i <= 0x1b; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 ret |= SETIREG(SISSR, i, 0x00);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01001945
1946 for (i = 0x79; i <= 0x7c; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 ret |= SETIREG(SISCR, i, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001949 if (ret)
1950 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951
1952 ret |= SETIREG(SISCR, 0x63, 0x80);
1953
1954 ret |= GETIREG(SISSR, 0x3a, &ramtype);
1955 ramtype &= 0x03;
1956
1957 ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
1958 ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
1959 ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
1960
1961 ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
1962 ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
1963 ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
1964
1965 ret |= SETIREG(SISSR, 0x07, 0x18);
1966 ret |= SETIREG(SISSR, 0x11, 0x0f);
1967
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001968 if (ret)
1969 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970
1971 for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001972 ret |= SETIREG(SISSR, i,
1973 ramtypetable1[(j*4) + ramtype]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 }
1975 for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001976 ret |= SETIREG(SISCR, i,
1977 ramtypetable2[(j*4) + ramtype]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 }
1979
1980 ret |= SETIREG(SISCR, 0x49, 0xaa);
1981
1982 ret |= SETIREG(SISSR, 0x1f, 0x00);
1983 ret |= SETIREG(SISSR, 0x20, 0xa0);
1984 ret |= SETIREG(SISSR, 0x23, 0xf6);
1985 ret |= SETIREG(SISSR, 0x24, 0x0d);
1986 ret |= SETIREG(SISSR, 0x25, 0x33);
1987
1988 ret |= SETIREG(SISSR, 0x11, 0x0f);
1989
1990 ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
1991
1992 ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
1993
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01001994 if (ret)
1995 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996
1997 ret |= SETIREG(SISPART1, 0x00, 0x00);
1998
1999 ret |= GETIREG(SISSR, 0x13, &tmp8);
2000 tmp8 >>= 4;
2001
2002 ret |= SETIREG(SISPART1, 0x02, 0x00);
2003 ret |= SETIREG(SISPART1, 0x2e, 0x08);
2004
2005 ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
2006 tmp32 &= 0x00f00000;
2007 tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
2008 ret |= SETIREG(SISSR, 0x25, tmp8);
2009 tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
2010 ret |= SETIREG(SISCR, 0x49, tmp8);
2011
2012 ret |= SETIREG(SISSR, 0x27, 0x1f);
2013 ret |= SETIREG(SISSR, 0x31, 0x00);
2014 ret |= SETIREG(SISSR, 0x32, 0x11);
2015 ret |= SETIREG(SISSR, 0x33, 0x00);
2016
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002017 if (ret)
2018 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019
2020 ret |= SETIREG(SISCR, 0x83, 0x00);
2021
2022 ret |= sisusb_set_default_mode(sisusb, 0);
2023
2024 ret |= SETIREGAND(SISSR, 0x21, 0xdf);
2025 ret |= SETIREGOR(SISSR, 0x01, 0x20);
2026 ret |= SETIREGOR(SISSR, 0x16, 0x0f);
2027
2028 ret |= sisusb_triggersr16(sisusb, ramtype);
2029
2030 /* Disable refresh */
2031 ret |= SETIREGAND(SISSR, 0x17, 0xf8);
2032 ret |= SETIREGOR(SISSR, 0x19, 0x03);
2033
2034 ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
2035 ret |= sisusb_verify_mclk(sisusb);
2036
2037 if (ramtype <= 1) {
2038 ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
2039 if (iret) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002040 dev_err(&sisusb->sisusb_dev->dev,
2041 "RAM size detection failed, assuming 8MB video RAM\n");
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002042 ret |= SETIREG(SISSR, 0x14, 0x31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 /* TODO */
2044 }
2045 } else {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002046 dev_err(&sisusb->sisusb_dev->dev,
2047 "DDR RAM device found, assuming 8MB video RAM\n");
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002048 ret |= SETIREG(SISSR, 0x14, 0x31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 /* *** TODO *** */
2050 }
2051
2052 /* Enable refresh */
2053 ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
2054 ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
2055 ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
2056
2057 ret |= SETIREGOR(SISSR, 0x21, 0x20);
2058
2059 ret |= SETIREG(SISSR, 0x22, 0xfb);
2060 ret |= SETIREG(SISSR, 0x21, 0xa5);
2061
2062 if (ret == 0)
2063 break;
2064 }
2065
2066 return ret;
2067}
2068
2069#undef SETREG
2070#undef GETREG
2071#undef SETIREG
2072#undef GETIREG
2073#undef SETIREGOR
2074#undef SETIREGAND
2075#undef SETIREGANDOR
2076#undef READL
2077#undef WRITEL
2078
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002079static void sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080{
2081 u8 tmp8, tmp82, ramtype;
2082 int bw = 0;
2083 char *ramtypetext1 = NULL;
Joe Perches3c1b2c32014-02-24 10:01:16 -08002084 static const char ram_datarate[4] = {'S', 'S', 'D', 'D'};
2085 static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 static const int busSDR[4] = {64, 64, 128, 128};
2087 static const int busDDR[4] = {32, 32, 64, 64};
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002088 static const int busDDRA[4] = {64+32, 64+32, (64+32)*2, (64+32)*2};
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089
2090 sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
2091 sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
2092 sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
2093 sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
2094 ramtype &= 0x03;
2095 switch ((tmp8 >> 2) & 0x03) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002096 case 0:
2097 ramtypetext1 = "1 ch/1 r";
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01002098 if (tmp82 & 0x10)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 bw = 32;
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01002100 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 bw = busSDR[(tmp8 & 0x03)];
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01002102
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 break;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002104 case 1:
2105 ramtypetext1 = "1 ch/2 r";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 sisusb->vramsize <<= 1;
2107 bw = busSDR[(tmp8 & 0x03)];
2108 break;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002109 case 2:
Colin Ian Kinga4a00f62018-04-27 20:12:31 +01002110 ramtypetext1 = "asymmetric";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 sisusb->vramsize += sisusb->vramsize/2;
2112 bw = busDDRA[(tmp8 & 0x03)];
2113 break;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002114 case 3:
2115 ramtypetext1 = "2 channel";
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 sisusb->vramsize <<= 1;
2117 bw = busDDR[(tmp8 & 0x03)];
2118 break;
2119 }
2120
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002121 dev_info(&sisusb->sisusb_dev->dev,
2122 "%dMB %s %cDR S%cRAM, bus width %d\n",
2123 sisusb->vramsize >> 20, ramtypetext1,
2124 ram_datarate[ramtype], ram_dynamictype[ramtype], bw);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125}
2126
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002127static int sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128{
2129 struct sisusb_packet packet;
2130 int ret;
2131 u32 tmp32;
2132
2133 /* Do some magic */
2134 packet.header = 0x001f;
2135 packet.address = 0x00000324;
2136 packet.data = 0x00000004;
2137 ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2138
2139 packet.header = 0x001f;
2140 packet.address = 0x00000364;
2141 packet.data = 0x00000004;
2142 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2143
2144 packet.header = 0x001f;
2145 packet.address = 0x00000384;
2146 packet.data = 0x00000004;
2147 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2148
2149 packet.header = 0x001f;
2150 packet.address = 0x00000100;
2151 packet.data = 0x00000700;
2152 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2153
2154 packet.header = 0x000f;
2155 packet.address = 0x00000004;
2156 ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
2157 packet.data |= 0x17;
2158 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2159
2160 /* Init BAR 0 (VRAM) */
2161 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2162 ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
2163 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
2164 tmp32 &= 0x0f;
2165 tmp32 |= SISUSB_PCI_MEMBASE;
2166 ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
2167
2168 /* Init BAR 1 (MMIO) */
2169 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2170 ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
2171 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
2172 tmp32 &= 0x0f;
2173 tmp32 |= SISUSB_PCI_MMIOBASE;
2174 ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
2175
2176 /* Init BAR 2 (i/o ports) */
2177 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2178 ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
2179 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
2180 tmp32 &= 0x0f;
2181 tmp32 |= SISUSB_PCI_IOPORTBASE;
2182 ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
2183
2184 /* Enable memory and i/o access */
2185 ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
2186 tmp32 |= 0x3;
2187 ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
2188
2189 if (ret == 0) {
2190 /* Some further magic */
2191 packet.header = 0x001f;
2192 packet.address = 0x00000050;
2193 packet.data = 0x000000ff;
2194 ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
2195 }
2196
2197 return ret;
2198}
2199
2200/* Initialize the graphics device (return 0 on success)
2201 * This initializes the net2280 as well as the PCI registers
2202 * of the graphics board.
2203 */
2204
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002205static int sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206{
2207 int ret = 0, test = 0;
2208 u32 tmp32;
2209
2210 if (sisusb->devinit == 1) {
2211 /* Read PCI BARs and see if they have been set up */
2212 ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002213 if (ret)
2214 return ret;
2215
2216 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE)
2217 test++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218
2219 ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002220 if (ret)
2221 return ret;
2222
2223 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE)
2224 test++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225
2226 ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002227 if (ret)
2228 return ret;
2229
2230 if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE)
2231 test++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 }
2233
2234 /* No? So reset the device */
2235 if ((sisusb->devinit == 0) || (test != 3)) {
2236
2237 ret |= sisusb_do_init_gfxdevice(sisusb);
2238
2239 if (ret == 0)
2240 sisusb->devinit = 1;
2241
2242 }
2243
2244 if (sisusb->devinit) {
2245 /* Initialize the graphics core */
2246 if (sisusb_init_gfxcore(sisusb) == 0) {
2247 sisusb->gfxinit = 1;
2248 sisusb_get_ramconfig(sisusb);
2249 ret |= sisusb_set_default_mode(sisusb, 1);
2250 ret |= sisusb_setup_screen(sisusb, 1, initscreen);
2251 }
2252 }
2253
2254 return ret;
2255}
2256
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002257
Jiri Slaby022e4682019-01-22 16:12:00 +01002258#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002259
2260/* Set up default text mode:
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01002261 * - Set text mode (0x03)
2262 * - Upload default font
2263 * - Upload user font (if available)
2264 */
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002265
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002266int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002267{
2268 int ret = 0, slot = sisusb->font_slot, i;
Andrew Mortondabb5922005-09-22 23:55:22 -07002269 const struct font_desc *myfont;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002270 u8 *tempbuf;
2271 u16 *tempbufb;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002272 static const char bootstring[] =
2273 "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
Arjan van de Ven4c4c9432005-11-29 09:43:42 +01002274 static const char bootlogo[] = "(o_ //\\ V_/_";
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002275
2276 /* sisusb->lock is down */
2277
2278 if (!sisusb->SiS_Pr)
2279 return 1;
2280
2281 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2282 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2283
2284 /* Set mode 0x03 */
2285 SiSUSBSetMode(sisusb->SiS_Pr, 0x03);
2286
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002287 myfont = find_font("VGA8x16");
2288 if (!myfont)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002289 return 1;
2290
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002291 tempbuf = vmalloc(8192);
2292 if (!tempbuf)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002293 return 1;
2294
2295 for (i = 0; i < 256; i++)
2296 memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16);
2297
2298 /* Upload default font */
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002299 ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192,
2300 0, 1, NULL, 16, 0);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002301
2302 vfree(tempbuf);
2303
2304 /* Upload user font (and reset current slot) */
2305 if (sisusb->font_backup) {
2306 ret |= sisusbcon_do_font_op(sisusb, 1, 2, sisusb->font_backup,
2307 8192, sisusb->font_backup_512, 1, NULL,
2308 sisusb->font_backup_height, 0);
2309 if (slot != 2)
2310 sisusbcon_do_font_op(sisusb, 1, 0, NULL, 0, 0, 1,
2311 NULL, 16, 0);
2312 }
2313
2314 if (init && !sisusb->scrbuf) {
2315
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002316 tempbuf = vmalloc(8192);
2317 if (tempbuf) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002318
2319 i = 4096;
2320 tempbufb = (u16 *)tempbuf;
2321 while (i--)
2322 *(tempbufb++) = 0x0720;
2323
2324 i = 0;
2325 tempbufb = (u16 *)tempbuf;
2326 while (bootlogo[i]) {
2327 *(tempbufb++) = 0x0700 | bootlogo[i++];
2328 if (!(i % 4))
2329 tempbufb += 76;
2330 }
2331
2332 i = 0;
2333 tempbufb = (u16 *)tempbuf + 6;
2334 while (bootstring[i])
2335 *(tempbufb++) = 0x0700 | bootstring[i++];
2336
2337 ret |= sisusb_copy_memory(sisusb, tempbuf,
Jiri Slaby3cc5be72016-06-23 13:34:25 +02002338 sisusb->vrambase, 8192);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002339
2340 vfree(tempbuf);
2341
2342 }
2343
2344 } else if (sisusb->scrbuf) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002345 ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf,
Jiri Slaby3cc5be72016-06-23 13:34:25 +02002346 sisusb->vrambase, sisusb->scrbuf_size);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002347 }
2348
2349 if (sisusb->sisusb_cursor_size_from >= 0 &&
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002350 sisusb->sisusb_cursor_size_to >= 0) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002351 sisusb_setidxreg(sisusb, SISCR, 0x0a,
2352 sisusb->sisusb_cursor_size_from);
2353 sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0,
2354 sisusb->sisusb_cursor_size_to);
2355 } else {
2356 sisusb_setidxreg(sisusb, SISCR, 0x0a, 0x2d);
2357 sisusb_setidxreg(sisusb, SISCR, 0x0b, 0x0e);
2358 sisusb->sisusb_cursor_size_to = -1;
2359 }
2360
2361 slot = sisusb->sisusb_cursor_loc;
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002362 if (slot < 0)
2363 slot = 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002364
2365 sisusb->sisusb_cursor_loc = -1;
2366 sisusb->bad_cursor_pos = 1;
2367
2368 sisusb_set_cursor(sisusb, slot);
2369
2370 sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
2371 sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
2372
2373 sisusb->textmodedestroyed = 0;
2374
2375 /* sisusb->lock is down */
2376
2377 return ret;
2378}
2379
2380#endif
2381
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382/* fops */
2383
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002384static int sisusb_open(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385{
2386 struct sisusb_usb_data *sisusb;
2387 struct usb_interface *interface;
2388 int subminor = iminor(inode);
2389
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002390 interface = usb_find_interface(&sisusb_driver, subminor);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01002391 if (!interface)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002394 sisusb = usb_get_intfdata(interface);
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01002395 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397
Arjan van de Ven2682d272006-03-28 01:00:21 -08002398 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399
2400 if (!sisusb->present || !sisusb->ready) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002401 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 return -ENODEV;
2403 }
2404
2405 if (sisusb->isopen) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002406 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 return -EBUSY;
2408 }
2409
2410 if (!sisusb->devinit) {
Oliver Neukum20a12f02010-07-16 17:36:26 +02002411 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH ||
Oliver Neukum88a0044e2016-05-02 13:09:30 +02002412 sisusb->sisusb_dev->speed >= USB_SPEED_SUPER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 if (sisusb_init_gfxdevice(sisusb, 0)) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002414 mutex_unlock(&sisusb->lock);
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002415 dev_err(&sisusb->sisusb_dev->dev,
2416 "Failed to initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 return -EIO;
2418 }
2419 } else {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002420 mutex_unlock(&sisusb->lock);
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002421 dev_err(&sisusb->sisusb_dev->dev,
2422 "Device not attached to USB 2.0 hub\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 return -EIO;
2424 }
2425 }
2426
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002427 /* Increment usage count for our sisusb */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 kref_get(&sisusb->kref);
2429
2430 sisusb->isopen = 1;
2431
2432 file->private_data = sisusb;
2433
Arjan van de Ven2682d272006-03-28 01:00:21 -08002434 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 return 0;
2437}
2438
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002439void sisusb_delete(struct kref *kref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440{
2441 struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
2442
2443 if (!sisusb)
2444 return;
2445
Markus Elfringbb2d43e2014-11-21 15:50:44 +01002446 usb_put_dev(sisusb->sisusb_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447
2448 sisusb->sisusb_dev = NULL;
2449 sisusb_free_buffers(sisusb);
2450 sisusb_free_urbs(sisusb);
Jiri Slaby022e4682019-01-22 16:12:00 +01002451#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002452 kfree(sisusb->SiS_Pr);
2453#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 kfree(sisusb);
2455}
2456
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002457static int sisusb_release(struct inode *inode, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458{
2459 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002461 sisusb = file->private_data;
2462 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464
Arjan van de Ven2682d272006-03-28 01:00:21 -08002465 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466
2467 if (sisusb->present) {
2468 /* Wait for all URBs to finish if device still present */
2469 if (!sisusb_wait_all_out_complete(sisusb))
2470 sisusb_kill_all_busy(sisusb);
2471 }
2472
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473 sisusb->isopen = 0;
2474 file->private_data = NULL;
2475
Arjan van de Ven2682d272006-03-28 01:00:21 -08002476 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477
2478 /* decrement the usage count on our device */
2479 kref_put(&sisusb->kref, sisusb_delete);
2480
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481 return 0;
2482}
2483
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002484static ssize_t sisusb_read(struct file *file, char __user *buffer,
2485 size_t count, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486{
2487 struct sisusb_usb_data *sisusb;
2488 ssize_t bytes_read = 0;
2489 int errno = 0;
2490 u8 buf8;
2491 u16 buf16;
2492 u32 buf32, address;
2493
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002494 sisusb = file->private_data;
2495 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 return -ENODEV;
2497
Arjan van de Ven2682d272006-03-28 01:00:21 -08002498 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499
2500 /* Sanity check */
2501 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002502 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 return -ENODEV;
2504 }
2505
2506 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002507 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002509 address = (*ppos) - SISUSB_PCI_PSEUDO_IOPORTBASE +
2510 SISUSB_PCI_IOPORTBASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511
2512 /* Read i/o ports
2513 * Byte, word and long(32) can be read. As this
2514 * emulates inX instructions, the data returned is
2515 * in machine-endianness.
2516 */
2517 switch (count) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002518 case 1:
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002519 if (sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002520 address, &buf8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 errno = -EIO;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002522 else if (put_user(buf8, (u8 __user *)buffer))
2523 errno = -EFAULT;
2524 else
2525 bytes_read = 1;
2526
2527 break;
2528
2529 case 2:
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002530 if (sisusb_read_memio_word(sisusb, SISUSB_TYPE_IO,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002531 address, &buf16))
2532 errno = -EIO;
2533 else if (put_user(buf16, (u16 __user *)buffer))
2534 errno = -EFAULT;
2535 else
2536 bytes_read = 2;
2537
2538 break;
2539
2540 case 4:
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002541 if (sisusb_read_memio_long(sisusb, SISUSB_TYPE_IO,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002542 address, &buf32))
2543 errno = -EIO;
2544 else if (put_user(buf32, (u32 __user *)buffer))
2545 errno = -EFAULT;
2546 else
2547 bytes_read = 4;
2548
2549 break;
2550
2551 default:
2552 errno = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553
2554 }
2555
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002556 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && (*ppos) <
2557 SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002559 address = (*ppos) - SISUSB_PCI_PSEUDO_MEMBASE +
2560 SISUSB_PCI_MEMBASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561
2562 /* Read video ram
2563 * Remember: Data delivered is never endian-corrected
2564 */
2565 errno = sisusb_read_mem_bulk(sisusb, address,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002566 NULL, count, buffer, &bytes_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567
2568 if (bytes_read)
2569 errno = bytes_read;
2570
2571 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002572 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE +
2573 SISUSB_PCI_MMIOSIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002575 address = (*ppos) - SISUSB_PCI_PSEUDO_MMIOBASE +
2576 SISUSB_PCI_MMIOBASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577
2578 /* Read MMIO
2579 * Remember: Data delivered is never endian-corrected
2580 */
2581 errno = sisusb_read_mem_bulk(sisusb, address,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002582 NULL, count, buffer, &bytes_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583
2584 if (bytes_read)
2585 errno = bytes_read;
2586
2587 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002588 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589
2590 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002591 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 return -EINVAL;
2593 }
2594
2595 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2596
2597 /* Read PCI config register
2598 * Return value delivered in machine endianness.
2599 */
2600 if (sisusb_read_pci_config(sisusb, address, &buf32))
2601 errno = -EIO;
2602 else if (put_user(buf32, (u32 __user *)buffer))
2603 errno = -EFAULT;
2604 else
2605 bytes_read = 4;
2606
2607 } else {
2608
2609 errno = -EBADFD;
2610
2611 }
2612
2613 (*ppos) += bytes_read;
2614
Arjan van de Ven2682d272006-03-28 01:00:21 -08002615 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
2617 return errno ? errno : bytes_read;
2618}
2619
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002620static ssize_t sisusb_write(struct file *file, const char __user *buffer,
2621 size_t count, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622{
2623 struct sisusb_usb_data *sisusb;
2624 int errno = 0;
2625 ssize_t bytes_written = 0;
2626 u8 buf8;
2627 u16 buf16;
2628 u32 buf32, address;
2629
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002630 sisusb = file->private_data;
2631 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632 return -ENODEV;
2633
Arjan van de Ven2682d272006-03-28 01:00:21 -08002634 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635
2636 /* Sanity check */
2637 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002638 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639 return -ENODEV;
2640 }
2641
2642 if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002643 (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002645 address = (*ppos) - SISUSB_PCI_PSEUDO_IOPORTBASE +
2646 SISUSB_PCI_IOPORTBASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647
2648 /* Write i/o ports
2649 * Byte, word and long(32) can be written. As this
2650 * emulates outX instructions, the data is expected
2651 * in machine-endianness.
2652 */
2653 switch (count) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002654 case 1:
2655 if (get_user(buf8, (u8 __user *)buffer))
2656 errno = -EFAULT;
2657 else if (sisusb_write_memio_byte(sisusb,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002658 SISUSB_TYPE_IO, address, buf8))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659 errno = -EIO;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002660 else
2661 bytes_written = 1;
2662
2663 break;
2664
2665 case 2:
2666 if (get_user(buf16, (u16 __user *)buffer))
2667 errno = -EFAULT;
2668 else if (sisusb_write_memio_word(sisusb,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002669 SISUSB_TYPE_IO, address, buf16))
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002670 errno = -EIO;
2671 else
2672 bytes_written = 2;
2673
2674 break;
2675
2676 case 4:
2677 if (get_user(buf32, (u32 __user *)buffer))
2678 errno = -EFAULT;
2679 else if (sisusb_write_memio_long(sisusb,
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002680 SISUSB_TYPE_IO, address, buf32))
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002681 errno = -EIO;
2682 else
2683 bytes_written = 4;
2684
2685 break;
2686
2687 default:
2688 errno = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 }
2690
2691 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002692 (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE +
2693 sisusb->vramsize) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002695 address = (*ppos) - SISUSB_PCI_PSEUDO_MEMBASE +
2696 SISUSB_PCI_MEMBASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697
2698 /* Write video ram.
2699 * Buffer is copied 1:1, therefore, on big-endian
2700 * machines, the data must be swapped by userland
2701 * in advance (if applicable; no swapping in 8bpp
2702 * mode or if YUV data is being transferred).
2703 */
2704 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002705 count, buffer, 0, &bytes_written);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706
2707 if (bytes_written)
2708 errno = bytes_written;
2709
2710 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002711 (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE +
2712 SISUSB_PCI_MMIOSIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002714 address = (*ppos) - SISUSB_PCI_PSEUDO_MMIOBASE +
2715 SISUSB_PCI_MMIOBASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716
2717 /* Write MMIO.
2718 * Buffer is copied 1:1, therefore, on big-endian
2719 * machines, the data must be swapped by userland
2720 * in advance.
2721 */
2722 errno = sisusb_write_mem_bulk(sisusb, address, NULL,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002723 count, buffer, 0, &bytes_written);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724
2725 if (bytes_written)
2726 errno = bytes_written;
2727
2728 } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002729 (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE +
2730 SISUSB_PCI_PCONFSIZE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731
2732 if (count != 4) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002733 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734 return -EINVAL;
2735 }
2736
2737 address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
2738
2739 /* Write PCI config register.
2740 * Given value expected in machine endianness.
2741 */
2742 if (get_user(buf32, (u32 __user *)buffer))
2743 errno = -EFAULT;
2744 else if (sisusb_write_pci_config(sisusb, address, buf32))
2745 errno = -EIO;
2746 else
2747 bytes_written = 4;
2748
2749
2750 } else {
2751
2752 /* Error */
2753 errno = -EBADFD;
2754
2755 }
2756
2757 (*ppos) += bytes_written;
2758
Arjan van de Ven2682d272006-03-28 01:00:21 -08002759 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760
2761 return errno ? errno : bytes_written;
2762}
2763
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002764static loff_t sisusb_lseek(struct file *file, loff_t offset, int orig)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765{
2766 struct sisusb_usb_data *sisusb;
2767 loff_t ret;
2768
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002769 sisusb = file->private_data;
2770 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 return -ENODEV;
2772
Arjan van de Ven2682d272006-03-28 01:00:21 -08002773 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774
2775 /* Sanity check */
2776 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
Arjan van de Ven2682d272006-03-28 01:00:21 -08002777 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 return -ENODEV;
2779 }
2780
Al Virob25472f2015-12-05 22:04:48 -05002781 ret = no_seek_end_llseek(file, offset, orig);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782
Arjan van de Ven2682d272006-03-28 01:00:21 -08002783 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 return ret;
2785}
2786
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002787static int sisusb_handle_command(struct sisusb_usb_data *sisusb,
2788 struct sisusb_command *y, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789{
Felipe Balbied86d972007-08-10 09:34:24 -04002790 int retval, port, length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 u32 address;
2792
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002793 /* All our commands require the device
2794 * to be initialized.
2795 */
2796 if (!sisusb->devinit)
2797 return -ENODEV;
2798
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 port = y->data3 -
2800 SISUSB_PCI_PSEUDO_IOPORTBASE +
2801 SISUSB_PCI_IOPORTBASE;
2802
2803 switch (y->operation) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002804 case SUCMD_GET:
2805 retval = sisusb_getidxreg(sisusb, port, y->data0, &y->data1);
2806 if (!retval) {
2807 if (copy_to_user((void __user *)arg, y, sizeof(*y)))
2808 retval = -EFAULT;
2809 }
2810 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002812 case SUCMD_SET:
2813 retval = sisusb_setidxreg(sisusb, port, y->data0, y->data1);
2814 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002816 case SUCMD_SETOR:
2817 retval = sisusb_setidxregor(sisusb, port, y->data0, y->data1);
2818 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002820 case SUCMD_SETAND:
2821 retval = sisusb_setidxregand(sisusb, port, y->data0, y->data1);
2822 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002824 case SUCMD_SETANDOR:
2825 retval = sisusb_setidxregandor(sisusb, port, y->data0,
2826 y->data1, y->data2);
2827 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002829 case SUCMD_SETMASK:
2830 retval = sisusb_setidxregmask(sisusb, port, y->data0,
2831 y->data1, y->data2);
2832 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002834 case SUCMD_CLRSCR:
2835 /* Gfx core must be initialized */
2836 if (!sisusb->gfxinit)
2837 return -ENODEV;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002838
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002839 length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
2840 address = y->data3 - SISUSB_PCI_PSEUDO_MEMBASE +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 SISUSB_PCI_MEMBASE;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002842 retval = sisusb_clear_vram(sisusb, address, length);
2843 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002845 case SUCMD_HANDLETEXTMODE:
2846 retval = 0;
Jiri Slaby022e4682019-01-22 16:12:00 +01002847#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002848 /* Gfx core must be initialized, SiS_Pr must exist */
2849 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2850 return -ENODEV;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002851
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002852 switch (y->data0) {
2853 case 0:
2854 retval = sisusb_reset_text_mode(sisusb, 0);
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002855 break;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002856 case 1:
2857 sisusb->textmodedestroyed = 1;
2858 break;
2859 }
2860#endif
2861 break;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002862
Jiri Slaby022e4682019-01-22 16:12:00 +01002863#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002864 case SUCMD_SETMODE:
2865 /* Gfx core must be initialized, SiS_Pr must exist */
2866 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2867 return -ENODEV;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002868
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002869 retval = 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002870
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002871 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2872 sisusb->SiS_Pr->sisusb = (void *)sisusb;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002873
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002874 if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 retval = -EINVAL;
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002876
2877 break;
2878
2879 case SUCMD_SETVESAMODE:
2880 /* Gfx core must be initialized, SiS_Pr must exist */
2881 if (!sisusb->gfxinit || !sisusb->SiS_Pr)
2882 return -ENODEV;
2883
2884 retval = 0;
2885
2886 sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
2887 sisusb->SiS_Pr->sisusb = (void *)sisusb;
2888
2889 if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3))
2890 retval = -EINVAL;
2891
2892 break;
2893#endif
2894
2895 default:
2896 retval = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 }
2898
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002899 if (retval > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 retval = -EIO;
2901
2902 return retval;
2903}
2904
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002905static long sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906{
2907 struct sisusb_usb_data *sisusb;
2908 struct sisusb_info x;
2909 struct sisusb_command y;
Alessio Igor Bogani41f2c6e2010-03-01 14:10:56 +01002910 long retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911 u32 __user *argp = (u32 __user *)arg;
2912
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02002913 sisusb = file->private_data;
2914 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915 return -ENODEV;
2916
Arjan van de Ven2682d272006-03-28 01:00:21 -08002917 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918
2919 /* Sanity check */
2920 if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
2921 retval = -ENODEV;
2922 goto err_out;
2923 }
2924
2925 switch (cmd) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002926 case SISUSB_GET_CONFIG_SIZE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002928 if (put_user(sizeof(x), argp))
2929 retval = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002931 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002933 case SISUSB_GET_CONFIG:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002934
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002935 x.sisusb_id = SISUSB_ID;
2936 x.sisusb_version = SISUSB_VERSION;
2937 x.sisusb_revision = SISUSB_REVISION;
2938 x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
2939 x.sisusb_gfxinit = sisusb->gfxinit;
2940 x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE;
2941 x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE;
2942 x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE;
2943 x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE;
2944 x.sisusb_vramsize = sisusb->vramsize;
2945 x.sisusb_minor = sisusb->minor;
2946 x.sisusb_fbdevactive = 0;
Jiri Slaby022e4682019-01-22 16:12:00 +01002947#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002948 x.sisusb_conactive = sisusb->haveconsole ? 1 : 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002949#else
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002950 x.sisusb_conactive = 0;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02002951#endif
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002952 memset(x.sisusb_reserved, 0, sizeof(x.sisusb_reserved));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002954 if (copy_to_user((void __user *)arg, &x, sizeof(x)))
2955 retval = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002957 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002958
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002959 case SISUSB_COMMAND:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002961 if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
2962 retval = -EFAULT;
2963 else
2964 retval = sisusb_handle_command(sisusb, &y, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002966 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002968 default:
2969 retval = -ENOTTY;
2970 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 }
2972
2973err_out:
Arjan van de Ven2682d272006-03-28 01:00:21 -08002974 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 return retval;
2976}
2977
Jiri Slaby022e4682019-01-22 16:12:00 +01002978#ifdef CONFIG_COMPAT
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01002979static long sisusb_compat_ioctl(struct file *f, unsigned int cmd,
2980 unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002982 switch (cmd) {
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002983 case SISUSB_GET_CONFIG_SIZE:
2984 case SISUSB_GET_CONFIG:
2985 case SISUSB_COMMAND:
Gustavo A. R. Silvae6f9e132017-07-09 22:31:31 -05002986 return sisusb_ioctl(f, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01002988 default:
2989 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990 }
2991}
2992#endif
2993
Luiz Fernando N. Capitulino066202d2006-08-05 20:37:11 -03002994static const struct file_operations usb_sisusb_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995 .owner = THIS_MODULE,
2996 .open = sisusb_open,
2997 .release = sisusb_release,
2998 .read = sisusb_read,
2999 .write = sisusb_write,
Felipe Balbied86d972007-08-10 09:34:24 -04003000 .llseek = sisusb_lseek,
Jiri Slaby022e4682019-01-22 16:12:00 +01003001#ifdef CONFIG_COMPAT
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002 .compat_ioctl = sisusb_compat_ioctl,
3003#endif
Alan Cox49f15252008-05-22 22:48:48 +01003004 .unlocked_ioctl = sisusb_ioctl
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005};
3006
3007static struct usb_class_driver usb_sisusb_class = {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003008 .name = "sisusbvga%d",
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003009 .fops = &usb_sisusb_fops,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003010 .minor_base = SISUSB_MINOR
3011};
3012
3013static int sisusb_probe(struct usb_interface *intf,
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01003014 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015{
3016 struct usb_device *dev = interface_to_usbdev(intf);
3017 struct sisusb_usb_data *sisusb;
3018 int retval = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003020 dev_info(&dev->dev, "USB2VGA dongle found at address %d\n",
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01003021 dev->devnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003022
3023 /* Allocate memory for our private */
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02003024 sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL);
Peter Senna Tschudinf40849b2016-01-15 18:41:33 +01003025 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003026 return -ENOMEM;
Peter Senna Tschudinf40849b2016-01-15 18:41:33 +01003027
Linus Torvalds1da177e2005-04-16 15:20:36 -07003028 kref_init(&sisusb->kref);
3029
Arjan van de Ven2682d272006-03-28 01:00:21 -08003030 mutex_init(&(sisusb->lock));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003031
3032 /* Register device */
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02003033 retval = usb_register_dev(intf, &usb_sisusb_class);
3034 if (retval) {
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01003035 dev_err(&sisusb->sisusb_dev->dev,
3036 "Failed to get a minor for device %d\n",
3037 dev->devnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038 retval = -ENODEV;
3039 goto error_1;
3040 }
3041
3042 sisusb->sisusb_dev = dev;
3043 sisusb->minor = intf->minor;
3044 sisusb->vrambase = SISUSB_PCI_MEMBASE;
3045 sisusb->mmiobase = SISUSB_PCI_MMIOBASE;
3046 sisusb->mmiosize = SISUSB_PCI_MMIOSIZE;
3047 sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
3048 /* Everything else is zero */
3049
3050 /* Allocate buffers */
3051 sisusb->ibufsize = SISUSB_IBUF_SIZE;
Peter Senna Tschudin662bfe72016-01-15 18:41:31 +01003052 sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL);
3053 if (!sisusb->ibuf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003054 retval = -ENOMEM;
3055 goto error_2;
3056 }
3057
3058 sisusb->numobufs = 0;
3059 sisusb->obufsize = SISUSB_OBUF_SIZE;
3060 for (i = 0; i < NUMOBUFS; i++) {
Peter Senna Tschudin662bfe72016-01-15 18:41:31 +01003061 sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL);
3062 if (!sisusb->obuf[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003063 if (i == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064 retval = -ENOMEM;
3065 goto error_3;
3066 }
3067 break;
Peter Senna Tschudinf996c492016-01-15 18:41:30 +01003068 }
3069 sisusb->numobufs++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003070 }
3071
3072 /* Allocate URBs */
Peter Senna Tschudin662bfe72016-01-15 18:41:31 +01003073 sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL);
3074 if (!sisusb->sisurbin) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003075 retval = -ENOMEM;
3076 goto error_3;
3077 }
3078 sisusb->completein = 1;
3079
3080 for (i = 0; i < sisusb->numobufs; i++) {
Peter Senna Tschudin662bfe72016-01-15 18:41:31 +01003081 sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL);
3082 if (!sisusb->sisurbout[i]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083 retval = -ENOMEM;
3084 goto error_4;
3085 }
3086 sisusb->urbout_context[i].sisusb = (void *)sisusb;
3087 sisusb->urbout_context[i].urbindex = i;
3088 sisusb->urbstatus[i] = 0;
3089 }
3090
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01003091 dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n",
3092 sisusb->numobufs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093
Jiri Slaby022e4682019-01-22 16:12:00 +01003094#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003095 /* Allocate our SiS_Pr */
Peter Senna Tschudin662bfe72016-01-15 18:41:31 +01003096 sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL);
3097 if (!sisusb->SiS_Pr) {
Peter Senna Tschudin0e781c22016-01-15 18:41:34 +01003098 retval = -ENOMEM;
3099 goto error_4;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003100 }
3101#endif
3102
Linus Torvalds1da177e2005-04-16 15:20:36 -07003103 /* Do remaining init stuff */
3104
3105 init_waitqueue_head(&sisusb->wait_q);
3106
3107 usb_set_intfdata(intf, sisusb);
3108
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003109 usb_get_dev(sisusb->sisusb_dev);
3110
3111 sisusb->present = 1;
3112
Oliver Neukum88a0044e2016-05-02 13:09:30 +02003113 if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003114 int initscreen = 1;
Jiri Slaby022e4682019-01-22 16:12:00 +01003115#ifdef CONFIG_USB_SISUSBVGA_CON
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01003116 if (sisusb_first_vc > 0 && sisusb_last_vc > 0 &&
3117 sisusb_first_vc <= sisusb_last_vc &&
3118 sisusb_last_vc <= MAX_NR_CONSOLES)
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003119 initscreen = 0;
3120#endif
3121 if (sisusb_init_gfxdevice(sisusb, initscreen))
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01003122 dev_err(&sisusb->sisusb_dev->dev,
3123 "Failed to early initialize device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124
3125 } else
Peter Senna Tschudin06e21ef2016-01-15 18:41:29 +01003126 dev_info(&sisusb->sisusb_dev->dev,
3127 "Not attached to USB 2.0 hub, deferring init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003128
3129 sisusb->ready = 1;
3130
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003131#ifdef SISUSBENDIANTEST
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003132 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003133 sisusb_testreadwrite(sisusb);
Felipe Balbi7b5cd5f2007-08-15 10:38:12 -04003134 dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003135#endif
3136
Jiri Slaby022e4682019-01-22 16:12:00 +01003137#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003138 sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
3139#endif
3140
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 return 0;
3142
3143error_4:
3144 sisusb_free_urbs(sisusb);
3145error_3:
3146 sisusb_free_buffers(sisusb);
3147error_2:
3148 usb_deregister_dev(intf, &usb_sisusb_class);
3149error_1:
3150 kfree(sisusb);
3151 return retval;
3152}
3153
3154static void sisusb_disconnect(struct usb_interface *intf)
3155{
3156 struct sisusb_usb_data *sisusb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158 /* This should *not* happen */
Greg Kroah-Hartman187859f2015-04-30 11:33:02 +02003159 sisusb = usb_get_intfdata(intf);
3160 if (!sisusb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003161 return;
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003162
Jiri Slaby022e4682019-01-22 16:12:00 +01003163#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003164 sisusb_console_exit(sisusb);
3165#endif
3166
Alan Sternd4ead162007-05-22 11:46:41 -04003167 usb_deregister_dev(intf, &usb_sisusb_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003168
Arjan van de Ven2682d272006-03-28 01:00:21 -08003169 mutex_lock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170
3171 /* Wait for all URBs to complete and kill them in case (MUST do) */
3172 if (!sisusb_wait_all_out_complete(sisusb))
3173 sisusb_kill_all_busy(sisusb);
3174
Linus Torvalds1da177e2005-04-16 15:20:36 -07003175 usb_set_intfdata(intf, NULL);
3176
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177 sisusb->present = 0;
3178 sisusb->ready = 0;
3179
Arjan van de Ven2682d272006-03-28 01:00:21 -08003180 mutex_unlock(&sisusb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181
3182 /* decrement our usage count */
3183 kref_put(&sisusb->kref, sisusb_delete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184}
3185
Németh Márton33b9e162010-01-10 15:34:45 +01003186static const struct usb_device_id sisusb_table[] = {
samson yeungca9024e2007-08-31 16:40:40 -04003187 { USB_DEVICE(0x0711, 0x0550) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 { USB_DEVICE(0x0711, 0x0900) },
Nobuhiro Iwamatsu3003b9f2006-09-01 11:32:28 +09003189 { USB_DEVICE(0x0711, 0x0901) },
3190 { USB_DEVICE(0x0711, 0x0902) },
Albert Comerma859ff402008-11-04 10:44:01 -08003191 { USB_DEVICE(0x0711, 0x0903) },
Stefan Lippers-Hollmanneaea0432008-08-21 13:46:11 +02003192 { USB_DEVICE(0x0711, 0x0918) },
Tanaka Akirabbcb8bb2010-01-21 02:31:09 +09003193 { USB_DEVICE(0x0711, 0x0920) },
Jóhann B. Guðmundsson58fc90d2013-07-04 21:47:52 +00003194 { USB_DEVICE(0x0711, 0x0950) },
Stephen Hemminger5b6b80a2014-08-25 21:07:47 -07003195 { USB_DEVICE(0x0711, 0x5200) },
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003196 { USB_DEVICE(0x182d, 0x021c) },
Thomas Winischhofercef11122005-04-22 15:06:59 -07003197 { USB_DEVICE(0x182d, 0x0269) },
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198 { }
3199};
3200
Peter Senna Tschudinf74a0392016-01-15 18:41:28 +01003201MODULE_DEVICE_TABLE(usb, sisusb_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202
3203static struct usb_driver sisusb_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204 .name = "sisusb",
3205 .probe = sisusb_probe,
3206 .disconnect = sisusb_disconnect,
Thomas Winischhofer7ab7c342005-04-18 17:39:28 -07003207 .id_table = sisusb_table,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208};
3209
3210static int __init usb_sisusb_init(void)
3211{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003212
Jiri Slaby022e4682019-01-22 16:12:00 +01003213#ifdef CONFIG_USB_SISUSBVGA_CON
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003214 sisusb_init_concode();
3215#endif
3216
Andrew Morton9dcfbd92007-10-02 14:40:46 -07003217 return usb_register(&sisusb_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003218}
3219
3220static void __exit usb_sisusb_exit(void)
3221{
3222 usb_deregister(&sisusb_driver);
3223}
3224
3225module_init(usb_sisusb_init);
3226module_exit(usb_sisusb_exit);
3227
3228MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
Thomas Winischhofer1bbb4f22005-08-29 17:01:16 +02003229MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230MODULE_LICENSE("GPL");
3231