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