diff options
author | Jiri Slaby (SUSE) <jirislaby@kernel.org> | 2022-12-08 10:07:47 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-12-08 10:44:24 +0100 |
commit | a2f3d83cd74eb7cfc69c92d086ec4509cd9c58fb (patch) | |
tree | 33fb594bf85bed226605e4579a4800f88f6c9ac7 /drivers/usb/misc/sisusbvga/sisusb.c | |
parent | USB: sisusbvga: remove console support (diff) | |
download | wireguard-linux-a2f3d83cd74eb7cfc69c92d086ec4509cd9c58fb.tar.xz wireguard-linux-a2f3d83cd74eb7cfc69c92d086ec4509cd9c58fb.zip |
USB: sisusbvga: rename sisusb.c to sisusbvga.c
As it's the only source for the sisusbvga module, there is no need for a
2-steps build.
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Cc: Rich Felker <dalias@libc.org>
Cc: Thomas Winischhofer <thomas@winischhofer.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-sh@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org>
Link: https://lore.kernel.org/r/20221208090749.28056-2-jirislaby@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/misc/sisusbvga/sisusb.c')
-rw-r--r-- | drivers/usb/misc/sisusbvga/sisusb.c | 2966 |
1 files changed, 0 insertions, 2966 deletions
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c deleted file mode 100644 index a0d5ba8058f8..000000000000 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ /dev/null @@ -1,2966 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) -/* - * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles - * - * Main part - * - * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria - * - * If distributed as part of the Linux kernel, this code is licensed under the - * terms of the GPL v2. - * - * Otherwise, the following license terms apply: - * - * * Redistribution and use in source and binary forms, with or without - * * modification, are permitted provided that the following conditions - * * are met: - * * 1) Redistributions of source code must retain the above copyright - * * notice, this list of conditions and the following disclaimer. - * * 2) Redistributions in binary form must reproduce the above copyright - * * notice, this list of conditions and the following disclaimer in the - * * documentation and/or other materials provided with the distribution. - * * 3) The name of the author may not be used to endorse or promote products - * * derived from this software without specific psisusbr written permission. - * * - * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR - * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Author: Thomas Winischhofer <thomas@winischhofer.net> - * - */ - -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/poll.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/kref.h> -#include <linux/usb.h> -#include <linux/vmalloc.h> - -#include "sisusb.h" - -#define SISUSB_DONTSYNC - -/* Forward declarations / clean-up routines */ - -static struct usb_driver sisusb_driver; - -static void sisusb_free_buffers(struct sisusb_usb_data *sisusb) -{ - int i; - - for (i = 0; i < NUMOBUFS; i++) { - kfree(sisusb->obuf[i]); - sisusb->obuf[i] = NULL; - } - kfree(sisusb->ibuf); - sisusb->ibuf = NULL; -} - -static void sisusb_free_urbs(struct sisusb_usb_data *sisusb) -{ - int i; - - for (i = 0; i < NUMOBUFS; i++) { - usb_free_urb(sisusb->sisurbout[i]); - sisusb->sisurbout[i] = NULL; - } - usb_free_urb(sisusb->sisurbin); - sisusb->sisurbin = NULL; -} - -/* Level 0: USB transport layer */ - -/* 1. out-bulks */ - -/* out-urb management */ - -/* Return 1 if all free, 0 otherwise */ -static int sisusb_all_free(struct sisusb_usb_data *sisusb) -{ - int i; - - for (i = 0; i < sisusb->numobufs; i++) { - - if (sisusb->urbstatus[i] & SU_URB_BUSY) - return 0; - - } - - return 1; -} - -/* Kill all busy URBs */ -static void sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) -{ - int i; - - if (sisusb_all_free(sisusb)) - return; - - for (i = 0; i < sisusb->numobufs; i++) { - - if (sisusb->urbstatus[i] & SU_URB_BUSY) - usb_kill_urb(sisusb->sisurbout[i]); - - } -} - -/* Return 1 if ok, 0 if error (not all complete within timeout) */ -static int sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb) -{ - int timeout = 5 * HZ, i = 1; - - wait_event_timeout(sisusb->wait_q, (i = sisusb_all_free(sisusb)), - timeout); - - return i; -} - -static int sisusb_outurb_available(struct sisusb_usb_data *sisusb) -{ - int i; - - for (i = 0; i < sisusb->numobufs; i++) { - - if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0) - return i; - - } - - return -1; -} - -static int sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb) -{ - int i, timeout = 5 * HZ; - - wait_event_timeout(sisusb->wait_q, - ((i = sisusb_outurb_available(sisusb)) >= 0), timeout); - - return i; -} - -static int sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) -{ - int i; - - i = sisusb_outurb_available(sisusb); - - if (i >= 0) - sisusb->urbstatus[i] |= SU_URB_ALLOC; - - return i; -} - -static void sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) -{ - if ((index >= 0) && (index < sisusb->numobufs)) - sisusb->urbstatus[index] &= ~SU_URB_ALLOC; -} - -/* completion callback */ - -static void sisusb_bulk_completeout(struct urb *urb) -{ - struct sisusb_urb_context *context = urb->context; - struct sisusb_usb_data *sisusb; - - if (!context) - return; - - sisusb = context->sisusb; - - if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) - return; - -#ifndef SISUSB_DONTSYNC - if (context->actual_length) - *(context->actual_length) += urb->actual_length; -#endif - - sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY; - wake_up(&sisusb->wait_q); -} - -static int sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, - unsigned int pipe, void *data, int len, int *actual_length, - int timeout, unsigned int tflags) -{ - struct urb *urb = sisusb->sisurbout[index]; - int retval, byteswritten = 0; - - /* Set up URB */ - urb->transfer_flags = 0; - - usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, - sisusb_bulk_completeout, - &sisusb->urbout_context[index]); - - urb->transfer_flags |= tflags; - urb->actual_length = 0; - - /* Set up context */ - sisusb->urbout_context[index].actual_length = (timeout) ? - NULL : actual_length; - - /* Declare this urb/buffer in use */ - sisusb->urbstatus[index] |= SU_URB_BUSY; - - /* Submit URB */ - retval = usb_submit_urb(urb, GFP_KERNEL); - - /* If OK, and if timeout > 0, wait for completion */ - if ((retval == 0) && timeout) { - wait_event_timeout(sisusb->wait_q, - (!(sisusb->urbstatus[index] & SU_URB_BUSY)), - timeout); - if (sisusb->urbstatus[index] & SU_URB_BUSY) { - /* URB timed out... kill it and report error */ - usb_kill_urb(urb); - retval = -ETIMEDOUT; - } else { - /* Otherwise, report urb status */ - retval = urb->status; - byteswritten = urb->actual_length; - } - } - - if (actual_length) - *actual_length = byteswritten; - - return retval; -} - -/* 2. in-bulks */ - -/* completion callback */ - -static void sisusb_bulk_completein(struct urb *urb) -{ - struct sisusb_usb_data *sisusb = urb->context; - - if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) - return; - - sisusb->completein = 1; - wake_up(&sisusb->wait_q); -} - -static int sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, - unsigned int pipe, void *data, int len, - int *actual_length, int timeout, unsigned int tflags) -{ - struct urb *urb = sisusb->sisurbin; - int retval, readbytes = 0; - - urb->transfer_flags = 0; - - usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, - sisusb_bulk_completein, sisusb); - - urb->transfer_flags |= tflags; - urb->actual_length = 0; - - sisusb->completein = 0; - retval = usb_submit_urb(urb, GFP_KERNEL); - if (retval == 0) { - wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout); - if (!sisusb->completein) { - /* URB timed out... kill it and report error */ - usb_kill_urb(urb); - retval = -ETIMEDOUT; - } else { - /* URB completed within timeout */ - retval = urb->status; - readbytes = urb->actual_length; - } - } - - if (actual_length) - *actual_length = readbytes; - - return retval; -} - - -/* Level 1: */ - -/* Send a bulk message of variable size - * - * To copy the data from userspace, give pointer to "userbuffer", - * to copy from (non-DMA) kernel memory, give "kernbuffer". If - * both of these are NULL, it is assumed, that the transfer - * buffer "sisusb->obuf[index]" is set up with the data to send. - * Index is ignored if either kernbuffer or userbuffer is set. - * If async is nonzero, URBs will be sent without waiting for - * completion of the previous URB. - * - * (return 0 on success) - */ - -static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, - char *kernbuffer, const char __user *userbuffer, int index, - ssize_t *bytes_written, unsigned int tflags, int async) -{ - int result = 0, retry, count = len; - int passsize, thispass, transferred_len = 0; - int fromuser = (userbuffer != NULL) ? 1 : 0; - int fromkern = (kernbuffer != NULL) ? 1 : 0; - unsigned int pipe; - char *buffer; - - (*bytes_written) = 0; - - /* Sanity check */ - if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) - return -ENODEV; - - /* If we copy data from kernel or userspace, force the - * allocation of a buffer/urb. If we have the data in - * the transfer buffer[index] already, reuse the buffer/URB - * if the length is > buffer size. (So, transmitting - * large data amounts directly from the transfer buffer - * treats the buffer as a ring buffer. However, we need - * to sync in this case.) - */ - if (fromuser || fromkern) - index = -1; - else if (len > sisusb->obufsize) - async = 0; - - pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep); - - do { - passsize = thispass = (sisusb->obufsize < count) ? - sisusb->obufsize : count; - - if (index < 0) - index = sisusb_get_free_outbuf(sisusb); - - if (index < 0) - return -EIO; - - buffer = sisusb->obuf[index]; - - if (fromuser) { - - if (copy_from_user(buffer, userbuffer, passsize)) - return -EFAULT; - - userbuffer += passsize; - - } else if (fromkern) { - - memcpy(buffer, kernbuffer, passsize); - kernbuffer += passsize; - - } - - retry = 5; - while (thispass) { - - if (!sisusb->sisusb_dev) - return -ENODEV; - - result = sisusb_bulkout_msg(sisusb, index, pipe, - buffer, thispass, &transferred_len, - async ? 0 : 5 * HZ, tflags); - - if (result == -ETIMEDOUT) { - - /* Will not happen if async */ - if (!retry--) - return -ETIME; - - continue; - } - - if ((result == 0) && !async && transferred_len) { - - thispass -= transferred_len; - buffer += transferred_len; - - } else - break; - } - - if (result) - return result; - - (*bytes_written) += passsize; - count -= passsize; - - /* Force new allocation in next iteration */ - if (fromuser || fromkern) - index = -1; - - } while (count > 0); - - if (async) { -#ifdef SISUSB_DONTSYNC - (*bytes_written) = len; - /* Some URBs/buffers might be busy */ -#else - sisusb_wait_all_out_complete(sisusb); - (*bytes_written) = transferred_len; - /* All URBs and all buffers are available */ -#endif - } - - return ((*bytes_written) == len) ? 0 : -EIO; -} - -/* Receive a bulk message of variable size - * - * To copy the data to userspace, give pointer to "userbuffer", - * to copy to kernel memory, give "kernbuffer". One of them - * MUST be set. (There is no technique for letting the caller - * read directly from the ibuf.) - * - */ - -static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, - void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read, - unsigned int tflags) -{ - int result = 0, retry, count = len; - int bufsize, thispass, transferred_len; - unsigned int pipe; - char *buffer; - - (*bytes_read) = 0; - - /* Sanity check */ - if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) - return -ENODEV; - - pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep); - buffer = sisusb->ibuf; - bufsize = sisusb->ibufsize; - - retry = 5; - -#ifdef SISUSB_DONTSYNC - if (!(sisusb_wait_all_out_complete(sisusb))) - return -EIO; -#endif - - while (count > 0) { - - if (!sisusb->sisusb_dev) - return -ENODEV; - - thispass = (bufsize < count) ? bufsize : count; - - result = sisusb_bulkin_msg(sisusb, pipe, buffer, thispass, - &transferred_len, 5 * HZ, tflags); - - if (transferred_len) - thispass = transferred_len; - - else if (result == -ETIMEDOUT) { - - if (!retry--) - return -ETIME; - - continue; - - } else - return -EIO; - - - if (thispass) { - - (*bytes_read) += thispass; - count -= thispass; - - if (userbuffer) { - - if (copy_to_user(userbuffer, buffer, thispass)) - return -EFAULT; - - userbuffer += thispass; - - } else { - - memcpy(kernbuffer, buffer, thispass); - kernbuffer += thispass; - - } - - } - - } - - return ((*bytes_read) == len) ? 0 : -EIO; -} - -static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, - struct sisusb_packet *packet) -{ - int ret; - ssize_t bytes_transferred = 0; - __le32 tmp; - - if (len == 6) - packet->data = 0; - -#ifdef SISUSB_DONTSYNC - if (!(sisusb_wait_all_out_complete(sisusb))) - return 1; -#endif - - /* Eventually correct endianness */ - SISUSB_CORRECT_ENDIANNESS_PACKET(packet); - - /* 1. send the packet */ - ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len, - (char *)packet, NULL, 0, &bytes_transferred, 0, 0); - - if ((ret == 0) && (len == 6)) { - - /* 2. if packet len == 6, it means we read, so wait for 32bit - * return value and write it to packet->data - */ - ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4, - (char *)&tmp, NULL, &bytes_transferred, 0); - - packet->data = le32_to_cpu(tmp); - } - - return ret; -} - -static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, - struct sisusb_packet *packet, unsigned int tflags) -{ - int ret; - ssize_t bytes_transferred = 0; - __le32 tmp; - - if (len == 6) - packet->data = 0; - -#ifdef SISUSB_DONTSYNC - if (!(sisusb_wait_all_out_complete(sisusb))) - return 1; -#endif - - /* Eventually correct endianness */ - SISUSB_CORRECT_ENDIANNESS_PACKET(packet); - - /* 1. send the packet */ - ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len, - (char *)packet, NULL, 0, &bytes_transferred, tflags, 0); - - if ((ret == 0) && (len == 6)) { - - /* 2. if packet len == 6, it means we read, so wait for 32bit - * return value and write it to packet->data - */ - ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4, - (char *)&tmp, NULL, &bytes_transferred, 0); - - packet->data = le32_to_cpu(tmp); - } - - return ret; -} - -/* access video memory and mmio (return 0 on success) */ - -/* Low level */ - -/* The following routines assume being used to transfer byte, word, - * long etc. - * This means that - * - the write routines expect "data" in machine endianness format. - * The data will be converted to leXX in sisusb_xxx_packet. - * - the read routines can expect read data in machine-endianess. - */ - -static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, - u32 addr, u8 data) -{ - struct sisusb_packet packet; - - packet.header = (1 << (addr & 3)) | (type << 6); - packet.address = addr & ~3; - packet.data = data << ((addr & 3) << 3); - return sisusb_send_packet(sisusb, 10, &packet); -} - -static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type, - u32 addr, u16 data) -{ - struct sisusb_packet packet; - int ret = 0; - - packet.address = addr & ~3; - - switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0003; - packet.data = (u32)data; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 1: - packet.header = (type << 6) | 0x0006; - packet.data = (u32)data << 8; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 2: - packet.header = (type << 6) | 0x000c; - packet.data = (u32)data << 16; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 3: - packet.header = (type << 6) | 0x0008; - packet.data = (u32)data << 24; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - packet.data = (u32)data >> 8; - ret |= sisusb_send_packet(sisusb, 10, &packet); - } - - return ret; -} - -static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 data) -{ - struct sisusb_packet packet; - int ret = 0; - - packet.address = addr & ~3; - - switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0007; - packet.data = data & 0x00ffffff; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 1: - packet.header = (type << 6) | 0x000e; - packet.data = data << 8; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 2: - packet.header = (type << 6) | 0x000c; - packet.data = data << 16; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - packet.data = (data >> 16) & 0x00ff; - ret |= sisusb_send_packet(sisusb, 10, &packet); - break; - case 3: - packet.header = (type << 6) | 0x0008; - packet.data = data << 24; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - packet.data = (data >> 8) & 0xffff; - ret |= sisusb_send_packet(sisusb, 10, &packet); - } - - return ret; -} - -static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 data) -{ - struct sisusb_packet packet; - int ret = 0; - - packet.address = addr & ~3; - - switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x000f; - packet.data = data; - ret = sisusb_send_packet(sisusb, 10, &packet); - break; - case 1: - packet.header = (type << 6) | 0x000e; - packet.data = data << 8; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - packet.data = data >> 24; - ret |= sisusb_send_packet(sisusb, 10, &packet); - break; - case 2: - packet.header = (type << 6) | 0x000c; - packet.data = data << 16; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - packet.data = data >> 16; - ret |= sisusb_send_packet(sisusb, 10, &packet); - break; - case 3: - packet.header = (type << 6) | 0x0008; - packet.data = data << 24; - ret = sisusb_send_packet(sisusb, 10, &packet); - packet.header = (type << 6) | 0x0007; - packet.address = (addr & ~3) + 4; - packet.data = data >> 8; - ret |= sisusb_send_packet(sisusb, 10, &packet); - } - - return ret; -} - -/* The xxx_bulk routines copy a buffer of variable size. They treat the - * buffer as chars, therefore lsb/msb has to be corrected if using the - * byte/word/long/etc routines for speed-up - * - * If data is from userland, set "userbuffer" (and clear "kernbuffer"), - * if data is in kernel space, set "kernbuffer" (and clear "userbuffer"); - * if neither "kernbuffer" nor "userbuffer" are given, it is assumed - * that the data already is in the transfer buffer "sisusb->obuf[index]". - */ - -static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, - char *kernbuffer, int length, const char __user *userbuffer, - int index, ssize_t *bytes_written) -{ - struct sisusb_packet packet; - int ret = 0; - static int msgcount; - u8 swap8, fromkern = kernbuffer ? 1 : 0; - u16 swap16; - u32 swap32, flag = (length >> 28) & 1; - u8 buf[4]; - - /* if neither kernbuffer not userbuffer are given, assume - * data in obuf - */ - if (!fromkern && !userbuffer) - kernbuffer = sisusb->obuf[index]; - - (*bytes_written = 0); - - length &= 0x00ffffff; - - while (length) { - switch (length) { - case 1: - if (userbuffer) { - if (get_user(swap8, (u8 __user *)userbuffer)) - return -EFAULT; - } else - swap8 = kernbuffer[0]; - - ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, - addr, swap8); - - if (!ret) - (*bytes_written)++; - - return ret; - - case 2: - if (userbuffer) { - if (get_user(swap16, (u16 __user *)userbuffer)) - return -EFAULT; - } else - swap16 = *((u16 *)kernbuffer); - - ret = sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - addr, swap16); - - if (!ret) - (*bytes_written) += 2; - - return ret; - - case 3: - if (userbuffer) { - if (copy_from_user(&buf, userbuffer, 3)) - return -EFAULT; -#ifdef __BIG_ENDIAN - swap32 = (buf[0] << 16) | - (buf[1] << 8) | - buf[2]; -#else - swap32 = (buf[2] << 16) | - (buf[1] << 8) | - buf[0]; -#endif - } else -#ifdef __BIG_ENDIAN - swap32 = (kernbuffer[0] << 16) | - (kernbuffer[1] << 8) | - kernbuffer[2]; -#else - swap32 = (kernbuffer[2] << 16) | - (kernbuffer[1] << 8) | - kernbuffer[0]; -#endif - - ret = sisusb_write_memio_24bit(sisusb, SISUSB_TYPE_MEM, - addr, swap32); - - if (!ret) - (*bytes_written) += 3; - - return ret; - - case 4: - if (userbuffer) { - if (get_user(swap32, (u32 __user *)userbuffer)) - return -EFAULT; - } else - swap32 = *((u32 *)kernbuffer); - - ret = sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, - addr, swap32); - if (!ret) - (*bytes_written) += 4; - - return ret; - - default: - if ((length & ~3) > 0x10000) { - - packet.header = 0x001f; - packet.address = 0x000001d4; - packet.data = addr; - ret = sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - packet.header = 0x001f; - packet.address = 0x000001d0; - packet.data = (length & ~3); - ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - packet.header = 0x001f; - packet.address = 0x000001c0; - packet.data = flag | 0x16; - ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - if (userbuffer) { - ret |= sisusb_send_bulk_msg(sisusb, - SISUSB_EP_GFX_LBULK_OUT, - (length & ~3), - NULL, userbuffer, 0, - bytes_written, 0, 1); - userbuffer += (*bytes_written); - } else if (fromkern) { - ret |= sisusb_send_bulk_msg(sisusb, - SISUSB_EP_GFX_LBULK_OUT, - (length & ~3), - kernbuffer, NULL, 0, - bytes_written, 0, 1); - kernbuffer += (*bytes_written); - } else { - ret |= sisusb_send_bulk_msg(sisusb, - SISUSB_EP_GFX_LBULK_OUT, - (length & ~3), - NULL, NULL, index, - bytes_written, 0, 1); - kernbuffer += ((*bytes_written) & - (sisusb->obufsize-1)); - } - - } else { - - packet.header = 0x001f; - packet.address = 0x00000194; - packet.data = addr; - ret = sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - packet.header = 0x001f; - packet.address = 0x00000190; - packet.data = (length & ~3); - ret |= sisusb_send_bridge_packet(sisusb, 10, - &packet, 0); - if (sisusb->flagb0 != 0x16) { - packet.header = 0x001f; - packet.address = 0x00000180; - packet.data = flag | 0x16; - ret |= sisusb_send_bridge_packet(sisusb, - 10, &packet, 0); - sisusb->flagb0 = 0x16; - } - if (userbuffer) { - ret |= sisusb_send_bulk_msg(sisusb, - SISUSB_EP_GFX_BULK_OUT, - (length & ~3), - NULL, userbuffer, 0, - bytes_written, 0, 1); - userbuffer += (*bytes_written); - } else if (fromkern) { - ret |= sisusb_send_bulk_msg(sisusb, - SISUSB_EP_GFX_BULK_OUT, - (length & ~3), - kernbuffer, NULL, 0, - bytes_written, 0, 1); - kernbuffer += (*bytes_written); - } else { - ret |= sisusb_send_bulk_msg(sisusb, - SISUSB_EP_GFX_BULK_OUT, - (length & ~3), - NULL, NULL, index, - bytes_written, 0, 1); - kernbuffer += ((*bytes_written) & - (sisusb->obufsize-1)); - } - } - if (ret) { - msgcount++; - if (msgcount < 500) - dev_err(&sisusb->sisusb_dev->dev, - "Wrote %zd of %d bytes, error %d\n", - *bytes_written, length, - ret); - else if (msgcount == 500) - dev_err(&sisusb->sisusb_dev->dev, - "Too many errors, logging stopped\n"); - } - addr += (*bytes_written); - length -= (*bytes_written); - } - - if (ret) - break; - - } - - return ret ? -EIO : 0; -} - -/* Remember: Read data in packet is in machine-endianess! So for - * byte, word, 24bit, long no endian correction is necessary. - */ - -static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, - u32 addr, u8 *data) -{ - struct sisusb_packet packet; - int ret; - - CLEARPACKET(&packet); - packet.header = (1 << (addr & 3)) | (type << 6); - packet.address = addr & ~3; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u8)(packet.data >> ((addr & 3) << 3)); - return ret; -} - -static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type, - u32 addr, u16 *data) -{ - struct sisusb_packet packet; - int ret = 0; - - CLEARPACKET(&packet); - - packet.address = addr & ~3; - - switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0003; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data); - break; - case 1: - packet.header = (type << 6) | 0x0006; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data >> 8); - break; - case 2: - packet.header = (type << 6) | 0x000c; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data >> 16); - break; - case 3: - packet.header = (type << 6) | 0x0008; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = (u16)(packet.data >> 24); - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (u16)(packet.data << 8); - } - - return ret; -} - -static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 *data) -{ - struct sisusb_packet packet; - int ret = 0; - - packet.address = addr & ~3; - - switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x0007; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data & 0x00ffffff; - break; - case 1: - packet.header = (type << 6) | 0x000e; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 8; - break; - case 2: - packet.header = (type << 6) | 0x000c; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 16; - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= ((packet.data & 0xff) << 16); - break; - case 3: - packet.header = (type << 6) | 0x0008; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 24; - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= ((packet.data & 0xffff) << 8); - } - - return ret; -} - -static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type, - u32 addr, u32 *data) -{ - struct sisusb_packet packet; - int ret = 0; - - packet.address = addr & ~3; - - switch (addr & 3) { - case 0: - packet.header = (type << 6) | 0x000f; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data; - break; - case 1: - packet.header = (type << 6) | 0x000e; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 8; - packet.header = (type << 6) | 0x0001; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (packet.data << 24); - break; - case 2: - packet.header = (type << 6) | 0x000c; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 16; - packet.header = (type << 6) | 0x0003; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (packet.data << 16); - break; - case 3: - packet.header = (type << 6) | 0x0008; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data >> 24; - packet.header = (type << 6) | 0x0007; - packet.address = (addr & ~3) + 4; - ret |= sisusb_send_packet(sisusb, 6, &packet); - *data |= (packet.data << 8); - } - - return ret; -} - -static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, - char *kernbuffer, int length, char __user *userbuffer, - ssize_t *bytes_read) -{ - int ret = 0; - char buf[4]; - u16 swap16; - u32 swap32; - - (*bytes_read = 0); - - length &= 0x00ffffff; - - while (length) { - switch (length) { - case 1: - ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, - addr, &buf[0]); - if (!ret) { - (*bytes_read)++; - if (userbuffer) { - if (put_user(buf[0], (u8 __user *)userbuffer)) - return -EFAULT; - } else - kernbuffer[0] = buf[0]; - } - return ret; - - case 2: - ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, - addr, &swap16); - if (!ret) { - (*bytes_read) += 2; - if (userbuffer) { - if (put_user(swap16, (u16 __user *)userbuffer)) - return -EFAULT; - } else { - *((u16 *)kernbuffer) = swap16; - } - } - return ret; - - case 3: - ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM, - addr, &swap32); - if (!ret) { - (*bytes_read) += 3; -#ifdef __BIG_ENDIAN - buf[0] = (swap32 >> 16) & 0xff; - buf[1] = (swap32 >> 8) & 0xff; - buf[2] = swap32 & 0xff; -#else - buf[2] = (swap32 >> 16) & 0xff; - buf[1] = (swap32 >> 8) & 0xff; - buf[0] = swap32 & 0xff; -#endif - if (userbuffer) { - if (copy_to_user(userbuffer, - &buf[0], 3)) - return -EFAULT; - } else { - kernbuffer[0] = buf[0]; - kernbuffer[1] = buf[1]; - kernbuffer[2] = buf[2]; - } - } - return ret; - - default: - ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, - addr, &swap32); - if (!ret) { - (*bytes_read) += 4; - if (userbuffer) { - if (put_user(swap32, (u32 __user *)userbuffer)) - return -EFAULT; - - userbuffer += 4; - } else { - *((u32 *)kernbuffer) = swap32; - kernbuffer += 4; - } - addr += 4; - length -= 4; - } - } - if (ret) - break; - } - - return ret; -} - -/* High level: Gfx (indexed) register access */ - -static int sisusb_setidxreg(struct sisusb_usb_data *sisusb, u32 port, - u8 index, u8 data) -{ - int ret; - - ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); - ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); - return ret; -} - -static int sisusb_getidxreg(struct sisusb_usb_data *sisusb, u32 port, - u8 index, u8 *data) -{ - int ret; - - ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); - ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); - return ret; -} - -static int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, u32 port, u8 idx, - u8 myand, u8 myor) -{ - int ret; - u8 tmp; - - ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); - ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); - tmp &= myand; - tmp |= myor; - ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp); - return ret; -} - -static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb, - u32 port, u8 idx, u8 data, u8 mask) -{ - int ret; - u8 tmp; - - ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); - ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); - tmp &= ~(mask); - tmp |= (data & mask); - ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp); - return ret; -} - -static int sisusb_setidxregor(struct sisusb_usb_data *sisusb, u32 port, - u8 index, u8 myor) -{ - return sisusb_setidxregandor(sisusb, port, index, 0xff, myor); -} - -static int sisusb_setidxregand(struct sisusb_usb_data *sisusb, u32 port, - u8 idx, u8 myand) -{ - return sisusb_setidxregandor(sisusb, port, idx, myand, 0x00); -} - -/* Write/read video ram */ - -#ifdef SISUSBENDIANTEST -static void sisusb_testreadwrite(struct sisusb_usb_data *sisusb) -{ - static u8 srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; - char destbuffer[10]; - int i, j; - - sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7); - - for (i = 1; i <= 7; i++) { - dev_dbg(&sisusb->sisusb_dev->dev, - "sisusb: rwtest %d bytes\n", i); - sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i); - for (j = 0; j < i; j++) { - dev_dbg(&sisusb->sisusb_dev->dev, - "rwtest read[%d] = %x\n", - j, destbuffer[j]); - } - } -} -#endif - -/* access pci config registers (reg numbers 0, 4, 8, etc) */ - -static int sisusb_write_pci_config(struct sisusb_usb_data *sisusb, - int regnum, u32 data) -{ - struct sisusb_packet packet; - - packet.header = 0x008f; - packet.address = regnum | 0x10000; - packet.data = data; - return sisusb_send_packet(sisusb, 10, &packet); -} - -static int sisusb_read_pci_config(struct sisusb_usb_data *sisusb, - int regnum, u32 *data) -{ - struct sisusb_packet packet; - int ret; - - packet.header = 0x008f; - packet.address = (u32)regnum | 0x10000; - ret = sisusb_send_packet(sisusb, 6, &packet); - *data = packet.data; - return ret; -} - -/* Clear video RAM */ - -static int sisusb_clear_vram(struct sisusb_usb_data *sisusb, - u32 address, int length) -{ - int ret, i; - ssize_t j; - - if (address < sisusb->vrambase) - return 1; - - if (address >= sisusb->vrambase + sisusb->vramsize) - return 1; - - if (address + length > sisusb->vrambase + sisusb->vramsize) - length = sisusb->vrambase + sisusb->vramsize - address; - - if (length <= 0) - return 0; - - /* allocate free buffer/urb and clear the buffer */ - i = sisusb_alloc_outbuf(sisusb); - if (i < 0) - return -EBUSY; - - memset(sisusb->obuf[i], 0, sisusb->obufsize); - - /* We can write a length > buffer size here. The buffer - * data will simply be re-used (like a ring-buffer). - */ - ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j); - - /* Free the buffer/urb */ - sisusb_free_outbuf(sisusb, i); - - return ret; -} - -/* Initialize the graphics core (return 0 on success) - * This resets the graphics hardware and puts it into - * a defined mode (640x480@60Hz) - */ - -#define GETREG(r, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) -#define SETREG(r, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) -#define SETIREG(r, i, d) sisusb_setidxreg(sisusb, r, i, d) -#define GETIREG(r, i, d) sisusb_getidxreg(sisusb, r, i, d) -#define SETIREGOR(r, i, o) sisusb_setidxregor(sisusb, r, i, o) -#define SETIREGAND(r, i, a) sisusb_setidxregand(sisusb, r, i, a) -#define SETIREGANDOR(r, i, a, o) sisusb_setidxregandor(sisusb, r, i, a, o) -#define READL(a, d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) -#define WRITEL(a, d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) -#define READB(a, d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) -#define WRITEB(a, d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) - -static int sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype) -{ - int ret; - u8 tmp8; - - ret = GETIREG(SISSR, 0x16, &tmp8); - if (ramtype <= 1) { - tmp8 &= 0x3f; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 |= 0x80; - ret |= SETIREG(SISSR, 0x16, tmp8); - } else { - tmp8 |= 0xc0; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 &= 0x0f; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 |= 0x80; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 &= 0x0f; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 |= 0xd0; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 &= 0x0f; - ret |= SETIREG(SISSR, 0x16, tmp8); - tmp8 |= 0xa0; - ret |= SETIREG(SISSR, 0x16, tmp8); - } - return ret; -} - -static int sisusb_getbuswidth(struct sisusb_usb_data *sisusb, - int *bw, int *chab) -{ - int ret; - u8 ramtype, done = 0; - u32 t0, t1, t2, t3; - u32 ramptr = SISUSB_PCI_MEMBASE; - - ret = GETIREG(SISSR, 0x3a, &ramtype); - ramtype &= 3; - - ret |= SETIREG(SISSR, 0x13, 0x00); - - if (ramtype <= 1) { - ret |= SETIREG(SISSR, 0x14, 0x12); - ret |= SETIREGAND(SISSR, 0x15, 0xef); - } else { - ret |= SETIREG(SISSR, 0x14, 0x02); - } - - ret |= sisusb_triggersr16(sisusb, ramtype); - ret |= WRITEL(ramptr + 0, 0x01234567); - ret |= WRITEL(ramptr + 4, 0x456789ab); - ret |= WRITEL(ramptr + 8, 0x89abcdef); - ret |= WRITEL(ramptr + 12, 0xcdef0123); - ret |= WRITEL(ramptr + 16, 0x55555555); - ret |= WRITEL(ramptr + 20, 0x55555555); - ret |= WRITEL(ramptr + 24, 0xffffffff); - ret |= WRITEL(ramptr + 28, 0xffffffff); - ret |= READL(ramptr + 0, &t0); - ret |= READL(ramptr + 4, &t1); - ret |= READL(ramptr + 8, &t2); - ret |= READL(ramptr + 12, &t3); - - if (ramtype <= 1) { - - *chab = 0; *bw = 64; - - if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) { - if ((t1 == 0x456789ab) && (t0 == 0x01234567)) { - *chab = 0; *bw = 64; - ret |= SETIREGAND(SISSR, 0x14, 0xfd); - } - } - if ((t1 != 0x456789ab) || (t0 != 0x01234567)) { - *chab = 1; *bw = 64; - ret |= SETIREGANDOR(SISSR, 0x14, 0xfc, 0x01); - - ret |= sisusb_triggersr16(sisusb, ramtype); - ret |= WRITEL(ramptr + 0, 0x89abcdef); - ret |= WRITEL(ramptr + 4, 0xcdef0123); - ret |= WRITEL(ramptr + 8, 0x55555555); - ret |= WRITEL(ramptr + 12, 0x55555555); - ret |= WRITEL(ramptr + 16, 0xaaaaaaaa); - ret |= WRITEL(ramptr + 20, 0xaaaaaaaa); - ret |= READL(ramptr + 4, &t1); - - if (t1 != 0xcdef0123) { - *bw = 32; - ret |= SETIREGOR(SISSR, 0x15, 0x10); - } - } - - } else { - - *chab = 0; *bw = 64; /* default: cha, bw = 64 */ - - done = 0; - - if (t1 == 0x456789ab) { - if (t0 == 0x01234567) { - *chab = 0; *bw = 64; - done = 1; - } - } else { - if (t0 == 0x01234567) { - *chab = 0; *bw = 32; - ret |= SETIREG(SISSR, 0x14, 0x00); - done = 1; - } - } - - if (!done) { - ret |= SETIREG(SISSR, 0x14, 0x03); - ret |= sisusb_triggersr16(sisusb, ramtype); - - ret |= WRITEL(ramptr + 0, 0x01234567); - ret |= WRITEL(ramptr + 4, 0x456789ab); - ret |= WRITEL(ramptr + 8, 0x89abcdef); - ret |= WRITEL(ramptr + 12, 0xcdef0123); - ret |= WRITEL(ramptr + 16, 0x55555555); - ret |= WRITEL(ramptr + 20, 0x55555555); - ret |= WRITEL(ramptr + 24, 0xffffffff); - ret |= WRITEL(ramptr + 28, 0xffffffff); - ret |= READL(ramptr + 0, &t0); - ret |= READL(ramptr + 4, &t1); - - if (t1 == 0x456789ab) { - if (t0 == 0x01234567) { - *chab = 1; *bw = 64; - return ret; - } /* else error */ - } else { - if (t0 == 0x01234567) { - *chab = 1; *bw = 32; - ret |= SETIREG(SISSR, 0x14, 0x01); - } /* else error */ - } - } - } - return ret; -} - -static int sisusb_verify_mclk(struct sisusb_usb_data *sisusb) -{ - int ret = 0; - u32 ramptr = SISUSB_PCI_MEMBASE; - u8 tmp1, tmp2, i, j; - - ret |= WRITEB(ramptr, 0xaa); - ret |= WRITEB(ramptr + 16, 0x55); - ret |= READB(ramptr, &tmp1); - ret |= READB(ramptr + 16, &tmp2); - if ((tmp1 != 0xaa) || (tmp2 != 0x55)) { - for (i = 0, j = 16; i < 2; i++, j += 16) { - ret |= GETIREG(SISSR, 0x21, &tmp1); - ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb)); - ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */ - ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */ - ret |= SETIREG(SISSR, 0x21, tmp1); - ret |= WRITEB(ramptr + 16 + j, j); - ret |= READB(ramptr + 16 + j, &tmp1); - if (tmp1 == j) { - ret |= WRITEB(ramptr + j, j); - break; - } - } - } - return ret; -} - -static int sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, - int index, u8 rankno, u8 chab, const u8 dramtype[][5], int bw) -{ - int ret = 0, ranksize; - u8 tmp; - - *iret = 0; - - if ((rankno == 2) && (dramtype[index][0] == 2)) - return ret; - - ranksize = dramtype[index][3] / 2 * bw / 32; - - if ((ranksize * rankno) > 128) - return ret; - - tmp = 0; - while ((ranksize >>= 1) > 0) - tmp += 0x10; - - tmp |= ((rankno - 1) << 2); - tmp |= ((bw / 64) & 0x02); - tmp |= (chab & 0x01); - - ret = SETIREG(SISSR, 0x14, tmp); - ret |= sisusb_triggersr16(sisusb, 0); /* sic! */ - - *iret = 1; - - return ret; -} - -static int sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, - u32 inc, int testn) -{ - int ret = 0, i; - u32 j, tmp; - - *iret = 0; - - for (i = 0, j = 0; i < testn; i++) { - ret |= WRITEL(sisusb->vrambase + j, j); - j += inc; - } - - for (i = 0, j = 0; i < testn; i++) { - ret |= READL(sisusb->vrambase + j, &tmp); - if (tmp != j) - return ret; - - j += inc; - } - - *iret = 1; - return ret; -} - -static int sisusb_check_ranks(struct sisusb_usb_data *sisusb, - int *iret, int rankno, int idx, int bw, const u8 rtype[][5]) -{ - int ret = 0, i, i2ret; - u32 inc; - - *iret = 0; - - for (i = rankno; i >= 1; i--) { - inc = 1 << (rtype[idx][2] + rtype[idx][1] + rtype[idx][0] + - bw / 64 + i); - ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); - if (!i2ret) - return ret; - } - - inc = 1 << (rtype[idx][2] + bw / 64 + 2); - ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4); - if (!i2ret) - return ret; - - inc = 1 << (10 + bw / 64); - ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); - if (!i2ret) - return ret; - - *iret = 1; - return ret; -} - -static int sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, - int bw, int chab) -{ - int ret = 0, i2ret = 0, i, j; - static const u8 sdramtype[13][5] = { - { 2, 12, 9, 64, 0x35 }, - { 1, 13, 9, 64, 0x44 }, - { 2, 12, 8, 32, 0x31 }, - { 2, 11, 9, 32, 0x25 }, - { 1, 12, 9, 32, 0x34 }, - { 1, 13, 8, 32, 0x40 }, - { 2, 11, 8, 16, 0x21 }, - { 1, 12, 8, 16, 0x30 }, - { 1, 11, 9, 16, 0x24 }, - { 1, 11, 8, 8, 0x20 }, - { 2, 9, 8, 4, 0x01 }, - { 1, 10, 8, 4, 0x10 }, - { 1, 9, 8, 2, 0x00 } - }; - - *iret = 1; /* error */ - - for (i = 0; i < 13; i++) { - ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]); - for (j = 2; j > 0; j--) { - ret |= sisusb_set_rank(sisusb, &i2ret, i, j, chab, - sdramtype, bw); - if (!i2ret) - continue; - - ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, bw, - sdramtype); - if (i2ret) { - *iret = 0; /* ram size found */ - return ret; - } - } - } - - return ret; -} - -static int sisusb_setup_screen(struct sisusb_usb_data *sisusb, - int clrall, int drwfr) -{ - int ret = 0; - u32 address; - int i, length, modex, modey, bpp; - - modex = 640; modey = 480; bpp = 2; - - address = sisusb->vrambase; /* Clear video ram */ - - if (clrall) - length = sisusb->vramsize; - else - length = modex * bpp * modey; - - ret = sisusb_clear_vram(sisusb, address, length); - - if (!ret && drwfr) { - for (i = 0; i < modex; i++) { - address = sisusb->vrambase + (i * bpp); - ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); - address += (modex * (modey-1) * bpp); - ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); - } - for (i = 0; i < modey; i++) { - address = sisusb->vrambase + ((i * modex) * bpp); - ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); - address += ((modex - 1) * bpp); - ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, - address, 0xf100); - } - } - - return ret; -} - -static void sisusb_set_default_mode(struct sisusb_usb_data *sisusb, - int touchengines) -{ - int i, j, modex, bpp, du; - u8 sr31, cr63, tmp8; - static const char attrdata[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x01, 0x00, 0x00, 0x00 - }; - static const char crtcrdata[] = { - 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3, - 0xff - }; - static const char grcdata[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, - 0xff - }; - static const char crtcdata[] = { - 0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e, - 0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05, - 0x00 - }; - - modex = 640; bpp = 2; - - GETIREG(SISSR, 0x31, &sr31); - GETIREG(SISCR, 0x63, &cr63); - SETIREGOR(SISSR, 0x01, 0x20); - SETIREG(SISCR, 0x63, cr63 & 0xbf); - SETIREGOR(SISCR, 0x17, 0x80); - SETIREGOR(SISSR, 0x1f, 0x04); - SETIREGAND(SISSR, 0x07, 0xfb); - SETIREG(SISSR, 0x00, 0x03); /* seq */ - SETIREG(SISSR, 0x01, 0x21); - SETIREG(SISSR, 0x02, 0x0f); - SETIREG(SISSR, 0x03, 0x00); - SETIREG(SISSR, 0x04, 0x0e); - SETREG(SISMISCW, 0x23); /* misc */ - for (i = 0; i <= 0x18; i++) { /* crtc */ - SETIREG(SISCR, i, crtcrdata[i]); - } - for (i = 0; i <= 0x13; i++) { /* att */ - GETREG(SISINPSTAT, &tmp8); - SETREG(SISAR, i); - SETREG(SISAR, attrdata[i]); - } - GETREG(SISINPSTAT, &tmp8); - SETREG(SISAR, 0x14); - SETREG(SISAR, 0x00); - GETREG(SISINPSTAT, &tmp8); - SETREG(SISAR, 0x20); - GETREG(SISINPSTAT, &tmp8); - for (i = 0; i <= 0x08; i++) { /* grc */ - SETIREG(SISGR, i, grcdata[i]); - } - SETIREGAND(SISGR, 0x05, 0xbf); - for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */ - SETIREG(SISSR, i, 0x00); - } - SETIREGAND(SISSR, 0x37, 0xfe); - SETREG(SISMISCW, 0xef); /* sync */ - SETIREG(SISCR, 0x11, 0x00); /* crtc */ - for (j = 0x00, i = 0; i <= 7; i++, j++) - SETIREG(SISCR, j, crtcdata[i]); - - for (j = 0x10; i <= 10; i++, j++) - SETIREG(SISCR, j, crtcdata[i]); - - for (j = 0x15; i <= 12; i++, j++) - SETIREG(SISCR, j, crtcdata[i]); - - for (j = 0x0A; i <= 15; i++, j++) - SETIREG(SISSR, j, crtcdata[i]); - - SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0)); - SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5)); - SETIREG(SISCR, 0x14, 0x4f); - du = (modex / 16) * (bpp * 2); /* offset/pitch */ - SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f)); - SETIREG(SISCR, 0x13, (du & 0xff)); - du <<= 5; - tmp8 = du >> 8; - SETIREG(SISSR, 0x10, tmp8); - SETIREG(SISSR, 0x31, 0x00); /* VCLK */ - SETIREG(SISSR, 0x2b, 0x1b); - SETIREG(SISSR, 0x2c, 0xe1); - SETIREG(SISSR, 0x2d, 0x01); - SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */ - SETIREG(SISSR, 0x08, 0xae); - SETIREGAND(SISSR, 0x09, 0xf0); - SETIREG(SISSR, 0x08, 0x34); - SETIREGOR(SISSR, 0x3d, 0x01); - SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */ - SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a); - SETIREG(SISCR, 0x19, 0x00); - SETIREGAND(SISCR, 0x1a, 0xfc); - SETIREGAND(SISSR, 0x0f, 0xb7); - SETIREGAND(SISSR, 0x31, 0xfb); - SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0); - SETIREGAND(SISSR, 0x32, 0xf3); - SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03); - SETIREG(SISCR, 0x52, 0x6c); - - SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */ - SETIREG(SISCR, 0x0c, 0x00); - SETIREG(SISSR, 0x0d, 0x00); - SETIREGAND(SISSR, 0x37, 0xfe); - - SETIREG(SISCR, 0x32, 0x20); - SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */ - SETIREG(SISCR, 0x63, (cr63 & 0xbf)); - SETIREG(SISSR, 0x31, (sr31 & 0xfb)); - - if (touchengines) { - SETIREG(SISSR, 0x20, 0xa1); /* enable engines */ - SETIREGOR(SISSR, 0x1e, 0x5a); - - SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */ - SETIREG(SISSR, 0x27, 0x1f); - SETIREG(SISSR, 0x26, 0x00); - } - - SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */ -} - -static int sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) -{ - int ret = 0, i, j, bw, chab, iret, retry = 3; - u8 tmp8, ramtype; - u32 tmp32; - static const char mclktable[] = { - 0x3b, 0x22, 0x01, 143, - 0x3b, 0x22, 0x01, 143, - 0x3b, 0x22, 0x01, 143, - 0x3b, 0x22, 0x01, 143 - }; - static const char eclktable[] = { - 0x3b, 0x22, 0x01, 143, - 0x3b, 0x22, 0x01, 143, - 0x3b, 0x22, 0x01, 143, - 0x3b, 0x22, 0x01, 143 - }; - static const char ramtypetable1[] = { - 0x00, 0x04, 0x60, 0x60, - 0x0f, 0x0f, 0x1f, 0x1f, - 0xba, 0xba, 0xba, 0xba, - 0xa9, 0xa9, 0xac, 0xac, - 0xa0, 0xa0, 0xa0, 0xa8, - 0x00, 0x00, 0x02, 0x02, - 0x30, 0x30, 0x40, 0x40 - }; - static const char ramtypetable2[] = { - 0x77, 0x77, 0x44, 0x44, - 0x77, 0x77, 0x44, 0x44, - 0x00, 0x00, 0x00, 0x00, - 0x5b, 0x5b, 0xab, 0xab, - 0x00, 0x00, 0xf0, 0xf8 - }; - - while (retry--) { - - /* Enable VGA */ - ret = GETREG(SISVGAEN, &tmp8); - ret |= SETREG(SISVGAEN, (tmp8 | 0x01)); - - /* Enable GPU access to VRAM */ - ret |= GETREG(SISMISCR, &tmp8); - ret |= SETREG(SISMISCW, (tmp8 | 0x01)); - - if (ret) - continue; - - /* Reset registers */ - ret |= SETIREGAND(SISCR, 0x5b, 0xdf); - ret |= SETIREG(SISSR, 0x05, 0x86); - ret |= SETIREGOR(SISSR, 0x20, 0x01); - - ret |= SETREG(SISMISCW, 0x67); - - for (i = 0x06; i <= 0x1f; i++) - ret |= SETIREG(SISSR, i, 0x00); - - for (i = 0x21; i <= 0x27; i++) - ret |= SETIREG(SISSR, i, 0x00); - - for (i = 0x31; i <= 0x3d; i++) - ret |= SETIREG(SISSR, i, 0x00); - - for (i = 0x12; i <= 0x1b; i++) - ret |= SETIREG(SISSR, i, 0x00); - - for (i = 0x79; i <= 0x7c; i++) - ret |= SETIREG(SISCR, i, 0x00); - - if (ret) - continue; - - ret |= SETIREG(SISCR, 0x63, 0x80); - - ret |= GETIREG(SISSR, 0x3a, &ramtype); - ramtype &= 0x03; - - ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]); - ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]); - ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]); - - ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]); - ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]); - ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]); - - ret |= SETIREG(SISSR, 0x07, 0x18); - ret |= SETIREG(SISSR, 0x11, 0x0f); - - if (ret) - continue; - - for (i = 0x15, j = 0; i <= 0x1b; i++, j++) { - ret |= SETIREG(SISSR, i, - ramtypetable1[(j*4) + ramtype]); - } - for (i = 0x40, j = 0; i <= 0x44; i++, j++) { - ret |= SETIREG(SISCR, i, - ramtypetable2[(j*4) + ramtype]); - } - - ret |= SETIREG(SISCR, 0x49, 0xaa); - - ret |= SETIREG(SISSR, 0x1f, 0x00); - ret |= SETIREG(SISSR, 0x20, 0xa0); - ret |= SETIREG(SISSR, 0x23, 0xf6); - ret |= SETIREG(SISSR, 0x24, 0x0d); - ret |= SETIREG(SISSR, 0x25, 0x33); - - ret |= SETIREG(SISSR, 0x11, 0x0f); - - ret |= SETIREGOR(SISPART1, 0x2f, 0x01); - - ret |= SETIREGAND(SISCAP, 0x3f, 0xef); - - if (ret) - continue; - - ret |= SETIREG(SISPART1, 0x00, 0x00); - - ret |= GETIREG(SISSR, 0x13, &tmp8); - tmp8 >>= 4; - - ret |= SETIREG(SISPART1, 0x02, 0x00); - ret |= SETIREG(SISPART1, 0x2e, 0x08); - - ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32); - tmp32 &= 0x00f00000; - tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03; - ret |= SETIREG(SISSR, 0x25, tmp8); - tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88; - ret |= SETIREG(SISCR, 0x49, tmp8); - - ret |= SETIREG(SISSR, 0x27, 0x1f); - ret |= SETIREG(SISSR, 0x31, 0x00); - ret |= SETIREG(SISSR, 0x32, 0x11); - ret |= SETIREG(SISSR, 0x33, 0x00); - - if (ret) - continue; - - ret |= SETIREG(SISCR, 0x83, 0x00); - - sisusb_set_default_mode(sisusb, 0); - - ret |= SETIREGAND(SISSR, 0x21, 0xdf); - ret |= SETIREGOR(SISSR, 0x01, 0x20); - ret |= SETIREGOR(SISSR, 0x16, 0x0f); - - ret |= sisusb_triggersr16(sisusb, ramtype); - - /* Disable refresh */ - ret |= SETIREGAND(SISSR, 0x17, 0xf8); - ret |= SETIREGOR(SISSR, 0x19, 0x03); - - ret |= sisusb_getbuswidth(sisusb, &bw, &chab); - ret |= sisusb_verify_mclk(sisusb); - - if (ramtype <= 1) { - ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab); - if (iret) { - dev_err(&sisusb->sisusb_dev->dev, - "RAM size detection failed, assuming 8MB video RAM\n"); - ret |= SETIREG(SISSR, 0x14, 0x31); - /* TODO */ - } - } else { - dev_err(&sisusb->sisusb_dev->dev, - "DDR RAM device found, assuming 8MB video RAM\n"); - ret |= SETIREG(SISSR, 0x14, 0x31); - /* *** TODO *** */ - } - - /* Enable refresh */ - ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]); - ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]); - ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]); - - ret |= SETIREGOR(SISSR, 0x21, 0x20); - - ret |= SETIREG(SISSR, 0x22, 0xfb); - ret |= SETIREG(SISSR, 0x21, 0xa5); - - if (ret == 0) - break; - } - - return ret; -} - -#undef SETREG -#undef GETREG -#undef SETIREG -#undef GETIREG -#undef SETIREGOR -#undef SETIREGAND -#undef SETIREGANDOR -#undef READL -#undef WRITEL - -static void sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) -{ - u8 tmp8, tmp82, ramtype; - int bw = 0; - char *ramtypetext1 = NULL; - static const char ram_datarate[4] = {'S', 'S', 'D', 'D'}; - static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'}; - static const int busSDR[4] = {64, 64, 128, 128}; - static const int busDDR[4] = {32, 32, 64, 64}; - static const int busDDRA[4] = {64+32, 64+32, (64+32)*2, (64+32)*2}; - - sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8); - sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82); - sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype); - sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024; - ramtype &= 0x03; - switch ((tmp8 >> 2) & 0x03) { - case 0: - ramtypetext1 = "1 ch/1 r"; - if (tmp82 & 0x10) - bw = 32; - else - bw = busSDR[(tmp8 & 0x03)]; - - break; - case 1: - ramtypetext1 = "1 ch/2 r"; - sisusb->vramsize <<= 1; - bw = busSDR[(tmp8 & 0x03)]; - break; - case 2: - ramtypetext1 = "asymmetric"; - sisusb->vramsize += sisusb->vramsize/2; - bw = busDDRA[(tmp8 & 0x03)]; - break; - case 3: - ramtypetext1 = "2 channel"; - sisusb->vramsize <<= 1; - bw = busDDR[(tmp8 & 0x03)]; - break; - } - - dev_info(&sisusb->sisusb_dev->dev, - "%dMB %s %cDR S%cRAM, bus width %d\n", - sisusb->vramsize >> 20, ramtypetext1, - ram_datarate[ramtype], ram_dynamictype[ramtype], bw); -} - -static int sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb) -{ - struct sisusb_packet packet; - int ret; - u32 tmp32; - - /* Do some magic */ - packet.header = 0x001f; - packet.address = 0x00000324; - packet.data = 0x00000004; - ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0); - - packet.header = 0x001f; - packet.address = 0x00000364; - packet.data = 0x00000004; - ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); - - packet.header = 0x001f; - packet.address = 0x00000384; - packet.data = 0x00000004; - ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); - - packet.header = 0x001f; - packet.address = 0x00000100; - packet.data = 0x00000700; - ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); - - packet.header = 0x000f; - packet.address = 0x00000004; - ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0); - packet.data |= 0x17; - ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); - - /* Init BAR 0 (VRAM) */ - ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); - ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0); - ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); - tmp32 &= 0x0f; - tmp32 |= SISUSB_PCI_MEMBASE; - ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32); - - /* Init BAR 1 (MMIO) */ - ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); - ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0); - ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); - tmp32 &= 0x0f; - tmp32 |= SISUSB_PCI_MMIOBASE; - ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32); - - /* Init BAR 2 (i/o ports) */ - ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); - ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0); - ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); - tmp32 &= 0x0f; - tmp32 |= SISUSB_PCI_IOPORTBASE; - ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32); - - /* Enable memory and i/o access */ - ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32); - tmp32 |= 0x3; - ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32); - - if (ret == 0) { - /* Some further magic */ - packet.header = 0x001f; - packet.address = 0x00000050; - packet.data = 0x000000ff; - ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); - } - - return ret; -} - -/* Initialize the graphics device (return 0 on success) - * This initializes the net2280 as well as the PCI registers - * of the graphics board. - */ - -static int sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) -{ - int ret = 0, test = 0; - u32 tmp32; - - if (sisusb->devinit == 1) { - /* Read PCI BARs and see if they have been set up */ - ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); - if (ret) - return ret; - - if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) - test++; - - ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); - if (ret) - return ret; - - if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) - test++; - - ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); - if (ret) - return ret; - - if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) - test++; - } - - /* No? So reset the device */ - if ((sisusb->devinit == 0) || (test != 3)) { - - ret |= sisusb_do_init_gfxdevice(sisusb); - - if (ret == 0) - sisusb->devinit = 1; - - } - - if (sisusb->devinit) { - /* Initialize the graphics core */ - if (sisusb_init_gfxcore(sisusb) == 0) { - sisusb->gfxinit = 1; - sisusb_get_ramconfig(sisusb); - sisusb_set_default_mode(sisusb, 1); - ret |= sisusb_setup_screen(sisusb, 1, initscreen); - } - } - - return ret; -} - -/* fops */ - -static int sisusb_open(struct inode *inode, struct file *file) -{ - struct sisusb_usb_data *sisusb; - struct usb_interface *interface; - int subminor = iminor(inode); - - interface = usb_find_interface(&sisusb_driver, subminor); - if (!interface) - return -ENODEV; - - sisusb = usb_get_intfdata(interface); - if (!sisusb) - return -ENODEV; - - mutex_lock(&sisusb->lock); - - if (!sisusb->present || !sisusb->ready) { - mutex_unlock(&sisusb->lock); - return -ENODEV; - } - - if (sisusb->isopen) { - mutex_unlock(&sisusb->lock); - return -EBUSY; - } - - if (!sisusb->devinit) { - if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH || - sisusb->sisusb_dev->speed >= USB_SPEED_SUPER) { - if (sisusb_init_gfxdevice(sisusb, 0)) { - mutex_unlock(&sisusb->lock); - dev_err(&sisusb->sisusb_dev->dev, - "Failed to initialize device\n"); - return -EIO; - } - } else { - mutex_unlock(&sisusb->lock); - dev_err(&sisusb->sisusb_dev->dev, - "Device not attached to USB 2.0 hub\n"); - return -EIO; - } - } - - /* Increment usage count for our sisusb */ - kref_get(&sisusb->kref); - - sisusb->isopen = 1; - - file->private_data = sisusb; - - mutex_unlock(&sisusb->lock); - - return 0; -} - -static void sisusb_delete(struct kref *kref) -{ - struct sisusb_usb_data *sisusb = to_sisusb_dev(kref); - - if (!sisusb) - return; - - usb_put_dev(sisusb->sisusb_dev); - - sisusb->sisusb_dev = NULL; - sisusb_free_buffers(sisusb); - sisusb_free_urbs(sisusb); - kfree(sisusb); -} - -static int sisusb_release(struct inode *inode, struct file *file) -{ - struct sisusb_usb_data *sisusb; - - sisusb = file->private_data; - if (!sisusb) - return -ENODEV; - - mutex_lock(&sisusb->lock); - - if (sisusb->present) { - /* Wait for all URBs to finish if device still present */ - if (!sisusb_wait_all_out_complete(sisusb)) - sisusb_kill_all_busy(sisusb); - } - - sisusb->isopen = 0; - file->private_data = NULL; - - mutex_unlock(&sisusb->lock); - - /* decrement the usage count on our device */ - kref_put(&sisusb->kref, sisusb_delete); - - return 0; -} - -static ssize_t sisusb_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct sisusb_usb_data *sisusb; - ssize_t bytes_read = 0; - int errno = 0; - u8 buf8; - u16 buf16; - u32 buf32, address; - - sisusb = file->private_data; - if (!sisusb) - return -ENODEV; - - mutex_lock(&sisusb->lock); - - /* Sanity check */ - if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { - mutex_unlock(&sisusb->lock); - return -ENODEV; - } - - if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && - (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { - - address = (*ppos) - SISUSB_PCI_PSEUDO_IOPORTBASE + - SISUSB_PCI_IOPORTBASE; - - /* Read i/o ports - * Byte, word and long(32) can be read. As this - * emulates inX instructions, the data returned is - * in machine-endianness. - */ - switch (count) { - case 1: - if (sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, - address, &buf8)) - errno = -EIO; - else if (put_user(buf8, (u8 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 1; - - break; - - case 2: - if (sisusb_read_memio_word(sisusb, SISUSB_TYPE_IO, - address, &buf16)) - errno = -EIO; - else if (put_user(buf16, (u16 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 2; - - break; - - case 4: - if (sisusb_read_memio_long(sisusb, SISUSB_TYPE_IO, - address, &buf32)) - errno = -EIO; - else if (put_user(buf32, (u32 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 4; - - break; - - default: - errno = -EIO; - - } - - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && (*ppos) < - SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { - - address = (*ppos) - SISUSB_PCI_PSEUDO_MEMBASE + - SISUSB_PCI_MEMBASE; - - /* Read video ram - * Remember: Data delivered is never endian-corrected - */ - errno = sisusb_read_mem_bulk(sisusb, address, - NULL, count, buffer, &bytes_read); - - if (bytes_read) - errno = bytes_read; - - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + - SISUSB_PCI_MMIOSIZE) { - - address = (*ppos) - SISUSB_PCI_PSEUDO_MMIOBASE + - SISUSB_PCI_MMIOBASE; - - /* Read MMIO - * Remember: Data delivered is never endian-corrected - */ - errno = sisusb_read_mem_bulk(sisusb, address, - NULL, count, buffer, &bytes_read); - - if (bytes_read) - errno = bytes_read; - - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && - (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) { - - if (count != 4) { - mutex_unlock(&sisusb->lock); - return -EINVAL; - } - - address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE; - - /* Read PCI config register - * Return value delivered in machine endianness. - */ - if (sisusb_read_pci_config(sisusb, address, &buf32)) - errno = -EIO; - else if (put_user(buf32, (u32 __user *)buffer)) - errno = -EFAULT; - else - bytes_read = 4; - - } else { - - errno = -EBADFD; - - } - - (*ppos) += bytes_read; - - mutex_unlock(&sisusb->lock); - - return errno ? errno : bytes_read; -} - -static ssize_t sisusb_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - struct sisusb_usb_data *sisusb; - int errno = 0; - ssize_t bytes_written = 0; - u8 buf8; - u16 buf16; - u32 buf32, address; - - sisusb = file->private_data; - if (!sisusb) - return -ENODEV; - - mutex_lock(&sisusb->lock); - - /* Sanity check */ - if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { - mutex_unlock(&sisusb->lock); - return -ENODEV; - } - - if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && - (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { - - address = (*ppos) - SISUSB_PCI_PSEUDO_IOPORTBASE + - SISUSB_PCI_IOPORTBASE; - - /* Write i/o ports - * Byte, word and long(32) can be written. As this - * emulates outX instructions, the data is expected - * in machine-endianness. - */ - switch (count) { - case 1: - if (get_user(buf8, (u8 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_memio_byte(sisusb, - SISUSB_TYPE_IO, address, buf8)) - errno = -EIO; - else - bytes_written = 1; - - break; - - case 2: - if (get_user(buf16, (u16 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_memio_word(sisusb, - SISUSB_TYPE_IO, address, buf16)) - errno = -EIO; - else - bytes_written = 2; - - break; - - case 4: - if (get_user(buf32, (u32 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_memio_long(sisusb, - SISUSB_TYPE_IO, address, buf32)) - errno = -EIO; - else - bytes_written = 4; - - break; - - default: - errno = -EIO; - } - - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + - sisusb->vramsize) { - - address = (*ppos) - SISUSB_PCI_PSEUDO_MEMBASE + - SISUSB_PCI_MEMBASE; - - /* Write video ram. - * Buffer is copied 1:1, therefore, on big-endian - * machines, the data must be swapped by userland - * in advance (if applicable; no swapping in 8bpp - * mode or if YUV data is being transferred). - */ - errno = sisusb_write_mem_bulk(sisusb, address, NULL, - count, buffer, 0, &bytes_written); - - if (bytes_written) - errno = bytes_written; - - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && - (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + - SISUSB_PCI_MMIOSIZE) { - - address = (*ppos) - SISUSB_PCI_PSEUDO_MMIOBASE + - SISUSB_PCI_MMIOBASE; - - /* Write MMIO. - * Buffer is copied 1:1, therefore, on big-endian - * machines, the data must be swapped by userland - * in advance. - */ - errno = sisusb_write_mem_bulk(sisusb, address, NULL, - count, buffer, 0, &bytes_written); - - if (bytes_written) - errno = bytes_written; - - } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && - (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + - SISUSB_PCI_PCONFSIZE) { - - if (count != 4) { - mutex_unlock(&sisusb->lock); - return -EINVAL; - } - - address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE; - - /* Write PCI config register. - * Given value expected in machine endianness. - */ - if (get_user(buf32, (u32 __user *)buffer)) - errno = -EFAULT; - else if (sisusb_write_pci_config(sisusb, address, buf32)) - errno = -EIO; - else - bytes_written = 4; - - - } else { - - /* Error */ - errno = -EBADFD; - - } - - (*ppos) += bytes_written; - - mutex_unlock(&sisusb->lock); - - return errno ? errno : bytes_written; -} - -static loff_t sisusb_lseek(struct file *file, loff_t offset, int orig) -{ - struct sisusb_usb_data *sisusb; - loff_t ret; - - sisusb = file->private_data; - if (!sisusb) - return -ENODEV; - - mutex_lock(&sisusb->lock); - - /* Sanity check */ - if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { - mutex_unlock(&sisusb->lock); - return -ENODEV; - } - - ret = no_seek_end_llseek(file, offset, orig); - - mutex_unlock(&sisusb->lock); - return ret; -} - -static int sisusb_handle_command(struct sisusb_usb_data *sisusb, - struct sisusb_command *y, unsigned long arg) -{ - int retval, length; - u32 port, address; - - /* All our commands require the device - * to be initialized. - */ - if (!sisusb->devinit) - return -ENODEV; - - port = y->data3 - - SISUSB_PCI_PSEUDO_IOPORTBASE + - SISUSB_PCI_IOPORTBASE; - - switch (y->operation) { - case SUCMD_GET: - retval = sisusb_getidxreg(sisusb, port, y->data0, &y->data1); - if (!retval) { - if (copy_to_user((void __user *)arg, y, sizeof(*y))) - retval = -EFAULT; - } - break; - - case SUCMD_SET: - retval = sisusb_setidxreg(sisusb, port, y->data0, y->data1); - break; - - case SUCMD_SETOR: - retval = sisusb_setidxregor(sisusb, port, y->data0, y->data1); - break; - - case SUCMD_SETAND: - retval = sisusb_setidxregand(sisusb, port, y->data0, y->data1); - break; - - case SUCMD_SETANDOR: - retval = sisusb_setidxregandor(sisusb, port, y->data0, - y->data1, y->data2); - break; - - case SUCMD_SETMASK: - retval = sisusb_setidxregmask(sisusb, port, y->data0, - y->data1, y->data2); - break; - - case SUCMD_CLRSCR: - /* Gfx core must be initialized */ - if (!sisusb->gfxinit) - return -ENODEV; - - length = (y->data0 << 16) | (y->data1 << 8) | y->data2; - address = y->data3 - SISUSB_PCI_PSEUDO_MEMBASE + - SISUSB_PCI_MEMBASE; - retval = sisusb_clear_vram(sisusb, address, length); - break; - - case SUCMD_HANDLETEXTMODE: - retval = 0; - break; - - default: - retval = -EINVAL; - } - - if (retval > 0) - retval = -EIO; - - return retval; -} - -static long sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct sisusb_usb_data *sisusb; - struct sisusb_info x; - struct sisusb_command y; - long retval = 0; - u32 __user *argp = (u32 __user *)arg; - - sisusb = file->private_data; - if (!sisusb) - return -ENODEV; - - mutex_lock(&sisusb->lock); - - /* Sanity check */ - if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { - retval = -ENODEV; - goto err_out; - } - - switch (cmd) { - case SISUSB_GET_CONFIG_SIZE: - - if (put_user(sizeof(x), argp)) - retval = -EFAULT; - - break; - - case SISUSB_GET_CONFIG: - - x.sisusb_id = SISUSB_ID; - x.sisusb_version = SISUSB_VERSION; - x.sisusb_revision = SISUSB_REVISION; - x.sisusb_patchlevel = SISUSB_PATCHLEVEL; - x.sisusb_gfxinit = sisusb->gfxinit; - x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE; - x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE; - x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE; - x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE; - x.sisusb_vramsize = sisusb->vramsize; - x.sisusb_minor = sisusb->minor; - x.sisusb_fbdevactive = 0; - x.sisusb_conactive = 0; - memset(x.sisusb_reserved, 0, sizeof(x.sisusb_reserved)); - - if (copy_to_user((void __user *)arg, &x, sizeof(x))) - retval = -EFAULT; - - break; - - case SISUSB_COMMAND: - - if (copy_from_user(&y, (void __user *)arg, sizeof(y))) - retval = -EFAULT; - else - retval = sisusb_handle_command(sisusb, &y, arg); - - break; - - default: - retval = -ENOTTY; - break; - } - -err_out: - mutex_unlock(&sisusb->lock); - return retval; -} - -#ifdef CONFIG_COMPAT -static long sisusb_compat_ioctl(struct file *f, unsigned int cmd, - unsigned long arg) -{ - switch (cmd) { - case SISUSB_GET_CONFIG_SIZE: - case SISUSB_GET_CONFIG: - case SISUSB_COMMAND: - return sisusb_ioctl(f, cmd, arg); - - default: - return -ENOIOCTLCMD; - } -} -#endif - -static const struct file_operations usb_sisusb_fops = { - .owner = THIS_MODULE, - .open = sisusb_open, - .release = sisusb_release, - .read = sisusb_read, - .write = sisusb_write, - .llseek = sisusb_lseek, -#ifdef CONFIG_COMPAT - .compat_ioctl = sisusb_compat_ioctl, -#endif - .unlocked_ioctl = sisusb_ioctl -}; - -static struct usb_class_driver usb_sisusb_class = { - .name = "sisusbvga%d", - .fops = &usb_sisusb_fops, - .minor_base = SISUSB_MINOR -}; - -static int sisusb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct sisusb_usb_data *sisusb; - int retval = 0, i; - - dev_info(&dev->dev, "USB2VGA dongle found at address %d\n", - dev->devnum); - - /* Allocate memory for our private */ - sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL); - if (!sisusb) - return -ENOMEM; - - kref_init(&sisusb->kref); - - mutex_init(&(sisusb->lock)); - - sisusb->sisusb_dev = dev; - sisusb->vrambase = SISUSB_PCI_MEMBASE; - sisusb->mmiobase = SISUSB_PCI_MMIOBASE; - sisusb->mmiosize = SISUSB_PCI_MMIOSIZE; - sisusb->ioportbase = SISUSB_PCI_IOPORTBASE; - /* Everything else is zero */ - - /* Register device */ - retval = usb_register_dev(intf, &usb_sisusb_class); - if (retval) { - dev_err(&sisusb->sisusb_dev->dev, - "Failed to get a minor for device %d\n", - dev->devnum); - retval = -ENODEV; - goto error_1; - } - - sisusb->minor = intf->minor; - - /* Allocate buffers */ - sisusb->ibufsize = SISUSB_IBUF_SIZE; - sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL); - if (!sisusb->ibuf) { - retval = -ENOMEM; - goto error_2; - } - - sisusb->numobufs = 0; - sisusb->obufsize = SISUSB_OBUF_SIZE; - for (i = 0; i < NUMOBUFS; i++) { - sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL); - if (!sisusb->obuf[i]) { - if (i == 0) { - retval = -ENOMEM; - goto error_3; - } - break; - } - sisusb->numobufs++; - } - - /* Allocate URBs */ - sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL); - if (!sisusb->sisurbin) { - retval = -ENOMEM; - goto error_3; - } - sisusb->completein = 1; - - for (i = 0; i < sisusb->numobufs; i++) { - sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL); - if (!sisusb->sisurbout[i]) { - retval = -ENOMEM; - goto error_4; - } - sisusb->urbout_context[i].sisusb = (void *)sisusb; - sisusb->urbout_context[i].urbindex = i; - sisusb->urbstatus[i] = 0; - } - - dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", - sisusb->numobufs); - - /* Do remaining init stuff */ - - init_waitqueue_head(&sisusb->wait_q); - - usb_set_intfdata(intf, sisusb); - - usb_get_dev(sisusb->sisusb_dev); - - sisusb->present = 1; - - if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) { - int initscreen = 1; - if (sisusb_init_gfxdevice(sisusb, initscreen)) - dev_err(&sisusb->sisusb_dev->dev, - "Failed to early initialize device\n"); - - } else - dev_info(&sisusb->sisusb_dev->dev, - "Not attached to USB 2.0 hub, deferring init\n"); - - sisusb->ready = 1; - -#ifdef SISUSBENDIANTEST - dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n"); - sisusb_testreadwrite(sisusb); - dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n"); -#endif - - return 0; - -error_4: - sisusb_free_urbs(sisusb); -error_3: - sisusb_free_buffers(sisusb); -error_2: - usb_deregister_dev(intf, &usb_sisusb_class); -error_1: - kfree(sisusb); - return retval; -} - -static void sisusb_disconnect(struct usb_interface *intf) -{ - struct sisusb_usb_data *sisusb; - - /* This should *not* happen */ - sisusb = usb_get_intfdata(intf); - if (!sisusb) - return; - - usb_deregister_dev(intf, &usb_sisusb_class); - - mutex_lock(&sisusb->lock); - - /* Wait for all URBs to complete and kill them in case (MUST do) */ - if (!sisusb_wait_all_out_complete(sisusb)) - sisusb_kill_all_busy(sisusb); - - usb_set_intfdata(intf, NULL); - - sisusb->present = 0; - sisusb->ready = 0; - - mutex_unlock(&sisusb->lock); - - /* decrement our usage count */ - kref_put(&sisusb->kref, sisusb_delete); -} - -static const struct usb_device_id sisusb_table[] = { - { USB_DEVICE(0x0711, 0x0550) }, - { USB_DEVICE(0x0711, 0x0900) }, - { USB_DEVICE(0x0711, 0x0901) }, - { USB_DEVICE(0x0711, 0x0902) }, - { USB_DEVICE(0x0711, 0x0903) }, - { USB_DEVICE(0x0711, 0x0918) }, - { USB_DEVICE(0x0711, 0x0920) }, - { USB_DEVICE(0x0711, 0x0950) }, - { USB_DEVICE(0x0711, 0x5200) }, - { USB_DEVICE(0x182d, 0x021c) }, - { USB_DEVICE(0x182d, 0x0269) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, sisusb_table); - -static struct usb_driver sisusb_driver = { - .name = "sisusb", - .probe = sisusb_probe, - .disconnect = sisusb_disconnect, - .id_table = sisusb_table, -}; - -static int __init usb_sisusb_init(void) -{ - return usb_register(&sisusb_driver); -} - -static void __exit usb_sisusb_exit(void) -{ - usb_deregister(&sisusb_driver); -} - -module_init(usb_sisusb_init); -module_exit(usb_sisusb_exit); - -MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>"); -MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles"); -MODULE_LICENSE("GPL"); - |