xref: /OK3568_Linux_fs/kernel/drivers/usb/misc/sisusbvga/sisusb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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