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