diff options
Diffstat (limited to 'drivers')
38 files changed, 3189 insertions, 3409 deletions
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 0f4203b..6055b9c 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -307,7 +307,9 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hu) { struct hci_dev *hdev = hu->hdev; - hci_uart_close(hdev); + + if (hdev) + hci_uart_close(hdev); if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { hu->proto->close(hu); @@ -473,12 +475,18 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, tty->low_latency = 1; } else return -EBUSY; + break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return hu->proto->id; return -EUNATCH; + case HCIUARTGETDEVICE: + if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return hu->hdev->id; + return -EUNATCH; + default: err = n_tty_ioctl(tty, file, cmd, arg); break; diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index b250e67..1097ce7 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -28,8 +28,9 @@ #endif /* Ioctls */ -#define HCIUARTSETPROTO _IOW('U', 200, int) -#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTSETPROTO _IOW('U', 200, int) +#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 4 diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 6ac3ca4..b3d4ccc 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1544,21 +1544,18 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol } struct tty_ldisc tty_ldisc_N_TTY = { - TTY_LDISC_MAGIC, /* magic */ - "n_tty", /* name */ - 0, /* num */ - 0, /* flags */ - n_tty_open, /* open */ - n_tty_close, /* close */ - n_tty_flush_buffer, /* flush_buffer */ - n_tty_chars_in_buffer, /* chars_in_buffer */ - read_chan, /* read */ - write_chan, /* write */ - n_tty_ioctl, /* ioctl */ - n_tty_set_termios, /* set_termios */ - normal_poll, /* poll */ - NULL, /* hangup */ - n_tty_receive_buf, /* receive_buf */ - n_tty_write_wakeup /* write_wakeup */ + .magic = TTY_LDISC_MAGIC, + .name = "n_tty", + .open = n_tty_open, + .close = n_tty_close, + .flush_buffer = n_tty_flush_buffer, + .chars_in_buffer = n_tty_chars_in_buffer, + .read = read_chan, + .write = write_chan, + .ioctl = n_tty_ioctl, + .set_termios = n_tty_set_termios, + .poll = normal_poll, + .receive_buf = n_tty_receive_buf, + .write_wakeup = n_tty_write_wakeup }; diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 245f031..8cc60b6 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -402,7 +402,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Host number %Zd, name ``%s''\n", HostP - p->RIOHosts, HostP->Name); rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: Rup number 0x%x\n", rup); - if (Rup >= (unsigned short) MAX_RUP) { + if (Rup < (unsigned short) MAX_RUP) { rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for RTA ``%s''\n", HostP->Mapping[Rup].Name); } else rio_dprintk(RIO_DEBUG_CMD, "CONTROL information: This is the RUP for link ``%c'' of host ``%s''\n", ('A' + Rup - MAX_RUP), HostP->Name); diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 61a63da..a3fd7e7 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1014,9 +1014,6 @@ static int rp_open(struct tty_struct *tty, struct file *filp) /* * Info->count is now 1; so it's safe to sleep now. */ - info->session = process_session(current); - info->pgrp = process_group(current); - if ((info->flags & ROCKET_INITIALIZED) == 0) { cp = &info->channel; sSetRxTrigger(cp, TRIG_1); diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 89b4d7b..b4c53df 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -1158,8 +1158,6 @@ struct r_port { int xmit_head; int xmit_tail; int xmit_cnt; - int session; - int pgrp; int cd_status; int ignore_status_mask; int read_status_mask; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 2a7736b..02b49bc 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1171,6 +1171,112 @@ static int ioctl(struct tty_struct *tty, struct file *file, } /* + * support for 32 bit ioctl calls on 64 bit systems + */ +#ifdef CONFIG_COMPAT +static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s get_params32\n", info->device_name)); + tmp_params.mode = (compat_ulong_t)info->params.mode; + tmp_params.loopback = info->params.loopback; + tmp_params.flags = info->params.flags; + tmp_params.encoding = info->params.encoding; + tmp_params.clock_speed = (compat_ulong_t)info->params.clock_speed; + tmp_params.addr_filter = info->params.addr_filter; + tmp_params.crc_type = info->params.crc_type; + tmp_params.preamble_length = info->params.preamble_length; + tmp_params.preamble = info->params.preamble; + tmp_params.data_rate = (compat_ulong_t)info->params.data_rate; + tmp_params.data_bits = info->params.data_bits; + tmp_params.stop_bits = info->params.stop_bits; + tmp_params.parity = info->params.parity; + if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + return 0; +} + +static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params) +{ + struct MGSL_PARAMS32 tmp_params; + + DBGINFO(("%s set_params32\n", info->device_name)); + if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32))) + return -EFAULT; + + spin_lock(&info->lock); + info->params.mode = tmp_params.mode; + info->params.loopback = tmp_params.loopback; + info->params.flags = tmp_params.flags; + info->params.encoding = tmp_params.encoding; + info->params.clock_speed = tmp_params.clock_speed; + info->params.addr_filter = tmp_params.addr_filter; + info->params.crc_type = tmp_params.crc_type; + info->params.preamble_length = tmp_params.preamble_length; + info->params.preamble = tmp_params.preamble; + info->params.data_rate = tmp_params.data_rate; + info->params.data_bits = tmp_params.data_bits; + info->params.stop_bits = tmp_params.stop_bits; + info->params.parity = tmp_params.parity; + spin_unlock(&info->lock); + + change_params(info); + + return 0; +} + +static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct slgt_info *info = tty->driver_data; + int rc = -ENOIOCTLCMD; + + if (sanity_check(info, tty->name, "compat_ioctl")) + return -ENODEV; + DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd)); + + switch (cmd) { + + case MGSL_IOCSPARAMS32: + rc = set_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS32: + rc = get_params32(info, compat_ptr(arg)); + break; + + case MGSL_IOCGPARAMS: + case MGSL_IOCSPARAMS: + case MGSL_IOCGTXIDLE: + case MGSL_IOCGSTATS: + case MGSL_IOCWAITEVENT: + case MGSL_IOCGIF: + case MGSL_IOCSGPIO: + case MGSL_IOCGGPIO: + case MGSL_IOCWAITGPIO: + case TIOCGICOUNT: + rc = ioctl(tty, file, cmd, (unsigned long)(compat_ptr(arg))); + break; + + case MGSL_IOCSTXIDLE: + case MGSL_IOCTXENABLE: + case MGSL_IOCRXENABLE: + case MGSL_IOCTXABORT: + case TIOCMIWAIT: + case MGSL_IOCSIF: + rc = ioctl(tty, file, cmd, arg); + break; + } + + DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc)); + return rc; +} +#else +#define slgt_compat_ioctl NULL +#endif /* ifdef CONFIG_COMPAT */ + +/* * proc fs support */ static inline int line_info(char *buf, struct slgt_info *info) @@ -3446,6 +3552,7 @@ static const struct tty_operations ops = { .chars_in_buffer = chars_in_buffer, .flush_buffer = flush_buffer, .ioctl = ioctl, + .compat_ioctl = slgt_compat_ioctl, .throttle = throttle, .unthrottle = unthrottle, .send_xchar = send_xchar, diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index fc662e4..fe62c21 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -151,6 +151,12 @@ static int tty_open(struct inode *, struct file *); static int tty_release(struct inode *, struct file *); int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +static long tty_compat_ioctl(struct file * file, unsigned int cmd, + unsigned long arg); +#else +#define tty_compat_ioctl NULL +#endif static int tty_fasync(int fd, struct file * filp, int on); static void release_tty(struct tty_struct *tty, int idx); static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); @@ -1143,8 +1149,8 @@ static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait) return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM; } -static int hung_up_tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static long hung_up_tty_ioctl(struct file * file, + unsigned int cmd, unsigned long arg) { return cmd == TIOCSPGRP ? -ENOTTY : -EIO; } @@ -1155,6 +1161,7 @@ static const struct file_operations tty_fops = { .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, @@ -1167,6 +1174,7 @@ static const struct file_operations ptmx_fops = { .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = ptmx_open, .release = tty_release, .fasync = tty_fasync, @@ -1179,6 +1187,7 @@ static const struct file_operations console_fops = { .write = redirected_tty_write, .poll = tty_poll, .ioctl = tty_ioctl, + .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, @@ -1189,7 +1198,8 @@ static const struct file_operations hung_up_tty_fops = { .read = hung_up_tty_read, .write = hung_up_tty_write, .poll = hung_up_tty_poll, - .ioctl = hung_up_tty_ioctl, + .unlocked_ioctl = hung_up_tty_ioctl, + .compat_ioctl = hung_up_tty_ioctl, .release = tty_release, }; @@ -3357,6 +3367,32 @@ int tty_ioctl(struct inode * inode, struct file * file, return retval; } +#ifdef CONFIG_COMPAT +static long tty_compat_ioctl(struct file * file, unsigned int cmd, + unsigned long arg) +{ + struct inode *inode = file->f_dentry->d_inode; + struct tty_struct *tty = file->private_data; + struct tty_ldisc *ld; + int retval = -ENOIOCTLCMD; + + if (tty_paranoia_check(tty, inode, "tty_ioctl")) + return -EINVAL; + + if (tty->driver->compat_ioctl) { + retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg); + if (retval != -ENOIOCTLCMD) + return retval; + } + + ld = tty_ldisc_ref_wait(tty); + if (ld->compat_ioctl) + retval = ld->compat_ioctl(tty, file, cmd, arg); + tty_ldisc_deref(ld); + + return retval; +} +#endif /* * This implements the "Secure Attention Key" --- the idea is to @@ -3689,6 +3725,7 @@ void tty_set_operations(struct tty_driver *driver, driver->write_room = op->write_room; driver->chars_in_buffer = op->chars_in_buffer; driver->ioctl = op->ioctl; + driver->compat_ioctl = op->compat_ioctl; driver->set_termios = op->set_termios; driver->throttle = op->throttle; driver->unthrottle = op->unthrottle; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a19b65e..7f81789 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -240,11 +240,94 @@ static inline void hidinput_pb_setup(struct input_dev *input) } #endif +static inline int match_scancode(int code, int scancode) +{ + if (scancode == 0) + return 1; + return ((code & (HID_USAGE_PAGE | HID_USAGE)) == scancode); +} + +static inline int match_keycode(int code, int keycode) +{ + if (keycode == 0) + return 1; + return (code == keycode); +} + +static struct hid_usage *hidinput_find_key(struct hid_device *hid, + int scancode, int keycode) +{ + int i, j, k; + struct hid_report *report; + struct hid_usage *usage; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + for ( j = 0; j < report->field[i]->maxusage; j++) { + usage = report->field[i]->usage + j; + if (usage->type == EV_KEY && + match_scancode(usage->hid, scancode) && + match_keycode(usage->code, keycode)) + return usage; + } + } + } + } + return NULL; +} + +static int hidinput_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct hid_device *hid = dev->private; + struct hid_usage *usage; + + usage = hidinput_find_key(hid, scancode, 0); + if (usage) { + *keycode = usage->code; + return 0; + } + return -EINVAL; +} + +static int hidinput_setkeycode(struct input_dev *dev, int scancode, + int keycode) +{ + struct hid_device *hid = dev->private; + struct hid_usage *usage; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + usage = hidinput_find_key(hid, scancode, 0); + if (usage) { + old_keycode = usage->code; + usage->code = keycode; + + clear_bit(old_keycode, dev->keybit); + set_bit(usage->code, dev->keybit); +#ifdef CONFIG_HID_DEBUG + printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); +#endif + /* Set the keybit for the old keycode if the old keycode is used + * by another key */ + if (hidinput_find_key (hid, 0, old_keycode)) + set_bit(old_keycode, dev->keybit); + + return 0; + } + + return -EINVAL; +} + + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { struct input_dev *input = hidinput->input; - struct hid_device *device = input->private; + struct hid_device *device = input_get_drvdata(input); int max = 0, code; unsigned long *bit = NULL; @@ -553,6 +636,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1015: map_key_clear(KEY_RECORD); break; case 0x1016: map_key_clear(KEY_PLAYER); break; case 0x1017: map_key_clear(KEY_EJECTCD); break; + case 0x1018: map_key_clear(KEY_MEDIA); break; case 0x1019: map_key_clear(KEY_PROG1); break; case 0x101a: map_key_clear(KEY_PROG2); break; case 0x101b: map_key_clear(KEY_PROG3); break; @@ -560,9 +644,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x1020: map_key_clear(KEY_ZOOMOUT); break; case 0x1021: map_key_clear(KEY_ZOOMRESET); break; case 0x1023: map_key_clear(KEY_CLOSE); break; + case 0x1027: map_key_clear(KEY_MENU); break; /* this one is marked as 'Rotate' */ case 0x1028: map_key_clear(KEY_ANGLE); break; case 0x1029: map_key_clear(KEY_SHUFFLE); break; + case 0x102a: map_key_clear(KEY_BACK); break; + case 0x102b: map_key_clear(KEY_CYCLEWINDOWS); break; case 0x1041: map_key_clear(KEY_BATTERY); break; case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; case 0x1043: map_key_clear(KEY_SPREADSHEET); break; @@ -855,13 +942,15 @@ EXPORT_SYMBOL_GPL(hidinput_find_field); static int hidinput_open(struct input_dev *dev) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); + return hid->hid_open(hid); } static void hidinput_close(struct input_dev *dev) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); + hid->hid_close(hid); } @@ -909,10 +998,12 @@ int hidinput_connect(struct hid_device *hid) return -1; } - input_dev->private = hid; + input_set_drvdata(input_dev, hid); input_dev->event = hid->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; input_dev->name = hid->name; input_dev->phys = hid->phys; @@ -921,7 +1012,7 @@ int hidinput_connect(struct hid_device *hid) input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; - input_dev->cdev.dev = hid->dev; + input_dev->dev.parent = hid->dev; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); } diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 7c87bdc..1b4b572 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -25,12 +25,12 @@ comment "Input core support is needed for USB HID input layer or HIDBP support" depends on USB_HID && INPUT=n config USB_HIDINPUT_POWERBOOK - bool "Enable support for iBook/PowerBook special keys" + bool "Enable support for iBook/PowerBook/MacBook/MacBookPro special keys" default n depends on USB_HID help Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks and PowerBooks. + Apple iBooks, PowerBooks, MacBooks and MacBook Pros. If unsure, say N. diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 91d6103..d91b9da 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -446,7 +446,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct hid_field *field; int offset; @@ -626,14 +626,10 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (usbhid->inbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); - if (usbhid->outbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); - if (usbhid->cr) - usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); - if (usbhid->ctrlbuf) - usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma); + usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma); + usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } /* @@ -692,6 +688,30 @@ static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) } } +/* + * Some USB barcode readers from cypress have usage min and usage max in + * the wrong order + */ +static void hid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) +{ + short fixed = 0; + int i; + + for (i = 0; i < rsize - 4; i++) { + if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { + unsigned char tmp; + + rdesc[i] = 0x19; rdesc[i+2] = 0x29; + tmp = rdesc[i+3]; + rdesc[i+3] = rdesc[i+1]; + rdesc[i+1] = tmp; + } + } + + if (fixed) + info("Fixing up Cypress report descriptor"); +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -758,6 +778,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR) hid_fixup_logitech_descriptor(rdesc, rsize); + if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) + hid_fixup_cypress_descriptor(rdesc, rsize); + #ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c index 92d2553..c5cd410 100644 --- a/drivers/hid/usbhid/hid-lgff.c +++ b/drivers/hid/usbhid/hid-lgff.c @@ -60,7 +60,7 @@ static const struct dev_type devices[] = { static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct hid_report *report = list_entry(report_list->next, struct hid_report, list); int x, y; diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/usbhid/hid-plff.c index 76d2e6e..d6a8f2b 100644 --- a/drivers/hid/usbhid/hid-plff.c +++ b/drivers/hid/usbhid/hid-plff.c @@ -37,7 +37,7 @@ struct plff_device { static int hid_plff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct plff_device *plff = data; int left, right; diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 17a8755..f6c4145 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -92,6 +92,8 @@ #define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 #define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 #define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 +#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 +#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 #define USB_VENDOR_ID_DELL 0x413c #define USB_DEVICE_ID_DELL_W7658 0x2005 @@ -193,6 +195,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 +#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 @@ -422,6 +425,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, @@ -445,6 +449,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_SWAPPED_MIN_MAX }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_SWAPPED_MIN_MAX }, + { 0, 0 } }; diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c index ab67331..ab5ba6e 100644 --- a/drivers/hid/usbhid/hid-tmff.c +++ b/drivers/hid/usbhid/hid-tmff.c @@ -59,7 +59,7 @@ static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct tmff_device *tmff = data; int left, right; /* Rumbling */ diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c index 7bd8238..a7fbffc 100644 --- a/drivers/hid/usbhid/hid-zpff.c +++ b/drivers/hid/usbhid/hid-zpff.c @@ -37,7 +37,7 @@ struct zpff_device { static int hid_zpff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct zpff_device *zpff = data; int left, right; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index a8b3d66..488d61b 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -51,6 +51,7 @@ struct hiddev { wait_queue_head_t wait; struct hid_device *hid; struct list_head list; + spinlock_t list_lock; }; struct hiddev_list { @@ -161,7 +162,9 @@ static void hiddev_send_event(struct hid_device *hid, { struct hiddev *hiddev = hid->hiddev; struct hiddev_list *list; + unsigned long flags; + spin_lock_irqsave(&hiddev->list_lock, flags); list_for_each_entry(list, &hiddev->list, node) { if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { @@ -171,6 +174,7 @@ static void hiddev_send_event(struct hid_device *hid, kill_fasync(&list->fasync, SIGIO, POLL_IN); } } + spin_unlock_irqrestore(&hiddev->list_lock, flags); wake_up_interruptible(&hiddev->wait); } @@ -235,9 +239,13 @@ static int hiddev_fasync(int fd, struct file *file, int on) static int hiddev_release(struct inode * inode, struct file * file) { struct hiddev_list *list = file->private_data; + unsigned long flags; hiddev_fasync(-1, file, 0); + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); if (!--list->hiddev->open) { if (list->hiddev->exist) @@ -257,6 +265,7 @@ static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; + unsigned long flags; int i = iminor(inode) - HIDDEV_MINOR_BASE; @@ -267,7 +276,11 @@ static int hiddev_open(struct inode *inode, struct file *file) return -ENOMEM; list->hiddev = hiddev_table[i]; + + spin_lock_irqsave(&list->hiddev->list_lock, flags); list_add_tail(&list->node, &hiddev_table[i]->list); + spin_unlock_irqrestore(&list->hiddev->list_lock, flags); + file->private_data = list; if (!list->hiddev->open++) @@ -773,6 +786,7 @@ int hiddev_connect(struct hid_device *hid) init_waitqueue_head(&hiddev->wait); INIT_LIST_HEAD(&hiddev->list); + spin_lock_init(&hiddev->list_lock); hiddev->hid = hid; hiddev->exist = 1; diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 65aa12e8..1309787 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -133,12 +133,11 @@ resubmit: static int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); if (type != EV_LED) return -1; - kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | (!!test_bit(LED_NUML, dev->led)); @@ -175,7 +174,7 @@ static void usb_kbd_led(struct urb *urb) static int usb_kbd_open(struct input_dev *dev) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); kbd->irq->dev = kbd->usbdev; if (usb_submit_urb(kbd->irq, GFP_KERNEL)) @@ -186,7 +185,7 @@ static int usb_kbd_open(struct input_dev *dev) static void usb_kbd_close(struct input_dev *dev) { - struct usb_kbd *kbd = dev->private; + struct usb_kbd *kbd = input_get_drvdata(dev); usb_kill_urb(kbd->irq); } @@ -211,12 +210,9 @@ static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd) { usb_free_urb(kbd->irq); usb_free_urb(kbd->led); - if (kbd->new) - usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); - if (kbd->cr) - usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); - if (kbd->leds) - usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); + usb_buffer_free(dev, 8, kbd->new, kbd->new_dma); + usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma); + usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma); } static int usb_kbd_probe(struct usb_interface *iface, @@ -274,8 +270,9 @@ static int usb_kbd_probe(struct usb_interface *iface, input_dev->name = kbd->name; input_dev->phys = kbd->phys; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &iface->dev; - input_dev->private = kbd; + input_dev->dev.parent = &iface->dev; + + input_set_drvdata(input_dev, kbd); input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index 573776d..5345c73 100644 --- a/drivers/hid/usbhid/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -96,7 +96,7 @@ resubmit: static int usb_mouse_open(struct input_dev *dev) { - struct usb_mouse *mouse = dev->private; + struct usb_mouse *mouse = input_get_drvdata(dev); mouse->irq->dev = mouse->usbdev; if (usb_submit_urb(mouse->irq, GFP_KERNEL)) @@ -107,7 +107,7 @@ static int usb_mouse_open(struct input_dev *dev) static void usb_mouse_close(struct input_dev *dev) { - struct usb_mouse *mouse = dev->private; + struct usb_mouse *mouse = input_get_drvdata(dev); usb_kill_urb(mouse->irq); } @@ -171,7 +171,7 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i input_dev->name = mouse->name; input_dev->phys = mouse->phys; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &intf->dev; + input_dev->dev.parent = &intf->dev; input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); @@ -179,7 +179,8 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); input_dev->relbit[0] |= BIT(REL_WHEEL); - input_dev->private = mouse; + input_set_drvdata(input_dev, mouse); + input_dev->open = usb_mouse_open; input_dev->close = usb_mouse_close; diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 55a7259..b234729 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -336,7 +336,7 @@ static int bits_to_user(unsigned long *bits, unsigned int maxbit, if (compat) { len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t); - if (len < maxlen) + if (len > maxlen) len = maxlen; for (i = 0; i < len / sizeof(compat_long_t); i++) diff --git a/drivers/md/md.c b/drivers/md/md.c index 65814b0..c10ce91 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5103,7 +5103,7 @@ static int is_mddev_idle(mddev_t *mddev) * * Note: the following is an unsigned comparison. */ - if ((curr_events - rdev->last_events + 4096) > 8192) { + if ((long)curr_events - (long)rdev->last_events > 4096) { rdev->last_events = curr_events; idle = 0; } diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index 7c8ccc0..829da9a 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -141,6 +141,20 @@ config ACT200L_DONGLE To activate support for ACTiSYS IR-200L dongle you will have to start irattach like this: "irattach -d act200l". +config KINGSUN_DONGLE + tristate "KingSun/DonShine DS-620 IrDA-USB dongle" + depends on IRDA && USB && EXPERIMENTAL + help + Say Y or M here if you want to build support for the KingSun/DonShine + DS-620 IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speed only (9600 bps). + + To compile it as a module, choose M here: the module will be called + kingsun-sir. + comment "Old SIR device drivers" config IRPORT_SIR diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index 5be09f1..233a2f9 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o +obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o # The SIR helper module sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c new file mode 100644 index 0000000..2174291 --- /dev/null +++ b/drivers/net/irda/kingsun-sir.c @@ -0,0 +1,657 @@ +/***************************************************************************** +* +* Filename: kingsun-sir.c +* Version: 0.1.1 +* Description: Irda KingSun/DonShine USB Dongle +* Status: Experimental +* Author: Alex Villac�s Lasso <a_villacis@palosanto.com> +* +* Based on stir4200 and mcs7780 drivers, with (strange?) differences +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This is my current (2007-04-25) understanding of how this dongle is supposed + * to work. This is based on reverse-engineering and examination of the packet + * data sent and received by the WinXP driver using USBSnoopy. Feel free to + * update here as more of this dongle is known: + * + * General: Unlike the other USB IrDA dongles, this particular dongle exposes, + * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, + * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in + * order to receive data. + * Transmission: Just like stir4200, this dongle uses a raw stream of data, + * which needs to be wrapped and escaped in a similar way as in stir4200.c. + * Reception: Poll-based, as in stir4200. Each read returns the contents of a + * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes + * (1-7) of valid data contained within the remaining 7 bytes. For example, if + * the buffer had the following contents: + * 06 ff ff ff c0 01 04 aa + * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the + * end is garbage (left over from a previous reception) and is discarded. + * If a read returns an "impossible" value as the length of valid data (such as + * 0x36) in the first byte, then the buffer is uninitialized (as is the case of + * first plug-in) and its contents should be discarded. There is currently no + * evidence that the top 5 bits of the 1st byte of the buffer can have values + * other than 0 once reception begins. + * Once valid bytes are collected, the assembled stream is a sequence of + * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. + * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after + * a successful read from the host, which means that in absence of further + * reception, repeated reads from the dongle will return the exact same + * contents repeatedly. Attempts to be smart and cache a previous read seem + * to result in corrupted packets, so this driver depends on the unwrap logic + * to sort out any repeated reads. + * Speed change: no commands observed so far to change speed, assumed fixed + * 9600bps (SIR). + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/crc32.h> + +#include <asm/unaligned.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/crc.h> + +/* + * According to lsusb, 0x07c0 is assigned to + * "Code Mercenaries Hard- und Software GmbH" + */ +#define KING_VENDOR_ID 0x07c0 +#define KING_PRODUCT_ID 0x4200 + +/* These are the currently known USB ids */ +static struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 + +#define KINGSUN_FIFO_SIZE 4096 +#define KINGSUN_EP_IN 0 +#define KINGSUN_EP_OUT 1 + +struct kingsun_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct net_device_stats stats; /* network statistics */ + struct qos_info qos; + + __u8 *in_buf; /* receive buffer */ + __u8 *out_buf; /* transmit buffer */ + __u8 max_rx; /* max. atomic read from dongle + (usually 8), also size of in_buf */ + __u8 max_tx; /* max. atomic write to dongle + (usually 8) */ + + iobuff_t rx_buff; /* receive unwrap state machine */ + struct timeval rx_time; + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *tx_urb; + struct urb *rx_urb; +}; + +/* Callback transmission routine */ +static void kingsun_send_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + err("kingsun_send_irq: Network not running!"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_send_irq: urb asynchronously failed - %d", + urb->status); + } + netif_wake_queue(netdev); +} + +/* + * Called from net/core when new frame is available. + */ +static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct kingsun_cb *kingsun; + int wraplen; + int ret = 0; + + if (skb == NULL || netdev == NULL) + return -EINVAL; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = async_wrap_skb(skb, + kingsun->out_buf, + KINGSUN_FIFO_SIZE); + + /* Calculate how much data can be transmitted in this urb */ + usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), + kingsun->out_buf, wraplen, kingsun_send_irq, + kingsun, 1); + + if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { + err("kingsun_hard_xmit: failed tx_urb submit: %d", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + kingsun->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + kingsun->stats.tx_packets++; + kingsun->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return ret; +} + +/* Receive callback function */ +static void kingsun_rcv_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + int ret; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_rcv_irq: urb asynchronously failed - %d", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length == kingsun->max_rx) { + __u8 *bytes = urb->transfer_buffer; + int i; + + /* The very first byte in the buffer indicates the length of + valid data in the read. This byte must be in the range + 1..kingsun->max_rx -1 . Values outside this range indicate + an uninitialized Rx buffer when the dongle has just been + plugged in. */ + if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { + for (i = 1; i <= bytes[0]; i++) { + async_unwrap_char(kingsun->netdev, + &kingsun->stats, + &kingsun->rx_buff, bytes[i]); + } + kingsun->netdev->last_rx = jiffies; + do_gettimeofday(&kingsun->rx_time); + kingsun->receiving = + (kingsun->rx_buff.state != OUTSIDE_FRAME) + ? 1 : 0; + } + } else if (urb->actual_length > 0) { + err("%s(): Unexpected response length, expected %d got %d", + __FUNCTION__, kingsun->max_rx, urb->actual_length); + } + /* This urb has already been filled in kingsun_net_open */ + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function kingsun_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int kingsun_net_open(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_buff.skb, 1); + kingsun->rx_buff.head = kingsun->rx_buff.skb->data; + do_gettimeofday(&kingsun->rx_time); + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + err("kingsun-sir: irlap_open failed"); + goto free_mem; + } + + /* Start first reception */ + usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), + kingsun->in_buf, kingsun->max_rx, + kingsun_rcv_irq, kingsun, 1); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + err("kingsun-sir: first urb-submit failed: %d", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + if (kingsun->tx_urb) { + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb) { + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + if (kingsun->rx_buff.skb) { + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + } + return err; +} + +/* + * Function kingsun_net_close (kingsun) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int kingsun_net_close(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_kill_urb(kingsun->rx_urb); + + usb_free_urb(kingsun->tx_urb); + usb_free_urb(kingsun->rx_urb); + + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + if (kingsun->irlap) + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct kingsun_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + /* No observed commands for speed change */ + ret = -EOPNOTSUPP; + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* + * Get device stats (for /proc/net/dev and ifconfig) + */ +static struct net_device_stats * +kingsun_net_get_stats(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + return &kingsun->stats; +} + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int kingsun_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + + struct usb_device *dev = interface_to_usbdev(intf); + struct kingsun_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + int pipe, maxp_in, maxp_out; + __u8 ep_in; + __u8 ep_out; + + /* Check that there really are two interrupt endpoints. + Check based on the one in drivers/usb/input/usbmouse.c + */ + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 2) { + err("kingsun-sir: expected 2 endpoints, found %d", + interface->desc.bNumEndpoints); + return -ENODEV; + } + endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + err("kingsun-sir: endpoint 0 is not interrupt IN"); + return -ENODEV; + } + + ep_in = endpoint->bEndpointAddress; + pipe = usb_rcvintpipe(dev, ep_in); + maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + if (maxp_in > 255 || maxp_in <= 1) { + err("%s: endpoint 0 has max packet size %d not in range", + __FILE__, maxp_in); + return -ENODEV; + } + + endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; + if (!usb_endpoint_is_int_out(endpoint)) { + err("kingsun-sir: endpoint 1 is not interrupt OUT"); + return -ENODEV; + } + + ep_out = endpoint->bEndpointAddress; + pipe = usb_sndintpipe(dev, ep_out); + maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if(!net) + goto err_out1; + + SET_MODULE_OWNER(net); + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->irlap = NULL; + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + kingsun->ep_in = ep_in; + kingsun->ep_out = ep_out; + kingsun->in_buf = NULL; + kingsun->out_buf = NULL; + kingsun->max_rx = (__u8)maxp_in; + kingsun->max_tx = (__u8)maxp_out; + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + /* Allocate input buffer */ + kingsun->in_buf = (__u8 *)kmalloc(kingsun->max_rx, GFP_KERNEL); + if (!kingsun->in_buf) + goto free_mem; + + /* Allocate output buffer */ + kingsun->out_buf = (__u8 *)kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->out_buf) + goto free_mem; + + printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* That's the Rx capability. */ + kingsun->qos.baud_rate.bits &= IR_9600; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->hard_start_xmit = kingsun_hard_xmit; + net->open = kingsun_net_open; + net->stop = kingsun_net_close; + net->get_stats = kingsun_net_get_stats; + net->do_ioctl = kingsun_net_ioctl; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + info("IrDA: Registered KingSun/DonShine device %s", net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - urbs not allocated, set to NULL + - max rx packet known (in max_rx) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + +free_mem: + if (kingsun->out_buf) kfree(kingsun->out_buf); + if (kingsun->in_buf) kfree(kingsun->in_buf); + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void kingsun_disconnect(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + if (kingsun->tx_urb != NULL) { + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb != NULL) { + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + + kfree(kingsun->out_buf); + kfree(kingsun->in_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int kingsun_resume(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "kingsun-sir", + .probe = kingsun_probe, + .disconnect = kingsun_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = kingsun_suspend, + .resume = kingsun_resume, +#endif +}; + +/* + * Module insertion + */ +static int __init kingsun_init(void) +{ + return usb_register(&irda_driver); +} +module_init(kingsun_init); + +/* + * Module removal + */ +static void __exit kingsun_cleanup(void) +{ + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(kingsun_cleanup); + +MODULE_AUTHOR("Alex Villac�s Lasso <a_villacis@palosanto.com>"); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 1759baa..95ce8f4 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -246,7 +246,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ - || M32R || ATARI || POWERPC) + || M32R || ATARI || POWERPC || MIPS) help Say "yes" here to get direct support for the real time clock found in every PC or ACPI-based system, and some other boards. @@ -397,7 +397,7 @@ config RTC_DRV_BFIN config RTC_DRV_RS5C313 tristate "Ricoh RS5C313" - depends on RTC_CLASS && BROKEN + depends on RTC_CLASS && SH_LANDISK help If you say yes here you get support for the Ricoh RS5C313 RTC chips. diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c index 9d6de37..66eb133 100644 --- a/drivers/rtc/rtc-rs5c313.c +++ b/drivers/rtc/rtc-rs5c313.c @@ -126,7 +126,7 @@ static void rs5c313_write_data(unsigned char data) static unsigned char rs5c313_read_data(void) { int i; - unsigned char data; + unsigned char data = 0; for (i = 0; i < 8; i++) { ndelay(700); @@ -194,7 +194,7 @@ static void rs5c313_write_reg(unsigned char addr, unsigned char data) return; } -static inline unsigned char rs5c313_read_cntreg(unsigned char addr) +static inline unsigned char rs5c313_read_cntreg(void) { return rs5c313_read_reg(RS5C313_ADDR_CNTREG); } @@ -212,7 +212,9 @@ static inline void rs5c313_write_intintvreg(unsigned char data) static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) { int data; + int cnt; + cnt = 0; while (1) { RS5C313_CEENABLE; /* CE:H */ @@ -225,6 +227,10 @@ static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) RS5C313_CEDISABLE; ndelay(700); /* CE:L */ + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __FUNCTION__); + return -EIO; + } } data = rs5c313_read_reg(RS5C313_ADDR_SEC); @@ -266,7 +272,9 @@ static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) { int data; + int cnt; + cnt = 0; /* busy check. */ while (1) { RS5C313_CEENABLE; /* CE:H */ @@ -279,6 +287,11 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) RS5C313_MISCOP; RS5C313_CEDISABLE; ndelay(700); /* CE:L */ + + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __FUNCTION__); + return -EIO; + } } data = BIN2BCD(tm->tm_sec); @@ -317,6 +330,7 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) static void rs5c313_check_xstp_bit(void) { struct rtc_time tm; + int cnt; RS5C313_CEENABLE; /* CE:H */ if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) { @@ -326,12 +340,16 @@ static void rs5c313_check_xstp_bit(void) rs5c313_write_cntreg(0x07); /* busy check. */ - while (rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY) + for (cnt = 0; cnt < 100; cnt++) { + if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) + break; RS5C313_MISCOP; + } memset(&tm, 0, sizeof(struct rtc_time)); tm.tm_mday = 1; - tm.tm_mon = 1; + tm.tm_mon = 1 - 1; + tm.tm_year = 2000 - 1900; rs5c313_rtc_set_time(NULL, &tm); printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to " @@ -356,7 +374,7 @@ static int rs5c313_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); - return err; + return 0; } static int __devexit rs5c313_rtc_remove(struct platform_device *pdev) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7c9d37f..5e3f748 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -107,6 +107,13 @@ config SPI_IMX This enables using the Freescale iMX SPI controller in master mode. +config SPI_MPC52xx_PSC + tristate "Freescale MPC52xx PSC SPI controller" + depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL + help + This enables using the Freescale MPC52xx Programmable Serial + Controller in master SPI mode. + config SPI_MPC83xx tristate "Freescale MPC83xx SPI controller" depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 624b636..5788d86 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o +obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c new file mode 100644 index 0000000..052359f --- /dev/null +++ b/drivers/spi/mpc52xx_psc_spi.c @@ -0,0 +1,654 @@ +/* + * MPC52xx SPC in SPI mode driver. + * + * Maintainer: Dragos Carp + * + * Copyright (C) 2006 TOPTICA Photonics AG. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/interrupt.h> + +#if defined(CONFIG_PPC_MERGE) +#include <asm/of_platform.h> +#else +#include <linux/platform_device.h> +#endif + +#include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/fsl_devices.h> + +#include <asm/mpc52xx.h> +#include <asm/mpc52xx_psc.h> + +#define MCLK 20000000 /* PSC port MClk in hz */ + +struct mpc52xx_psc_spi { + /* fsl_spi_platform data */ + void (*activate_cs)(u8, u8); + void (*deactivate_cs)(u8, u8); + u32 sysclk; + + /* driver internal data */ + struct mpc52xx_psc __iomem *psc; + unsigned int irq; + u8 bits_per_word; + u8 busy; + + struct workqueue_struct *workqueue; + struct work_struct work; + + struct list_head queue; + spinlock_t lock; + + struct completion done; +}; + +/* controller state */ +struct mpc52xx_psc_spi_cs { + int bits_per_word; + int speed_hz; +}; + +/* set clock freq, clock ramp, bits per work + * if t is NULL then reset the values to the default values + */ +static int mpc52xx_psc_spi_transfer_setup(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + + cs->speed_hz = (t && t->speed_hz) + ? t->speed_hz : spi->max_speed_hz; + cs->bits_per_word = (t && t->bits_per_word) + ? t->bits_per_word : spi->bits_per_word; + cs->bits_per_word = ((cs->bits_per_word + 7) / 8) * 8; + return 0; +} + +static void mpc52xx_psc_spi_activate_cs(struct spi_device *spi) +{ + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc __iomem *psc = mps->psc; + u32 sicr; + u16 ccr; + + sicr = in_be32(&psc->sicr); + + /* Set clock phase and polarity */ + if (spi->mode & SPI_CPHA) + sicr |= 0x00001000; + else + sicr &= ~0x00001000; + if (spi->mode & SPI_CPOL) + sicr |= 0x00002000; + else + sicr &= ~0x00002000; + + if (spi->mode & SPI_LSB_FIRST) + sicr |= 0x10000000; + else + sicr &= ~0x10000000; + out_be32(&psc->sicr, sicr); + + /* Set clock frequency and bits per word + * Because psc->ccr is defined as 16bit register instead of 32bit + * just set the lower byte of BitClkDiv + */ + ccr = in_be16(&psc->ccr); + ccr &= 0xFF00; + if (cs->speed_hz) + ccr |= (MCLK / cs->speed_hz - 1) & 0xFF; + else /* by default SPI Clk 1MHz */ + ccr |= (MCLK / 1000000 - 1) & 0xFF; + out_be16(&psc->ccr, ccr); + mps->bits_per_word = cs->bits_per_word; + + if (mps->activate_cs) + mps->activate_cs(spi->chip_select, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); +} + +static void mpc52xx_psc_spi_deactivate_cs(struct spi_device *spi) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + + if (mps->deactivate_cs) + mps->deactivate_cs(spi->chip_select, + (spi->mode & SPI_CS_HIGH) ? 1 : 0); +} + +#define MPC52xx_PSC_BUFSIZE (MPC52xx_PSC_RFNUM_MASK + 1) +/* wake up when 80% fifo full */ +#define MPC52xx_PSC_RFALARM (MPC52xx_PSC_BUFSIZE * 20 / 100) + +static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc __iomem *psc = mps->psc; + unsigned rb = 0; /* number of bytes receieved */ + unsigned sb = 0; /* number of bytes sent */ + unsigned char *rx_buf = (unsigned char *)t->rx_buf; + unsigned char *tx_buf = (unsigned char *)t->tx_buf; + unsigned rfalarm; + unsigned send_at_once = MPC52xx_PSC_BUFSIZE; + unsigned recv_at_once; + unsigned bpw = mps->bits_per_word / 8; + + if (!t->tx_buf && !t->rx_buf && t->len) + return -EINVAL; + + /* enable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + while (rb < t->len) { + if (t->len - rb > MPC52xx_PSC_BUFSIZE) { + rfalarm = MPC52xx_PSC_RFALARM; + } else { + send_at_once = t->len - sb; + rfalarm = MPC52xx_PSC_BUFSIZE - (t->len - rb); + } + + dev_dbg(&spi->dev, "send %d bytes...\n", send_at_once); + if (tx_buf) { + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag */ + if (mps->bits_per_word + && (sb + 1) % bpw == 0) + out_8(&psc->ircr2, 0x01); + out_8(&psc->mpc52xx_psc_buffer_8, tx_buf[sb]); + } + } else { + for (; send_at_once; sb++, send_at_once--) { + /* set EOF flag */ + if (mps->bits_per_word + && ((sb + 1) % bpw) == 0) + out_8(&psc->ircr2, 0x01); + out_8(&psc->mpc52xx_psc_buffer_8, 0); + } + } + + + /* enable interupts and wait for wake up + * if just one byte is expected the Rx FIFO genererates no + * FFULL interrupt, so activate the RxRDY interrupt + */ + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + if (t->len - rb == 1) { + out_8(&psc->mode, 0); + } else { + out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL); + out_be16(&psc->rfalarm, rfalarm); + } + out_be16(&psc->mpc52xx_psc_imr, MPC52xx_PSC_IMR_RXRDY); + wait_for_completion(&mps->done); + recv_at_once = in_be16(&psc->rfnum); + dev_dbg(&spi->dev, "%d bytes received\n", recv_at_once); + + send_at_once = recv_at_once; + if (rx_buf) { + for (; recv_at_once; rb++, recv_at_once--) + rx_buf[rb] = in_8(&psc->mpc52xx_psc_buffer_8); + } else { + for (; recv_at_once; rb++, recv_at_once--) + in_8(&psc->mpc52xx_psc_buffer_8); + } + } + /* disable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + + return 0; +} + +static void mpc52xx_psc_spi_work(struct work_struct *work) +{ + struct mpc52xx_psc_spi *mps = + container_of(work, struct mpc52xx_psc_spi, work); + + spin_lock_irq(&mps->lock); + mps->busy = 1; + while (!list_empty(&mps->queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + unsigned cs_change; + int status; + + m = container_of(mps->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irq(&mps->lock); + + spi = m->spi; + cs_change = 1; + status = 0; + list_for_each_entry (t, &m->transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { + status = mpc52xx_psc_spi_transfer_setup(spi, t); + if (status < 0) + break; + } + + if (cs_change) + mpc52xx_psc_spi_activate_cs(spi); + cs_change = t->cs_change; + + status = mpc52xx_psc_spi_transfer_rxtx(spi, t); + if (status) + break; + m->actual_length += t->len; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change) + mpc52xx_psc_spi_deactivate_cs(spi); + } + + m->status = status; + m->complete(m->context); + + if (status || !cs_change) + mpc52xx_psc_spi_deactivate_cs(spi); + + mpc52xx_psc_spi_transfer_setup(spi, NULL); + + spin_lock_irq(&mps->lock); + } + mps->busy = 0; + spin_unlock_irq(&mps->lock); +} + +static int mpc52xx_psc_spi_setup(struct spi_device *spi) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + struct mpc52xx_psc_spi_cs *cs = spi->controller_state; + unsigned long flags; + + if (spi->bits_per_word%8) + return -EINVAL; + + if (!cs) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + cs->bits_per_word = spi->bits_per_word; + cs->speed_hz = spi->max_speed_hz; + + spin_lock_irqsave(&mps->lock, flags); + if (!mps->busy) + mpc52xx_psc_spi_deactivate_cs(spi); + spin_unlock_irqrestore(&mps->lock, flags); + + return 0; +} + +static int mpc52xx_psc_spi_transfer(struct spi_device *spi, + struct spi_message *m) +{ + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + spin_lock_irqsave(&mps->lock, flags); + list_add_tail(&m->queue, &mps->queue); + queue_work(mps->workqueue, &mps->work); + spin_unlock_irqrestore(&mps->lock, flags); + + return 0; +} + +static void mpc52xx_psc_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps) +{ + struct mpc52xx_cdm __iomem *cdm; + struct mpc52xx_gpio __iomem *gpio; + struct mpc52xx_psc __iomem *psc = mps->psc; + u32 ul; + u32 mclken_div; + int ret = 0; + +#if defined(CONFIG_PPC_MERGE) + cdm = mpc52xx_find_and_map("mpc52xx-cdm"); + gpio = mpc52xx_find_and_map("mpc52xx-gpio"); +#else + cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); + gpio = ioremap(MPC52xx_PA(MPC52xx_GPIO_OFFSET), MPC52xx_GPIO_SIZE); +#endif + if (!cdm || !gpio) { + printk(KERN_ERR "Error mapping CDM/GPIO\n"); + ret = -EFAULT; + goto unmap_regs; + } + + /* default sysclk is 512MHz */ + mclken_div = 0x8000 | + (((mps->sysclk ? mps->sysclk : 512000000) / MCLK) & 0x1FF); + + switch (psc_id) { + case 1: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFFFF8; + ul |= 0x00000006; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc1, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000020; + out_be32(&cdm->clk_enables, ul); + break; + case 2: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFFF8F; + ul |= 0x00000060; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc2, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000040; + out_be32(&cdm->clk_enables, ul); + break; + case 3: + ul = in_be32(&gpio->port_config); + ul &= 0xFFFFF0FF; + ul |= 0x00000600; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc3, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000080; + out_be32(&cdm->clk_enables, ul); + break; + case 6: + ul = in_be32(&gpio->port_config); + ul &= 0xFF8FFFFF; + ul |= 0x00700000; + out_be32(&gpio->port_config, ul); + out_be16(&cdm->mclken_div_psc6, mclken_div); + ul = in_be32(&cdm->clk_enables); + ul |= 0x00000010; + out_be32(&cdm->clk_enables, ul); + break; + default: + ret = -EINVAL; + goto unmap_regs; + } + + /* Reset the PSC into a known state */ + out_8(&psc->command, MPC52xx_PSC_RST_RX); + out_8(&psc->command, MPC52xx_PSC_RST_TX); + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + + /* Disable interrupts, interrupts are based on alarm level */ + out_be16(&psc->mpc52xx_psc_imr, 0); + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&psc->rfcntl, 0); + out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL); + + /* Configure 8bit codec mode as a SPI master and use EOF flags */ + /* SICR_SIM_CODEC8|SICR_GENCLK|SICR_SPI|SICR_MSTR|SICR_USEEOF */ + out_be32(&psc->sicr, 0x0180C800); + out_be16(&psc->ccr, 0x070F); /* by default SPI Clk 1MHz */ + + /* Set 2ms DTL delay */ + out_8(&psc->ctur, 0x00); + out_8(&psc->ctlr, 0x84); + + mps->bits_per_word = 8; + +unmap_regs: + if (cdm) + iounmap(cdm); + if (gpio) + iounmap(gpio); + + return ret; +} + +static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id) +{ + struct mpc52xx_psc_spi *mps = (struct mpc52xx_psc_spi *)dev_id; + struct mpc52xx_psc __iomem *psc = mps->psc; + + /* disable interrupt and wake up the work queue */ + if (in_be16(&psc->mpc52xx_psc_isr) & MPC52xx_PSC_IMR_RXRDY) { + out_be16(&psc->mpc52xx_psc_imr, 0); + complete(&mps->done); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* bus_num is used only for the case dev->platform_data == NULL */ +static int __init mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, + u32 size, unsigned int irq, s16 bus_num) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct mpc52xx_psc_spi *mps; + struct spi_master *master; + int ret; + + if (pdata == NULL) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof *mps); + if (master == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, master); + mps = spi_master_get_devdata(master); + + mps->irq = irq; + if (pdata == NULL) { + dev_warn(dev, "probe called without platform data, no " + "(de)activate_cs function will be called\n"); + mps->activate_cs = NULL; + mps->deactivate_cs = NULL; + mps->sysclk = 0; + master->bus_num = bus_num; + master->num_chipselect = 255; + } else { + mps->activate_cs = pdata->activate_cs; + mps->deactivate_cs = pdata->deactivate_cs; + mps->sysclk = pdata->sysclk; + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->max_chipselect; + } + master->setup = mpc52xx_psc_spi_setup; + master->transfer = mpc52xx_psc_spi_transfer; + master->cleanup = mpc52xx_psc_spi_cleanup; + + mps->psc = ioremap(regaddr, size); + if (!mps->psc) { + dev_err(dev, "could not ioremap I/O port range\n"); + ret = -EFAULT; + goto free_master; + } + + ret = request_irq(mps->irq, mpc52xx_psc_spi_isr, 0, "mpc52xx-psc-spi", + mps); + if (ret) + goto free_master; + + ret = mpc52xx_psc_spi_port_config(master->bus_num, mps); + if (ret < 0) + goto free_irq; + + spin_lock_init(&mps->lock); + init_completion(&mps->done); + INIT_WORK(&mps->work, mpc52xx_psc_spi_work); + INIT_LIST_HEAD(&mps->queue); + + mps->workqueue = create_singlethread_workqueue( + master->cdev.dev->bus_id); + if (mps->workqueue == NULL) { + ret = -EBUSY; + goto free_irq; + } + + ret = spi_register_master(master); + if (ret < 0) + goto unreg_master; + + return ret; + +unreg_master: + destroy_workqueue(mps->workqueue); +free_irq: + free_irq(mps->irq, mps); +free_master: + if (mps->psc) + iounmap(mps->psc); + spi_master_put(master); + + return ret; +} + +static int __exit mpc52xx_psc_spi_do_remove(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); + + flush_workqueue(mps->workqueue); + destroy_workqueue(mps->workqueue); + spi_unregister_master(master); + free_irq(mps->irq, mps); + if (mps->psc) + iounmap(mps->psc); + + return 0; +} + +#if !defined(CONFIG_PPC_MERGE) +static int __init mpc52xx_psc_spi_probe(struct platform_device *dev) +{ + switch(dev->id) { + case 1: + case 2: + case 3: + case 6: + return mpc52xx_psc_spi_do_probe(&dev->dev, + MPC52xx_PA(MPC52xx_PSCx_OFFSET(dev->id)), + MPC52xx_PSC_SIZE, platform_get_irq(dev, 0), dev->id); + default: + return -EINVAL; + } +} + +static int __exit mpc52xx_psc_spi_remove(struct platform_device *dev) +{ + return mpc52xx_psc_spi_do_remove(&dev->dev); +} + +static struct platform_driver mpc52xx_psc_spi_platform_driver = { + .remove = __exit_p(mpc52xx_psc_spi_remove), + .driver = { + .name = "mpc52xx-psc-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc52xx_psc_spi_init(void) +{ + return platform_driver_probe(&mpc52xx_psc_spi_platform_driver, + mpc52xx_psc_spi_probe); +} +module_init(mpc52xx_psc_spi_init); + +static void __exit mpc52xx_psc_spi_exit(void) +{ + platform_driver_unregister(&mpc52xx_psc_spi_platform_driver); +} +module_exit(mpc52xx_psc_spi_exit); + +#else /* defined(CONFIG_PPC_MERGE) */ + +static int __init mpc52xx_psc_spi_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + const u32 *regaddr_p; + u64 regaddr64, size64; + s16 id = -1; + + regaddr_p = of_get_address(op->node, 0, &size64, NULL); + if (!regaddr_p) { + printk(KERN_ERR "Invalid PSC address\n"); + return -EINVAL; + } + regaddr64 = of_translate_address(op->node, regaddr_p); + + if (op->dev.platform_data == NULL) { + struct device_node *np; + int i = 0; + + for_each_node_by_type(np, "spi") { + if (of_find_device_by_node(np) == op) { + id = i; + break; + } + i++; + } + } + + return mpc52xx_psc_spi_do_probe(&op->dev, (u32)regaddr64, (u32)size64, + irq_of_parse_and_map(op->node, 0), id); +} + +static int __exit mpc52xx_psc_spi_of_remove(struct of_device *op) +{ + return mpc52xx_psc_spi_do_remove(&op->dev); +} + +static struct of_device_id mpc52xx_psc_spi_of_match[] = { + { .type = "spi", .compatible = "mpc52xx-psc-spi", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match); + +static struct of_platform_driver mpc52xx_psc_spi_of_driver = { + .owner = THIS_MODULE, + .name = "mpc52xx-psc-spi", + .match_table = mpc52xx_psc_spi_of_match, + .probe = mpc52xx_psc_spi_of_probe, + .remove = __exit_p(mpc52xx_psc_spi_of_remove), + .driver = { + .name = "mpc52xx-psc-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc52xx_psc_spi_init(void) +{ + return of_register_platform_driver(&mpc52xx_psc_spi_of_driver); +} +module_init(mpc52xx_psc_spi_init); + +static void __exit mpc52xx_psc_spi_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_psc_spi_of_driver); +} +module_exit(mpc52xx_psc_spi_exit); + +#endif /* defined(CONFIG_PPC_MERGE) */ + +MODULE_AUTHOR("Dragos Carp"); +MODULE_DESCRIPTION("MPC52xx PSC SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index f544388..eebcb70 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -748,6 +748,22 @@ config FB_S1D13XXX working with S1D13806). Product specs at <http://www.erd.epson.com/vdc/html/legacy_13xxx.htm> +config FB_ATMEL + tristate "AT91/AT32 LCD Controller support" + depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This enables support for the AT91/AT32 LCD Controller. + +config FB_INTSRAM + bool "Frame Buffer in internal SRAM" + depends on FB_ATMEL && ARCH_AT91SAM9261 + help + Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want + to let frame buffer in external SDRAM. + config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI @@ -780,6 +796,15 @@ config FB_NVIDIA_I2C independently validate video mode parameters, you should say Y here. +config FB_NVIDIA_DEBUG + bool "Lots of debug output" + depends on FB_NVIDIA + default n + help + Say Y here if you want the nVidia driver to output all sorts + of debugging information to provide to the maintainer when + something goes wrong. + config FB_NVIDIA_BACKLIGHT bool "Support for backlight control" depends on FB_NVIDIA @@ -819,7 +844,7 @@ config FB_RIVA_I2C here. config FB_RIVA_DEBUG - bool "Lots of debug output from Riva(nVidia) driver" + bool "Lots of debug output" depends on FB_RIVA default n help @@ -1431,8 +1456,11 @@ config FB_ARK and ICS 5342 RAMDAC. config FB_PM3 - tristate "Permedia3 support" - depends on FB && PCI && BROKEN + tristate "Permedia3 support (EXPERIMENTAL)" + depends on FB && PCI && EXPERIMENTAL + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT help This is the frame buffer device driver for the 3DLabs Permedia3 chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 & diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0b70567..bd8b052 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_FB_G364) += g364fb.o obj-$(CONFIG_FB_SA1100) += sa1100fb.o obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o +obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c new file mode 100644 index 0000000..e1d5bd0 --- /dev/null +++ b/drivers/video/atmel_lcdfb.c @@ -0,0 +1,752 @@ +/* + * Driver for AT91/AT32 LCD Controller + * + * Copyright (C) 2007 Atmel Corporation + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/arch/board.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> + +#include <video/atmel_lcdc.h> + +#define lcdc_readl(sinfo, reg) __raw_readl((sinfo)->mmio+(reg)) +#define lcdc_writel(sinfo, reg, val) __raw_writel((val), (sinfo)->mmio+(reg)) + +/* configurable parameters */ +#define ATMEL_LCDC_CVAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 8 + +#if defined(CONFIG_ARCH_AT91SAM9263) +#define ATMEL_LCDC_FIFO_SIZE 2048 +#else +#define ATMEL_LCDC_FIFO_SIZE 512 +#endif + +#if defined(CONFIG_ARCH_AT91) +#define ATMEL_LCDFB_FBINFO_DEFAULT FBINFO_DEFAULT + +static inline void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, + struct fb_var_screeninfo *var) +{ + +} +#elif defined(CONFIG_AVR32) +#define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \ + | FBINFO_PARTIAL_PAN_OK \ + | FBINFO_HWACCEL_XPAN \ + | FBINFO_HWACCEL_YPAN) + +static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo, + struct fb_var_screeninfo *var) +{ + u32 dma2dcfg; + u32 pixeloff; + + pixeloff = (var->xoffset * var->bits_per_pixel) & 0x1f; + + dma2dcfg = ((var->xres_virtual - var->xres) * var->bits_per_pixel) / 8; + dma2dcfg |= pixeloff << ATMEL_LCDC_PIXELOFF_OFFSET; + lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg); + + /* Update configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, + lcdc_readl(sinfo, ATMEL_LCDC_DMACON) + | ATMEL_LCDC_DMAUPDT); +} +#endif + + +static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + + +static void atmel_lcdfb_update_dma(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct atmel_lcdfb_info *sinfo = info->par; + struct fb_fix_screeninfo *fix = &info->fix; + unsigned long dma_addr; + + dma_addr = (fix->smem_start + var->yoffset * fix->line_length + + var->xoffset * var->bits_per_pixel / 8); + + dma_addr &= ~3UL; + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr); + + atmel_lcdfb_update_dma2d(sinfo, var); +} + +static inline void atmel_lcdfb_free_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + + dma_free_writecombine(info->device, info->fix.smem_len, + info->screen_base, info->fix.smem_start); +} + +/** + * atmel_lcdfb_alloc_video_memory - Allocate framebuffer memory + * @sinfo: the frame buffer to allocate memory for + */ +static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + struct fb_var_screeninfo *var = &info->var; + + info->fix.smem_len = (var->xres_virtual * var->yres_virtual + * ((var->bits_per_pixel + 7) / 8)); + + info->screen_base = dma_alloc_writecombine(info->device, info->fix.smem_len, + (dma_addr_t *)&info->fix.smem_start, GFP_KERNEL); + + if (!info->screen_base) { + return -ENOMEM; + } + + return 0; +} + +/** + * atmel_lcdfb_check_var - Validates a var passed in. + * @var: frame buffer variable screen structure + * @info: frame buffer structure that represents a single frame buffer + * + * Checks to see if the hardware supports the state requested by + * var passed in. This function does not alter the hardware + * state!!! This means the data stored in struct fb_info and + * struct atmel_lcdfb_info do not change. This includes the var + * inside of struct fb_info. Do NOT change these. This function + * can be called on its own if we intent to only test a mode and + * not actually set it. The stuff in modedb.c is a example of + * this. If the var passed in is slightly off by what the + * hardware can support then we alter the var PASSED in to what + * we can do. If the hardware doesn't support mode change a + * -EINVAL will be returned by the upper layers. You don't need + * to implement this function then. If you hardware doesn't + * support changing the resolution then this function is not + * needed. In this case the driver would just provide a var that + * represents the static state the screen is in. + * + * Returns negative errno on error, or zero on success. + */ +static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct device *dev = info->device; + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long clk_value_khz; + + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + dev_dbg(dev, "%s:\n", __func__); + dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); + dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); + dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); + dev_dbg(dev, " clk: %lu KHz\n", clk_value_khz); + + if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) { + dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; + } + + /* Force same alignment for each line */ + var->xres = (var->xres + 3) & ~3UL; + var->xres_virtual = (var->xres_virtual + 3) & ~3UL; + + var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0; + var->transp.msb_right = 0; + var->transp.offset = var->transp.length = 0; + var->xoffset = var->yoffset = 0; + + switch (var->bits_per_pixel) { + case 2: + case 4: + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length + = var->bits_per_pixel; + break; + case 15: + case 16: + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + var->red.length = var->green.length = var->blue.length = 5; + break; + case 24: + case 32: + var->red.offset = 0; + var->green.offset = 8; + var->blue.offset = 16; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + dev_err(dev, "color depth %d not supported\n", + var->bits_per_pixel); + return -EINVAL; + } + + return 0; +} + +/** + * atmel_lcdfb_set_par - Alters the hardware state. + * @info: frame buffer structure that represents a single frame buffer + * + * Using the fb_var_screeninfo in fb_info we set the resolution + * of the this particular framebuffer. This function alters the + * par AND the fb_fix_screeninfo stored in fb_info. It doesn't + * not alter var in fb_info since we are using that data. This + * means we depend on the data in var inside fb_info to be + * supported by the hardware. atmel_lcdfb_check_var is always called + * before atmel_lcdfb_set_par to ensure this. Again if you can't + * change the resolution you don't need this function. + * + */ +static int atmel_lcdfb_set_par(struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long value; + unsigned long clk_value_khz; + + dev_dbg(info->device, "%s:\n", __func__); + dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", + info->var.xres, info->var.yres, + info->var.xres_virtual, info->var.yres_virtual); + + /* Turn off the LCD controller and the DMA controller */ + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET); + + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0); + + if (info->var.bits_per_pixel <= 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + info->fix.line_length = info->var.xres_virtual * (info->var.bits_per_pixel / 8); + + /* Re-initialize the DMA engine... */ + dev_dbg(info->device, " * update DMA engine\n"); + atmel_lcdfb_update_dma(info, &info->var); + + /* ...set frame size and burst length = 8 words (?) */ + value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; + value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); + lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); + + /* Now, the LCDC core... */ + + /* Set pixel clock */ + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + value = clk_value_khz / PICOS2KHZ(info->var.pixclock); + + if (clk_value_khz % PICOS2KHZ(info->var.pixclock)) + value++; + + value = (value / 2) - 1; + + if (value <= 0) { + dev_notice(info->device, "Bypassing pixel clock divider\n"); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); + } else + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); + + /* Initialize control register 2 */ + value = sinfo->default_lcdcon2; + + if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + value |= ATMEL_LCDC_INVLINE_INVERTED; + if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + value |= ATMEL_LCDC_INVFRAME_INVERTED; + + switch (info->var.bits_per_pixel) { + case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; + case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; + case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; + case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; + case 15: /* fall through */ + case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; + case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; + case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; + default: BUG(); break; + } + dev_dbg(info->device, " * LCDCON2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value); + + /* Vertical timing */ + value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; + value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET; + value |= info->var.lower_margin; + dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value); + + /* Horizontal timing */ + value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; + value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; + value |= (info->var.left_margin - 1); + dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); + + /* Display size */ + value = (info->var.xres - 1) << ATMEL_LCDC_HOZVAL_OFFSET; + value |= info->var.yres - 1; + lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value); + + /* FIFO Threshold: Use formula from data sheet */ + value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); + lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); + + /* Set contrast */ + value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE; + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value); + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); + /* ...wait for DMA engine to become idle... */ + while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) + msleep(10); + + dev_dbg(info->device, " * re-enable DMA engine\n"); + /* ...and enable it with updated configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon); + + dev_dbg(info->device, " * re-enable LCDC core\n"); + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, + (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); + + dev_dbg(info->device, " * DONE\n"); + + return 0; +} + +static inline unsigned int chan_to_field(unsigned int chan, const struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/** + * atmel_lcdfb_setcolreg - Optional function. Sets a color register. + * @regno: Which register in the CLUT we are programming + * @red: The red value which can be up to 16 bits wide + * @green: The green value which can be up to 16 bits wide + * @blue: The blue value which can be up to 16 bits wide. + * @transp: If supported the alpha value which can be up to 16 bits wide. + * @info: frame buffer info structure + * + * Set a single color register. The values supplied have a 16 bit + * magnitude which needs to be scaled in this function for the hardware. + * Things to take into consideration are how many color registers, if + * any, are supported with the current color visual. With truecolor mode + * no color palettes are supported. Here a psuedo palette is created + * which we store the value in pseudo_palette in struct fb_info. For + * pseudocolor mode we have a limited color palette. To deal with this + * we can program what color is displayed for a particular pixel value. + * DirectColor is similar in that we can program each color field. If + * we have a static colormap we don't need to implement this function. + * + * Returns negative errno on error, or zero on success. In an + * ideal world, this would have been the case, but as it turns + * out, the other drivers return 1 on failure, so that's what + * we're going to do. + */ +static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned int val; + u32 *pal; + int ret = 1; + + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = ((red >> 11) & 0x001f); + val |= ((green >> 6) & 0x03e0); + val |= ((blue >> 1) & 0x7c00); + + /* + * TODO: intensity bit. Maybe something like + * ~(red[10] ^ green[10] ^ blue[10]) & 1 + */ + + lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val); + ret = 0; + } + break; + } + + return ret; +} + +static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + dev_dbg(info->device, "%s\n", __func__); + + atmel_lcdfb_update_dma(info, var); + + return 0; +} + +static struct fb_ops atmel_lcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = atmel_lcdfb_check_var, + .fb_set_par = atmel_lcdfb_set_par, + .fb_setcolreg = atmel_lcdfb_setcolreg, + .fb_pan_display = atmel_lcdfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id) +{ + struct fb_info *info = dev_id; + struct atmel_lcdfb_info *sinfo = info->par; + u32 status; + + status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); + lcdc_writel(sinfo, ATMEL_LCDC_IDR, status); + return IRQ_HANDLED; +} + +static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + int ret = 0; + + memset_io(info->screen_base, 0, info->fix.smem_len); + info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; + + dev_info(info->device, + "%luKiB frame buffer at %08lx (mapped at %p)\n", + (unsigned long)info->fix.smem_len / 1024, + (unsigned long)info->fix.smem_start, + info->screen_base); + + /* Allocate colormap */ + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) + dev_err(info->device, "Alloc color map failed\n"); + + return ret; +} + +static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk) + clk_enable(sinfo->bus_clk); + clk_enable(sinfo->lcdc_clk); +} + +static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk) + clk_disable(sinfo->bus_clk); + clk_disable(sinfo->lcdc_clk); +} + + +static int __init atmel_lcdfb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info; + struct atmel_lcdfb_info *sinfo; + struct atmel_lcdfb_info *pdata_sinfo; + struct resource *regs = NULL; + struct resource *map = NULL; + int ret; + + dev_dbg(dev, "%s BEGIN\n", __func__); + + ret = -ENOMEM; + info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev); + if (!info) { + dev_err(dev, "cannot allocate memory\n"); + goto out; + } + + sinfo = info->par; + + if (dev->platform_data) { + pdata_sinfo = (struct atmel_lcdfb_info *)dev->platform_data; + sinfo->default_bpp = pdata_sinfo->default_bpp; + sinfo->default_dmacon = pdata_sinfo->default_dmacon; + sinfo->default_lcdcon2 = pdata_sinfo->default_lcdcon2; + sinfo->default_monspecs = pdata_sinfo->default_monspecs; + sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; + sinfo->guard_time = pdata_sinfo->guard_time; + } else { + dev_err(dev, "cannot get default configuration\n"); + goto free_info; + } + sinfo->info = info; + sinfo->pdev = pdev; + + strcpy(info->fix.id, sinfo->pdev->name); + info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; + info->pseudo_palette = sinfo->pseudo_palette; + info->fbops = &atmel_lcdfb_ops; + + memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs)); + info->fix = atmel_lcdfb_fix; + + /* Enable LCDC Clocks */ + if (cpu_is_at91sam9261() || cpu_is_at32ap7000()) { + sinfo->bus_clk = clk_get(dev, "hck1"); + if (IS_ERR(sinfo->bus_clk)) { + ret = PTR_ERR(sinfo->bus_clk); + goto free_info; + } + } + sinfo->lcdc_clk = clk_get(dev, "lcdc_clk"); + if (IS_ERR(sinfo->lcdc_clk)) { + ret = PTR_ERR(sinfo->lcdc_clk); + goto put_bus_clk; + } + atmel_lcdfb_start_clock(sinfo); + + ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, + info->monspecs.modedb_len, info->monspecs.modedb, + sinfo->default_bpp); + if (!ret) { + dev_err(dev, "no suitable video mode found\n"); + goto stop_clk; + } + + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "resources unusable\n"); + ret = -ENXIO; + goto stop_clk; + } + + sinfo->irq_base = platform_get_irq(pdev, 0); + if (sinfo->irq_base < 0) { + dev_err(dev, "unable to get irq\n"); + ret = sinfo->irq_base; + goto stop_clk; + } + + /* Initialize video memory */ + map = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (map) { + /* use a pre-allocated memory buffer */ + info->fix.smem_start = map->start; + info->fix.smem_len = map->end - map->start + 1; + if (!request_mem_region(info->fix.smem_start, + info->fix.smem_len, pdev->name)) { + ret = -EBUSY; + goto stop_clk; + } + + info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); + if (!info->screen_base) + goto release_intmem; + } else { + /* alocate memory buffer */ + ret = atmel_lcdfb_alloc_video_memory(sinfo); + if (ret < 0) { + dev_err(dev, "cannot allocate framebuffer: %d\n", ret); + goto stop_clk; + } + } + + /* LCDC registers */ + info->fix.mmio_start = regs->start; + info->fix.mmio_len = regs->end - regs->start + 1; + + if (!request_mem_region(info->fix.mmio_start, + info->fix.mmio_len, pdev->name)) { + ret = -EBUSY; + goto free_fb; + } + + sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); + if (!sinfo->mmio) { + dev_err(dev, "cannot map LCDC registers\n"); + goto release_mem; + } + + /* interrupt */ + ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); + if (ret) { + dev_err(dev, "request_irq failed: %d\n", ret); + goto unmap_mmio; + } + + ret = atmel_lcdfb_init_fbinfo(sinfo); + if (ret < 0) { + dev_err(dev, "init fbinfo failed: %d\n", ret); + goto unregister_irqs; + } + + /* + * This makes sure that our colour bitfield + * descriptors are correctly initialised. + */ + atmel_lcdfb_check_var(&info->var, info); + + ret = fb_set_var(info, &info->var); + if (ret) { + dev_warn(dev, "unable to set display parameters\n"); + goto free_cmap; + } + + dev_set_drvdata(dev, info); + + /* + * Tell the world that we're ready to go + */ + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "failed to register framebuffer device: %d\n", ret); + goto free_cmap; + } + + /* Power up the LCDC screen */ + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(1); + + dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", + info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); + + return 0; + + +free_cmap: + fb_dealloc_cmap(&info->cmap); +unregister_irqs: + free_irq(sinfo->irq_base, info); +unmap_mmio: + iounmap(sinfo->mmio); +release_mem: + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); +free_fb: + if (map) + iounmap(info->screen_base); + else + atmel_lcdfb_free_video_memory(sinfo); + +release_intmem: + if (map) + release_mem_region(info->fix.smem_start, info->fix.smem_len); +stop_clk: + atmel_lcdfb_stop_clock(sinfo); + clk_put(sinfo->lcdc_clk); +put_bus_clk: + if (sinfo->bus_clk) + clk_put(sinfo->bus_clk); +free_info: + framebuffer_release(info); +out: + dev_dbg(dev, "%s FAILED\n", __func__); + return ret; +} + +static int __exit atmel_lcdfb_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info = dev_get_drvdata(dev); + struct atmel_lcdfb_info *sinfo = info->par; + + if (!sinfo) + return 0; + + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(0); + unregister_framebuffer(info); + atmel_lcdfb_stop_clock(sinfo); + clk_put(sinfo->lcdc_clk); + if (sinfo->bus_clk) + clk_put(sinfo->bus_clk); + fb_dealloc_cmap(&info->cmap); + free_irq(sinfo->irq_base, info); + iounmap(sinfo->mmio); + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); + if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) { + iounmap(info->screen_base); + release_mem_region(info->fix.smem_start, info->fix.smem_len); + } else { + atmel_lcdfb_free_video_memory(sinfo); + } + + dev_set_drvdata(dev, NULL); + framebuffer_release(info); + + return 0; +} + +static struct platform_driver atmel_lcdfb_driver = { + .remove = __exit_p(atmel_lcdfb_remove), + .driver = { + .name = "atmel_lcdfb", + .owner = THIS_MODULE, + }, +}; + +static int __init atmel_lcdfb_init(void) +{ + return platform_driver_probe(&atmel_lcdfb_driver, atmel_lcdfb_probe); +} + +static void __exit atmel_lcdfb_exit(void) +{ + platform_driver_unregister(&atmel_lcdfb_driver); +} + +module_init(atmel_lcdfb_init); +module_exit(atmel_lcdfb_exit); + +MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver"); +MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@rfo.atmel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index aa3935d..63b85bf 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -19,13 +19,6 @@ config VGA_CONSOLE Say Y. -# if [ "$CONFIG_PCI" = "y" -a "$CONFIG_VGA_CONSOLE" = "y" ]; then -# bool ' Allow VGA on any bus?' CONFIG_VGA_HOSE -# if [ "$CONFIG_VGA_HOSE" = "y" ]; then -# define_bool CONFIG_DUMMY_CONSOLE y -# fi -# fi - config VGACON_SOFT_SCROLLBACK bool "Enable Scrollback Buffer in System RAM" depends on VGA_CONSOLE diff --git a/drivers/video/nvidia/nv_hw.c b/drivers/video/nvidia/nv_hw.c index c627955..aff11bb 100644 --- a/drivers/video/nvidia/nv_hw.c +++ b/drivers/video/nvidia/nv_hw.c @@ -149,7 +149,9 @@ static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk, pll = NV_RD32(par->PMC, 0x4024); M = pll & 0xFF; N = (pll >> 8) & 0xFF; - if (((par->Chipset & 0xfff0) == 0x0290) || ((par->Chipset & 0xfff0) == 0x0390) || ((par->Chipset & 0xfff0) == 0x02E0)) { + if (((par->Chipset & 0xfff0) == 0x0290) || + ((par->Chipset & 0xfff0) == 0x0390) || + ((par->Chipset & 0xfff0) == 0x02E0)) { MB = 1; NB = 1; } else { diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index f85edf0..41f63658 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -37,7 +37,6 @@ #include "nv_proto.h" #include "nv_dma.h" -#undef CONFIG_FB_NVIDIA_DEBUG #ifdef CONFIG_FB_NVIDIA_DEBUG #define NVTRACE printk #else diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 1ac5264..ab5e668 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -204,17 +204,6 @@ static inline void WAIT_FIFO(struct pm2fb_par* p, u32 a) } #endif -static void wait_pm2(struct pm2fb_par* par) { - - WAIT_FIFO(par, 1); - pm2_WR(par, PM2R_SYNC, 0); - mb(); - do { - while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0); - rmb(); - } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); -} - /* * partial products for the supported horizontal resolutions. */ @@ -1050,13 +1039,30 @@ static int pm2fb_blank(int blank_mode, struct fb_info *info) return 0; } +static int pm2fb_sync(struct fb_info *info) +{ + struct pm2fb_par *par = info->par; + + WAIT_FIFO(par, 1); + pm2_WR(par, PM2R_SYNC, 0); + mb(); + do { + while (pm2_RD(par, PM2R_OUT_FIFO_WORDS) == 0) + udelay(10); + rmb(); + } while (pm2_RD(par, PM2R_OUT_FIFO) != PM2TAG(PM2R_SYNC)); + + return 0; +} + /* * block operation. copy=0: rectangle fill, copy=1: rectangle copy. */ -static void pm2fb_block_op(struct pm2fb_par* par, int copy, +static void pm2fb_block_op(struct fb_info* info, int copy, s32 xsrc, s32 ysrc, s32 x, s32 y, s32 w, s32 h, u32 color) { + struct pm2fb_par *par = info->par; if (!w || !h) return; @@ -1076,13 +1082,11 @@ static void pm2fb_block_op(struct pm2fb_par* par, int copy, (x<xsrc ? PM2F_INCREASE_X : 0) | (y<ysrc ? PM2F_INCREASE_Y : 0) | (copy ? 0 : PM2F_RENDER_FASTFILL)); - wait_pm2(par); } static void pm2fb_fillrect (struct fb_info *info, const struct fb_fillrect *region) { - struct pm2fb_par *par = info->par; struct fb_fillrect modded; int vxres, vyres; u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ? @@ -1116,7 +1120,7 @@ static void pm2fb_fillrect (struct fb_info *info, color |= color << 16; if(info->var.bits_per_pixel != 24) - pm2fb_block_op(par, 0, 0, 0, + pm2fb_block_op(info, 0, 0, 0, modded.dx, modded.dy, modded.width, modded.height, color); else @@ -1126,7 +1130,6 @@ static void pm2fb_fillrect (struct fb_info *info, static void pm2fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) { - struct pm2fb_par *par = info->par; struct fb_copyarea modded; u32 vxres, vyres; @@ -1156,7 +1159,7 @@ static void pm2fb_copyarea(struct fb_info *info, if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; - pm2fb_block_op(par, 1, modded.sx, modded.sy, + pm2fb_block_op(info, 1, modded.sx, modded.sy, modded.dx, modded.dy, modded.width, modded.height, 0); } @@ -1177,6 +1180,7 @@ static struct fb_ops pm2fb_ops = { .fb_fillrect = pm2fb_fillrect, .fb_copyarea = pm2fb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_sync = pm2fb_sync, }; /* diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index bd787e8..6c4dfcb0 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -1,55 +1,25 @@ /* * linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device - * - * Copyright (C) 2001 Romain Dolbeau <dolbeau@irisa.fr> + * + * Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>. + * + * Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl> + * based on pm2fb.c + * * Based on code written by: - * Sven Luther, <luther@dpt-info.u-strasbg.fr> - * Alan Hourihane, <alanh@fairlite.demon.co.uk> - * Russell King, <rmk@arm.linux.org.uk> + * Sven Luther, <luther@dpt-info.u-strasbg.fr> + * Alan Hourihane, <alanh@fairlite.demon.co.uk> + * Russell King, <rmk@arm.linux.org.uk> * Based on linux/drivers/video/skeletonfb.c: * Copyright (C) 1997 Geert Uytterhoeven * Based on linux/driver/video/pm2fb.c: - * Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) - * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) + * Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com) * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * - * $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $ - * - * CHANGELOG: - * Mon Feb 11 10:35:48 MET 2002, v 1.4.11B: Cosmetic update. - * Wed Jan 23 14:16:59 MET 2002, v 1.4.11: Preliminary 2.5.x support, patch for 2.5.2. - * Wed Nov 28 11:08:29 MET 2001, v 1.4.10: potential bug fix for SDRAM-based board, patch for 2.4.16. - * Thu Sep 20 10:24:42 MET DST 2001, v 1.4.9: sync bug fix, preliminary flatpanel support, better timings. - * Tue Aug 28 10:13:01 MET DST 2001, v 1.4.8: memory timings check, minor bug fixes. - * Wed Jul 18 19:06:14 CEST 2001, v 1.4.7: Mode fix (800x600-100, 1024x768-100 changed), using HW panning + accel bug fix. - * Mon Jun 25 10:33:56 MET DST 2001, v 1.4.6: Depth 12 fix, chip reset ioctl, moved memory erase ioctl to DEBUG. - * Wed Jun 20 11:13:08 MET DST 2001, v 1.4.5: Fixed missing blinking cursor in 8bpp, code cleaning, memory erase IOCTL. - * Mon Jun 18 16:00:27 CEST 2001, v 1.4.4: Depth 12 (RGBA 4444) support, code cleaning. - * Fri Jun 15 13:53:01 CEST 2001, v 1.4.3: Removed warnings, depth 15 support, add 'depth' option. - * Thu Jun 14 10:13:52 MET DST 2001, v 1.4.2: Fixed depth switching bug, preliminary 15bpp (RGB5551) support. - * Thu Apr 12 11:16:45 MET DST 2001, v 1.4.1B: Doc updates. - * Fri Apr 6 11:12:53 MET DST 2001, v 1.4.1: Configure.help, minor cleanup - * Thu Mar 29 10:56:50 MET DST 2001, v 1.4.0: Module & module options support (note: linux patch changed, 2.2.19 added). - * Thu Mar 15 15:30:31 MET 2001, v 1.3.2: Fixed mirroring bug on little-endian. - * Wed Mar 14 21:25:54 CET 2001, v 1.3.1: Fixed bug in BlockMove (_bmov). - * Tue Mar 13 10:53:19 MET 2001, v 1.3.0: Character drawing hardware support (in all width between 1 and 16), fixes. - * Thu Mar 8 10:20:16 MET 2001, v 1.2.2: Better J2000 support, "font:" option. - * Tue Mar 6 21:25:04 CET 2001, v 1.2.1: Better acceleration support. - * Mon Mar 5 21:54:17 CET 2001, v 1.2.0: Partial acceleration support (clear & bmove) - * Mon Mar 5 12:52:15 CET 2001, v 1.1.3: Big pan_display fix. - * Sun Mar 4 22:21:50 CET 2001, v 1.1.2: (numerous) bug fixes. - * Fri Mar 2 15:54:07 CET 2001, v 1.1.1: Might have Appian J2000 support, resource mangement in 2.4 - * Wed Feb 28 18:21:35 CET 2001, v 1.1.0: Might have multiple boards support (added, but not yest tested) - * Tue Feb 27 17:31:12 CET 2001, v 1.0.6: fixes boot-time mode select, add more default mode - * Tue Feb 27 14:01:36 CET 2001, v 1.0.5: fixes (1.0.4 was broken for 2.2), cleaning up - * Mon Feb 26 23:17:36 CET 2001, v 1.0.4: preliminary 2.4.x support, dropped (useless on pm3) partial product, more OF fix - * Mon Feb 26 20:59:05 CET 2001, v 1.0.3: No more shadow register (and wasted memory), endianess fix, use OF-preset resolution by default - * Wed Feb 21 22:09:30 CET 2001, v 1.0.2: Code cleaning for future multiboard support, better OF support, bugs fix - * Wed Feb 21 19:58:56 CET 2001, v 1.0.1: OpenFirmware support, fixed memory detection, better debug support, code cleaning - * Wed Feb 21 14:47:06 CET 2001, v 1.0.0: First working version */ #include <linux/module.h> @@ -58,856 +28,155 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/vmalloc.h> #include <linux/delay.h> -#include <linux/interrupt.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/ioport.h> -#include <linux/ctype.h> - -#include <video/fbcon.h> -#include <video/fbcon-mfb.h> -#include <video/fbcon-cfb2.h> -#include <video/fbcon-cfb4.h> -#include <video/fbcon-cfb8.h> -#include <video/fbcon-cfb16.h> -#include <video/fbcon-cfb24.h> -#include <video/fbcon-cfb32.h> -#include <video/pm3fb.h> -#include <asm/io.h> -#include <asm/uaccess.h> +#include <video/pm3fb.h> -#ifdef CONFIG_FB_OF -#include <asm/prom.h> +#if !defined(CONFIG_PCI) +#error "Only generic PCI cards supported." #endif -/* ************************************* */ -/* ***** The various "global" data ***** */ -/* ************************************* */ - -/* those will need a rework for multiple board support */ -/* Driver name */ -static const char permedia3_name[16] = "Permedia3"; - -/* the fb_par struct, mandatory */ -struct pm3fb_par { - u32 pixclock; /* pixclock in KHz */ - - u32 width; /* width of virtual screen */ - u32 height; /* height of virtual screen */ - - u32 hsstart; /* horiz. sync start */ - u32 hsend; /* horiz. sync end */ - u32 hbend; /* horiz. blank end (also gate end) */ - u32 htotal; /* total width (w/ sync & blank) */ - - u32 vsstart; /* vert. sync start */ - u32 vsend; /* vert. sync end */ - u32 vbend; /* vert. blank end */ - u32 vtotal; /* total height (w/ sync & blank) */ - - u32 stride; /* screen stride */ - u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */ - /* NOTE : unlike other pm3 stuff above, stored *after* shiftbpp. don't ask */ - u32 depth; /* screen depth (8, 12, 15, 16 or 32) */ - u32 video; /* video control (hsync,vsync) */ -}; - -/* memory timings */ -struct pm3fb_timings -{ - unsigned long caps; - unsigned long timings; - unsigned long control; - unsigned long refresh; - unsigned long powerdown; -}; -typedef enum pm3fb_timing_result { pm3fb_timing_ok, pm3fb_timing_problem, pm3fb_timing_retry } pm3fb_timing_result; -#define PM3FB_UNKNOWN_TIMING_VALUE ((unsigned long)-1) -#define PM3FB_UNKNOWN_TIMINGS { PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE } - -/* the fb_info struct, mandatory */ -struct pm3fb_info { - struct fb_info_gen gen; - unsigned long board_num; /* internal board number */ - unsigned long use_current; - struct pm3fb_par *current_par; - struct pci_dev *dev; /* PCI device */ - unsigned long board_type; /* index in the cardbase */ - unsigned char *fb_base; /* framebuffer memory base */ - u32 fb_size; /* framebuffer memory size */ - unsigned char *p_fb; /* physical address of frame buffer */ - unsigned char *v_fb; /* virtual address of frame buffer */ - unsigned char *pIOBase; /* physical address of registers region, must be rg_base or rg_base+PM2_REGS_SIZE depending on the host endianness */ - unsigned char *vIOBase; /* address of registers after ioremap() */ - struct { - u8 transp; - u8 red; - u8 green; - u8 blue; - } palette[256]; - union { -#ifdef FBCON_HAS_CFB16 - u16 cmap12[16]; /* RGBA 4444 */ - u16 cmap15[16]; /* RGBA 5551 */ - u16 cmap16[16]; /* RGBA 5650 */ -#endif -#ifdef FBCON_HAS_CFB32 - u32 cmap32[16]; +#undef PM3FB_MASTER_DEBUG +#ifdef PM3FB_MASTER_DEBUG +#define DPRINTK(a,b...) printk(KERN_DEBUG "pm3fb: %s: " a, __FUNCTION__ , ## b) +#else +#define DPRINTK(a,b...) #endif - } cmap; - struct pm3fb_timings memt; -}; - -/* regular resolution database*/ -static struct { - char name[16]; - struct pm3fb_par user_mode; -} mode_base[] __initdata = { - { - "default-800x600", { - 49500, 800, 600, 16, 96, 256, 1056, 1, 4, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, { - "1024x768-74", { - 78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, { - "1024x768-74-32", { - 78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38, - 806, 1024, 0, 32, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_32BIT}}, -/* Generated mode : "1600x1024", for the SGI 1600SW flat panel*/ - { - "SGI1600SW", { - 108000, 1600, 1024, 16, 56, 104, 1704, 3, 6, 32, - 1056, 1600, 0, 8, - PM3VideoControl_ENABLE| - PM3VideoControl_HSYNC_ACTIVE_LOW|PM3VideoControl_VSYNC_ACTIVE_LOW| - PM3VideoControl_PIXELSIZE_32BIT}}, -/* ##### auto-generated mode, by fbtimings2pm3 */ -/* Generated mode : "640x480-60" */ - { - "640x480-60", { - 25174, 640, 480, 16, 112, 160, 800, 10, 12, 45, - 525, 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-72" */ - { - "640x480-72", { - 31199, 640, 480, 24, 64, 192, 832, 9, 12, 40, 520, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-75" */ - { - "640x480-75", { - 31499, 640, 480, 16, 80, 200, 840, 1, 4, 20, 500, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-90" */ - { - "640x480-90", { - 39909, 640, 480, 32, 72, 192, 832, 25, 39, 53, 533, - 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "640x480-100" */ - { - "640x480-100", { - 44899, 640, 480, 32, 160, 208, 848, 22, 34, 51, - 531, 640, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-48-lace" */ -/* INTERLACED NOT SUPPORTED - {"800x600-48-lace", {35999, 800, 600, 80, 208, 264, 1064, 11, 23, 102, 702, 800, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "800x600-56" */ - { - "800x600-56", { - 35999, 800, 600, 24, 96, 224, 1024, 1, 3, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-60" */ - { - "800x600-60", { - 40000, 800, 600, 40, 168, 256, 1056, 1, 5, 28, 628, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-70" */ - { - "800x600-70", { - 44899, 800, 600, 24, 168, 208, 1008, 9, 21, 36, - 636, 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-72" */ - { - "800x600-72", { - 50000, 800, 600, 56, 176, 240, 1040, 37, 43, 66, - 666, 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-75" */ - { - "800x600-75", { - 49497, 800, 600, 16, 96, 256, 1056, 1, 4, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-90" */ - { - "800x600-90", { - 56637, 800, 600, 8, 72, 192, 992, 8, 19, 35, 635, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "800x600-100", from /etc/fb.modes */ -/* DISABLED, hsstart == 0 - { - "800x600-100", { - 67499, 800, 600, 0, 64, 280, 1080, 7, 11, 25, 625, - 800, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -*/ -/* Generated mode : "800x600-100", from ??? */ - { - "800x600-100", { - 69650, 800, 600, 64, 128, 288, 1088, 4, 10, 40, 640, 800, 0, 8, - PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_LOW| - PM3VideoControl_VSYNC_ACTIVE_LOW|PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1024x768-43-lace", {44899, 1024, 768, 8, 184, 240, 1264, 1, 9, 49, 817, 1024, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1024x768-60" */ - { - "1024x768-60", { - 64998, 1024, 768, 24, 160, 320, 1344, 3, 9, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-70" */ - { - "1024x768-70", { - 74996, 1024, 768, 24, 160, 304, 1328, 3, 9, 38, - 806, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-72" */ - { - "1024x768-72", { - 74996, 10224, 768, 24, 160, 264, 10488, 3, 9, 38, - 806, 10224, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-75" */ - { - "1024x768-75", { - 78746, 1024, 768, 16, 112, 288, 1312, 1, 4, 32, - 800, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-90" */ - { - "1024x768-90", { - 100000, 1024, 768, 0, 96, 288, 1312, 21, 36, 77, - 845, 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1024x768-100", from /etc/fb.modes */ -/* DISABLED, vsstart == 0 - { - "1024x768-100", { - 109998, 1024, 768, 0, 88, 368, 1392, 0, 8, 24, 792, - 1024, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -*/ -/* Generated mode : "1024x768-100", from ??? */ - { - "1024x768-100", { - 115500, 1024, 768, 32, 224, 416, 1440, 3, 13, 34, 802, 1024, 0, 8, - PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_LOW| - PM3VideoControl_VSYNC_ACTIVE_LOW|PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1152x864-43-lace", {64998, 1152, 864, 72, 200, 264, 1416, 78, 87, 191, 1055, 1152, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1152x864-47-lace" */ -/* INTERLACED NOT SUPPORTED - {"1152x864-47-lace", {64998, 1152, 864, 88, 216, 296, 1448, 30, 39, 83, 947, 1152, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1152x864-60" */ - { - "1152x864-60", { - 80000, 1152, 864, 64, 176, 304, 1456, 6, 11, 52, - 916, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-70" */ - { - "1152x864-70", { - 100000, 1152, 864, 40, 192, 360, 1512, 13, 24, 81, - 945, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-75" */ - { - "1152x864-75", { - 109998, 1152, 864, 24, 168, 312, 1464, 45, 53, 138, - 1002, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1152x864-80" */ - { - "1152x864-80", { - 109998, 1152, 864, 16, 128, 288, 1440, 30, 37, 94, - 958, 1152, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-43-lace" */ -/* INTERLACED NOT SUPPORTED - {"1280x1024-43-lace", {80000, 1024, 1024, 80, 160, 320, 1344, 50, 60, 125, 1149, 1024, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1280x1024-47-lace" */ -/* INTERLACED NOT SUPPORTED - {"1280x1024-47-lace", {80000, 1280, 1024, 80, 160, 320, 1600, 1, 11, 29, 1053, 1280, 0, 8, PM3VideoControl_ENABLE|PM3VideoControl_HSYNC_ACTIVE_HIGH|PM3VideoControl_VSYNC_ACTIVE_HIGH|PM3VideoControl_PIXELSIZE_8BIT}}, - INTERLACED NOT SUPPORTED */ -/* Generated mode : "1280x1024-60" */ - { - "1280x1024-60", { - 107991, 1280, 1024, 48, 160, 408, 1688, 1, 4, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-70" */ - { - "1280x1024-70", { - 125992, 1280, 1024, 80, 192, 408, 1688, 1, 6, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-74" */ - { - "1280x1024-74", { - 134989, 1280, 1024, 32, 176, 432, 1712, 0, 30, 40, - 1064, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1280x1024-75" */ - { - "1280x1024-75", { - 134989, 1280, 1024, 16, 160, 408, 1688, 1, 4, 42, - 1066, 1280, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_HIGH - | - PM3VideoControl_VSYNC_ACTIVE_HIGH - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-60" */ - { - "1600x1200-60", { - 155981, 1600, 1200, 32, 192, 448, 2048, 10, 18, 70, - 1270, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-66" */ - { - "1600x1200-66", { - 171998, 1600, 1200, 40, 176, 480, 2080, 3, 6, 53, - 1253, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* Generated mode : "1600x1200-76" */ - { - "1600x1200-76", { - 197980, 1600, 1200, 40, 176, 480, 2080, 3, 8, 50, - 1250, 1600, 0, 8, - PM3VideoControl_ENABLE | - PM3VideoControl_HSYNC_ACTIVE_LOW - | - PM3VideoControl_VSYNC_ACTIVE_LOW - | PM3VideoControl_PIXELSIZE_8BIT}}, -/* ##### end of auto-generated mode */ - { - "\0",} -}; -/* more mandatory stuff (see skeletonfb.c + framebuffer driver HOWTO */ -static struct pm3fb_info fb_info[PM3_MAX_BOARD]; -static struct pm3fb_par current_par[PM3_MAX_BOARD]; -static int current_par_valid[PM3_MAX_BOARD]; -/* to allow explicit filtering of board */ -short bus[PM3_MAX_BOARD]; -short slot[PM3_MAX_BOARD]; -short func[PM3_MAX_BOARD]; -short disable[PM3_MAX_BOARD]; -short noaccel[PM3_MAX_BOARD]; -char fontn[PM3_MAX_BOARD][PM3_FONTNAME_SIZE]; -short depth[PM3_MAX_BOARD]; -short flatpanel[PM3_MAX_BOARD]; -static struct display disp[PM3_MAX_BOARD]; -static char g_options[PM3_OPTIONS_SIZE] __initdata = "pm3fb,dummy"; -short printtimings = 0; -short forcesize[PM3_MAX_BOARD]; - -/* ********************* */ -/* ***** prototype ***** */ -/* ********************* */ -/* card-specific */ -static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info); -/* permedia3-specific */ -static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info); -static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info); -static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info); -static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info, - unsigned long r); -static unsigned long pm3fb_CalculateClock(struct pm3fb_info *l_fb_info, unsigned long reqclock, /* In kHz units */ - unsigned long refclock, /* In kHz units */ - unsigned char *prescale, /* ClkPreScale */ - unsigned char *feedback, /* ClkFeedBackScale */ - unsigned char *postscale - /* ClkPostScale */ ); -static void pm3fb_clear_memory(struct pm3fb_info *l_fb_info, u32 cc); -static void pm3fb_clear_colormap(struct pm3fb_info *l_fb_info, unsigned char r, unsigned char g, unsigned char b); -static void pm3fb_common_init(struct pm3fb_info *l_fb_info); -static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v); -static int pm3fb_Unshiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v); -static void pm3fb_mapIO(struct pm3fb_info *l_fb_info); -static void pm3fb_unmapIO(struct pm3fb_info *l_fb_info); -#if defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) -static void pm3fb_show_cur_mode(struct pm3fb_info *l_fb_info); -#endif -static void pm3fb_show_cur_timing(struct pm3fb_info *l_fb_info); -static void pm3fb_write_mode(struct pm3fb_info *l_fb_info); -static void pm3fb_read_mode(struct pm3fb_info *l_fb_info, - struct pm3fb_par *curpar); -static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info); -/* accelerated permedia3-specific */ -#ifdef PM3FB_USE_ACCEL -static void pm3fb_wait_pm3(struct pm3fb_info *l_fb_info); -static void pm3fb_init_engine(struct pm3fb_info *l_fb_info); -#ifdef FBCON_HAS_CFB32 -static void pm3fb_cfb32_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb32_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static void pm3fb_cfb16_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb16_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static void pm3fb_cfb8_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width); -static void pm3fb_cfb8_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only); -#endif /* FBCON_HAS_CFB8 */ -#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32) -static void pm3fb_cfbX_bmove(struct display *p, - int sy, int sx, - int dy, int dx, int height, int width); -static void pm3fb_cfbX_putc(struct vc_data *conp, struct display *p, - int c, int yy, int xx); -static void pm3fb_cfbX_putcs(struct vc_data *conp, struct display *p, - const unsigned short *s, int count, int yy, - int xx); -static void pm3fb_cfbX_revc(struct display *p, int xx, int yy); -#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */ -#endif /* PM3FB_USE_ACCEL */ -/* pre-init */ -static void pm3fb_mode_setup(char *mode, unsigned long board_num); -static void pm3fb_pciid_setup(char *pciid, unsigned long board_num); -static char *pm3fb_boardnum_setup(char *options, unsigned long *bn); -static void pm3fb_real_setup(char *options); -/* fbdev */ -static int pm3fb_encode_fix(struct fb_fix_screeninfo *fix, - const void *par, struct fb_info_gen *info); -static int pm3fb_decode_var(const struct fb_var_screeninfo *var, - void *par, struct fb_info_gen *info); -static void pm3fb_encode_depth(struct fb_var_screeninfo *var, long d); -static int pm3fb_encode_var(struct fb_var_screeninfo *var, - const void *par, struct fb_info_gen *info); -static void pm3fb_get_par(void *par, struct fb_info_gen *info); -static void pm3fb_set_par(const void *par, struct fb_info_gen *info); -static void pm3fb_set_color(struct pm3fb_info *l_fb_info, - unsigned char regno, unsigned char r, - unsigned char g, unsigned char b); -static int pm3fb_getcolreg(unsigned regno, unsigned *red, unsigned *green, - unsigned *blue, unsigned *transp, - struct fb_info *info); -static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, - struct fb_info *info); -static int pm3fb_blank(int blank_mode, struct fb_info_gen *info); -static void pm3fb_set_disp(const void *par, struct display *disp, - struct fb_info_gen *info); -static void pm3fb_detect(void); -static int pm3fb_pan_display(const struct fb_var_screeninfo *var, - struct fb_info_gen *info); -static int pm3fb_ioctl(struct fb_info *info, u_int cmd, u_long arg); - - -/* the struct that hold them together */ -struct fbgen_hwswitch pm3fb_switch = { - pm3fb_detect, pm3fb_encode_fix, pm3fb_decode_var, pm3fb_encode_var, - pm3fb_get_par, pm3fb_set_par, pm3fb_getcolreg, - pm3fb_pan_display, pm3fb_blank, pm3fb_set_disp -}; +/* + * Driver data + */ +static char *mode_option __devinitdata; -static struct fb_ops pm3fb_ops = { - .owner = THIS_MODULE, - .fb_get_fix = fbgen_get_fix, - .fb_get_var = fbgen_get_var, - .fb_set_var = fbgen_set_var, - .fb_get_cmap = fbgen_get_cmap, - .fb_set_cmap = fbgen_set_cmap, - .fb_setcolreg = pm3fb_setcolreg, - .fb_pan_display =fbgen_pan_display, - .fb_blank = fbgen_blank, - .fb_ioctl = pm3fb_ioctl, -}; +/* + * If your driver supports multiple boards, you should make the + * below data types arrays, or allocate them dynamically (using kmalloc()). + */ -#ifdef PM3FB_USE_ACCEL -#ifdef FBCON_HAS_CFB32 -static struct display_switch pm3fb_cfb32 = { - fbcon_cfb32_setup, pm3fb_cfbX_bmove, pm3fb_cfb32_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb32_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static struct display_switch pm3fb_cfb16 = { - fbcon_cfb16_setup, pm3fb_cfbX_bmove, pm3fb_cfb16_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb16_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static struct display_switch pm3fb_cfb8 = { - fbcon_cfb8_setup, pm3fb_cfbX_bmove, pm3fb_cfb8_clear, - pm3fb_cfbX_putc, pm3fb_cfbX_putcs, pm3fb_cfbX_revc, - NULL /* cursor() */ , NULL /* set_font() */ , - pm3fb_cfb8_clear_margins, - FONTWIDTHRANGE(1, 16) /* true only if accelerated... */ -}; -#endif /* FBCON_HAS_CFB8 */ -#endif /* PM3FB_USE_ACCEL */ - -/* ****************************** */ -/* ***** card-specific data ***** */ -/* ****************************** */ -struct pm3fb_card_timings { - unsigned long memsize; /* 0 for last value (i.e. default) */ - struct pm3fb_timings memt; +/* + * This structure defines the hardware state of the graphics card. Normally + * you place this in a header file in linux/include/video. This file usually + * also includes register information. That allows other driver subsystems + * and userland applications the ability to use the same header file to + * avoid duplicate work and easy porting of software. + */ +struct pm3_par { + unsigned char __iomem *v_regs;/* virtual address of p_regs */ + u32 video; /* video flags before blanking */ + u32 base; /* screen base (xoffset+yoffset) in 128 bits unit */ + u32 palette[16]; }; -static struct pm3fb_card_timings t_FormacProFormance3[] = { - { 16, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} }, - { 0, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} } /* from 16 MB PF3 */ +/* + * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo + * if we don't use modedb. If we do use modedb see pm3fb_init how to use it + * to get a fb_var_screeninfo. Otherwise define a default var as well. + */ +static struct fb_fix_screeninfo pm3fb_fix __devinitdata = { + .id = "Permedia3", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, }; -static struct pm3fb_card_timings t_AppianJeronimo2000[] = { - { 32, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} }, - { 0, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} } /* from 32MB J2000 */ -}; +/* + * Utility functions + */ -static struct pm3fb_card_timings t_3DLabsOxygenVX1[] = { - { 32, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} }, - { 0, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} } /* from 32MB VX1 */ -}; +static inline u32 PM3_READ_REG(struct pm3_par *par, s32 off) +{ + return fb_readl(par->v_regs + off); +} -static struct { - char cardname[32]; /* recognized card name */ - u16 subvendor; /* subvendor of the card */ - u16 subdevice; /* subdevice of the card */ - u8 func; /* function of the card to which the extra init apply */ - void (*specific_setup)(struct pm3fb_info *l_fb_info); /* card/func specific setup, done before _any_ FB access */ - struct pm3fb_card_timings *c_memt; /* defauls timings for the boards */ -} cardbase[] = { - { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, NULL }, - { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL, - t_AppianJeronimo2000 - }, - { "Appian Jeronimo 2000 head 2", 0x1097, 0x3d32, 2, pm3fb_j2000_setup, - t_AppianJeronimo2000 - }, - { "Formac ProFormance 3", PCI_VENDOR_ID_3DLABS, 0x000a, 0, NULL, /* Formac use 3DLabs ID ?!? */ - t_FormacProFormance3 - }, - { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, NULL }, - { "3DLabs Oxygen VX1 PCI", PCI_VENDOR_ID_3DLABS, 0x0121, 0, NULL, - t_3DLabsOxygenVX1 - }, - { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, NULL }, - { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, NULL }, - { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, NULL }, - { "\0", 0x0, 0x0, 0, NULL, NULL } -}; +static inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v) +{ + fb_writel(v, par->v_regs + off); +} -/* ********************************** */ -/* ***** card-specific function ***** */ -/* ********************************** */ -static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info) -{ /* the appian j2000 require more initialization of the second head */ - /* l_fb_info must point to the _second_ head of the J2000 */ - - DTRACE; - - l_fb_info->memt = t_AppianJeronimo2000[0].memt; /* 32 MB, first and only j2000 ? */ - - pm3fb_write_memory_timings(l_fb_info); +static inline void PM3_WAIT(struct pm3_par *par, u32 n) +{ + while (PM3_READ_REG(par, PM3InFIFOSpace) < n); } -/* *************************************** */ -/* ***** permedia3-specific function ***** */ -/* *************************************** */ -static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_SLOW_WRITE_REG(struct pm3_par *par, s32 off, u32 v) { - l_fb_info->memt.caps = PM3_READ_REG(PM3LocalMemCaps); - l_fb_info->memt.timings = PM3_READ_REG(PM3LocalMemTimings); - l_fb_info->memt.control = PM3_READ_REG(PM3LocalMemControl); - l_fb_info->memt.refresh = PM3_READ_REG(PM3LocalMemRefresh); - l_fb_info->memt.powerdown = PM3_READ_REG(PM3LocalMemPowerDown); - - if ((l_fb_info->memt.caps == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.timings == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.control == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.refresh == PM3FB_UNKNOWN_TIMING_VALUE) || - (l_fb_info->memt.powerdown == PM3FB_UNKNOWN_TIMING_VALUE)) - { - printk(KERN_ERR "pm3fb: invalid memory timings in permedia3 board #%ld\n", l_fb_info->board_num); - return(pm3fb_try_memory_timings(l_fb_info)); + if (par->v_regs) { + mb(); + PM3_WAIT(par, 1); + wmb(); + PM3_WRITE_REG(par, off, v); } - return(pm3fb_timing_ok); } -static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_SET_INDEX(struct pm3_par *par, unsigned index) { - if (cardbase[l_fb_info->board_type].c_memt) - { - int i = 0, done = 0; - while (!done) - { - if ((cardbase[l_fb_info->board_type].c_memt[i].memsize == l_fb_info->fb_size) - || !(cardbase[l_fb_info->board_type].c_memt[i].memsize)) - { /* will use the 0-sized timings by default */ - done = 1; - l_fb_info->memt = cardbase[l_fb_info->board_type].c_memt[i].memt; - printk(KERN_WARNING "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s, %ld MB)\n", - l_fb_info->board_num, - cardbase[l_fb_info->board_type].cardname, - cardbase[l_fb_info->board_type].c_memt[i].memsize); - pm3fb_write_memory_timings(l_fb_info); - return(pm3fb_timing_retry); - } - i++; - } - } else - return(pm3fb_timing_problem); - return(pm3fb_timing_ok); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexHigh, (index >> 8) & 0xff); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexLow, index & 0xff); } -static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info) +static inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v) { - unsigned char m, n, p; - unsigned long clockused; - - PM3_SLOW_WRITE_REG(PM3LocalMemCaps, l_fb_info->memt.caps); - PM3_SLOW_WRITE_REG(PM3LocalMemTimings, l_fb_info->memt.timings); - PM3_SLOW_WRITE_REG(PM3LocalMemControl, l_fb_info->memt.control); - PM3_SLOW_WRITE_REG(PM3LocalMemRefresh, l_fb_info->memt.refresh); - PM3_SLOW_WRITE_REG(PM3LocalMemPowerDown, l_fb_info->memt.powerdown); - - clockused = - pm3fb_CalculateClock(l_fb_info, 2 * 105000, PM3_REF_CLOCK, &m, - &n, &p); - - PM3_WRITE_DAC_REG(PM3RD_KClkPreScale, m); - PM3_WRITE_DAC_REG(PM3RD_KClkFeedbackScale, n); - PM3_WRITE_DAC_REG(PM3RD_KClkPostScale, p); - PM3_WRITE_DAC_REG(PM3RD_KClkControl, - PM3RD_KClkControl_STATE_RUN | - PM3RD_KClkControl_SOURCE_PLL | - PM3RD_KClkControl_ENABLE); - PM3_WRITE_DAC_REG(PM3RD_MClkControl, - PM3RD_MClkControl_STATE_RUN | - PM3RD_MClkControl_SOURCE_KCLK | - PM3RD_MClkControl_ENABLE); - PM3_WRITE_DAC_REG(PM3RD_SClkControl, - PM3RD_SClkControl_STATE_RUN | - PM3RD_SClkControl_SOURCE_PCLK | - PM3RD_SClkControl_ENABLE); + PM3_SET_INDEX(par, r); + wmb(); + PM3_WRITE_REG(par, PM3RD_IndexedData, v); } -static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info, - unsigned long r) +static inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno, + unsigned char r, unsigned char g, unsigned char b) { - DASSERT((l_fb_info->vIOBase != (unsigned char *) (-1)), - "l_fb_info->vIOBase mapped in read dac reg\n"); - PM3_SET_INDEX(r); - mb(); - return (PM3_READ_REG(PM3RD_IndexedData)); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, r); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, g); + PM3_SLOW_WRITE_REG(par, PM3RD_PaletteData, b); +} + +static void pm3fb_clear_colormap(struct pm3_par *par, + unsigned char r, unsigned char g, unsigned char b) +{ + int i; + + for (i = 0; i < 256 ; i++) /* fill color map with white */ + pm3fb_set_color(par, i, r, g, b); + } /* Calculating various clock parameter */ -static unsigned long pm3fb_CalculateClock(struct pm3fb_info *l_fb_info, unsigned long reqclock, /* In kHz units */ - unsigned long refclock, /* In kHz units */ - unsigned char *prescale, /* ClkPreScale */ - unsigned char *feedback, /* ClkFeedBackScale */ - unsigned char *postscale - /* ClkPostScale */ ) +static void pm3fb_calculate_clock(unsigned long reqclock, + unsigned char *prescale, + unsigned char *feedback, + unsigned char *postscale) { int f, pre, post; unsigned long freq; long freqerr = 1000; - unsigned long actualclock = 0; - - DTRACE; + long currerr; for (f = 1; f < 256; f++) { for (pre = 1; pre < 256; pre++) { for (post = 0; post < 5; post++) { - freq = - ((2 * refclock * f) / - (pre * (1 << post))); - if ((reqclock > freq - freqerr) - && (reqclock < freq + freqerr)) { - freqerr = - (reqclock > - freq) ? reqclock - - freq : freq - reqclock; + freq = ((2*PM3_REF_CLOCK * f) >> post) / pre; + currerr = (reqclock > freq) + ? reqclock - freq + : freq - reqclock; + if (currerr < freqerr) { + freqerr = currerr; *feedback = f; *prescale = pre; *postscale = post; - actualclock = freq; } } } } - - return (actualclock); } -static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v) +static inline int pm3fb_shift_bpp(unsigned long depth, int v) { - DTRACE; - switch (depth) { case 8: return (v >> 4); @@ -918,181 +187,59 @@ static int pm3fb_Shiftbpp(struct pm3fb_info *l_fb_info, case 32: return (v >> 2); } - DPRINTK(1, "Unsupported depth %ld\n", depth); - return (0); -} - -static int pm3fb_Unshiftbpp(struct pm3fb_info *l_fb_info, - unsigned long depth, int v) -{ - DTRACE; - - switch (depth) { - case 8: - return (v << 4); - case 12: - case 15: - case 16: - return (v << 3); - case 32: - return (v << 2); - } - DPRINTK(1, "Unsupported depth %ld\n", depth); - return (0); -} - -static void pm3fb_mapIO(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - l_fb_info->vIOBase = - ioremap((unsigned long) l_fb_info->pIOBase, PM3_REGS_SIZE); - l_fb_info->v_fb = - ioremap((unsigned long) l_fb_info->p_fb, l_fb_info->fb_size); - DPRINTK(2, "IO mapping : IOBase %lx / %lx, fb %lx / %lx\n", - (unsigned long) l_fb_info->pIOBase, - (unsigned long) l_fb_info->vIOBase, - (unsigned long) l_fb_info->p_fb, - (unsigned long) l_fb_info->v_fb); -} - -static void pm3fb_unmapIO(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - iounmap(l_fb_info->vIOBase); - iounmap(l_fb_info->v_fb); - l_fb_info->vIOBase = (unsigned char *) -1; - l_fb_info->v_fb = (unsigned char *) -1; -} - -#if defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) -static void pm3fb_show_cur_mode(struct pm3fb_info *l_fb_info) -{ - DPRINTK(2, "PM3Aperture0: 0x%08x\n", PM3_READ_REG(PM3Aperture0)); - DPRINTK(2, "PM3Aperture1: 0x%08x\n", PM3_READ_REG(PM3Aperture1)); - DPRINTK(2, "PM3ByAperture1Mode: 0x%08x\n", - PM3_READ_REG(PM3ByAperture1Mode)); - DPRINTK(2, "PM3ByAperture2Mode: 0x%08x\n", - PM3_READ_REG(PM3ByAperture2Mode)); - DPRINTK(2, "PM3ChipConfig: 0x%08x\n", PM3_READ_REG(PM3ChipConfig)); - DPRINTK(2, "PM3FIFODis: 0x%08x\n", PM3_READ_REG(PM3FIFODis)); - DPRINTK(2, "PM3HTotal: 0x%08x\n", PM3_READ_REG(PM3HTotal)); - DPRINTK(2, "PM3HbEnd: 0x%08x\n", PM3_READ_REG(PM3HbEnd)); - DPRINTK(2, "PM3HgEnd: 0x%08x\n", PM3_READ_REG(PM3HgEnd)); - DPRINTK(2, "PM3HsEnd: 0x%08x\n", PM3_READ_REG(PM3HsEnd)); - DPRINTK(2, "PM3HsStart: 0x%08x\n", PM3_READ_REG(PM3HsStart)); - DPRINTK(2, "PM3MemBypassWriteMask: 0x%08x\n", - PM3_READ_REG(PM3MemBypassWriteMask)); - DPRINTK(2, "PM3RD_IndexControl: 0x%08x\n", - PM3_READ_REG(PM3RD_IndexControl)); - DPRINTK(2, "PM3ScreenBase: 0x%08x\n", PM3_READ_REG(PM3ScreenBase)); - DPRINTK(2, "PM3ScreenStride: 0x%08x\n", - PM3_READ_REG(PM3ScreenStride)); - DPRINTK(2, "PM3VClkCtl: 0x%08x\n", PM3_READ_REG(PM3VClkCtl)); - DPRINTK(2, "PM3VTotal: 0x%08x\n", PM3_READ_REG(PM3VTotal)); - DPRINTK(2, "PM3VbEnd: 0x%08x\n", PM3_READ_REG(PM3VbEnd)); - DPRINTK(2, "PM3VideoControl: 0x%08x\n", - PM3_READ_REG(PM3VideoControl)); - DPRINTK(2, "PM3VsEnd: 0x%08x\n", PM3_READ_REG(PM3VsEnd)); - DPRINTK(2, "PM3VsStart: 0x%08x\n", PM3_READ_REG(PM3VsStart)); - - DPRINTK(2, "PM3RD_ColorFormat: %ld\n", - PM3_READ_DAC_REG(PM3RD_ColorFormat)); - DPRINTK(2, "PM3RD_DACControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_DACControl)); - DPRINTK(2, "PM3RD_DClk0FeedbackScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0FeedbackScale)); - DPRINTK(2, "PM3RD_DClk0PostScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0PostScale)); - DPRINTK(2, "PM3RD_DClk0PreScale: %ld\n", - PM3_READ_DAC_REG(PM3RD_DClk0PreScale)); - DPRINTK(2, "[not set] PM3RD_IndexControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_IndexControl)); - DPRINTK(2, "PM3RD_MiscControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_MiscControl)); - DPRINTK(2, "PM3RD_PixelSize: %ld\n", - PM3_READ_DAC_REG(PM3RD_PixelSize)); - DPRINTK(2, "PM3RD_SyncControl: %ld\n", - PM3_READ_DAC_REG(PM3RD_SyncControl)); -} - -#endif /* defined(PM3FB_MASTER_DEBUG) && (PM3FB_MASTER_DEBUG >= 2) */ -static void pm3fb_show_cur_timing(struct pm3fb_info *l_fb_info) -{ - u16 subvendor, subdevice; - - if ((!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - && - (!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_ID, &subdevice))) { - /* well, nothing... */ - } else { - subvendor = subdevice = (u16)-1; - } - - printk(KERN_INFO "pm3fb: memory timings for board #%ld (subvendor: 0x%hx, subdevice: 0x%hx)\n", l_fb_info->board_num, subvendor, subdevice); - printk(KERN_INFO " PM3LocalMemCaps: 0x%08x\n", - PM3_READ_REG(PM3LocalMemCaps)); - printk(KERN_INFO " PM3LocalMemTimings: 0x%08x\n", - PM3_READ_REG(PM3LocalMemTimings)); - printk(KERN_INFO " PM3LocalMemControl: 0x%08x\n", - PM3_READ_REG(PM3LocalMemControl)); - printk(KERN_INFO " PM3LocalMemRefresh: 0x%08x\n", - PM3_READ_REG(PM3LocalMemRefresh)); - printk(KERN_INFO " PM3LocalMemPowerDown: 0x%08x\n", - PM3_READ_REG(PM3LocalMemPowerDown)); + DPRINTK("Unsupported depth %ld\n", depth); + return 0; } /* write the mode to registers */ -static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) +static void pm3fb_write_mode(struct fb_info *info) { + struct pm3_par *par = info->par; char tempsync = 0x00, tempmisc = 0x00; - DTRACE; - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3Aperture0, 0x00000000); - PM3_SLOW_WRITE_REG(PM3Aperture1, 0x00000000); - PM3_SLOW_WRITE_REG(PM3FIFODis, 0x00000007); - - PM3_SLOW_WRITE_REG(PM3HTotal, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->htotal - - 1)); - PM3_SLOW_WRITE_REG(PM3HsEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hsend)); - PM3_SLOW_WRITE_REG(PM3HsStart, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par-> + const u32 hsstart = info->var.right_margin; + const u32 hsend = hsstart + info->var.hsync_len; + const u32 hbend = hsend + info->var.left_margin; + const u32 xres = (info->var.xres + 31) & ~31; + const u32 htotal = xres + hbend; + const u32 vsstart = info->var.lower_margin; + const u32 vsend = vsstart + info->var.vsync_len; + const u32 vbend = vsend + info->var.upper_margin; + const u32 vtotal = info->var.yres + vbend; + const u32 width = (info->var.xres_virtual + 7) & ~7; + + PM3_SLOW_WRITE_REG(par, PM3MemBypassWriteMask, 0xffffffff); + PM3_SLOW_WRITE_REG(par, PM3Aperture0, 0x00000000); + PM3_SLOW_WRITE_REG(par, PM3Aperture1, 0x00000000); + PM3_SLOW_WRITE_REG(par, PM3FIFODis, 0x00000007); + + PM3_SLOW_WRITE_REG(par, PM3HTotal, + pm3fb_shift_bpp(info->var.bits_per_pixel, + htotal - 1)); + PM3_SLOW_WRITE_REG(par, PM3HsEnd, + pm3fb_shift_bpp(info->var.bits_per_pixel, + hsend)); + PM3_SLOW_WRITE_REG(par, PM3HsStart, + pm3fb_shift_bpp(info->var.bits_per_pixel, hsstart)); - PM3_SLOW_WRITE_REG(PM3HbEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hbend)); - PM3_SLOW_WRITE_REG(PM3HgEnd, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->hbend)); - PM3_SLOW_WRITE_REG(PM3ScreenStride, - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - l_fb_info->current_par->stride)); - PM3_SLOW_WRITE_REG(PM3VTotal, l_fb_info->current_par->vtotal - 1); - PM3_SLOW_WRITE_REG(PM3VsEnd, l_fb_info->current_par->vsend - 1); - PM3_SLOW_WRITE_REG(PM3VsStart, - l_fb_info->current_par->vsstart - 1); - PM3_SLOW_WRITE_REG(PM3VbEnd, l_fb_info->current_par->vbend); - - switch (l_fb_info->current_par->depth) { + PM3_SLOW_WRITE_REG(par, PM3HbEnd, + pm3fb_shift_bpp(info->var.bits_per_pixel, + hbend)); + PM3_SLOW_WRITE_REG(par, PM3HgEnd, + pm3fb_shift_bpp(info->var.bits_per_pixel, + hbend)); + PM3_SLOW_WRITE_REG(par, PM3ScreenStride, + pm3fb_shift_bpp(info->var.bits_per_pixel, + width)); + PM3_SLOW_WRITE_REG(par, PM3VTotal, vtotal - 1); + PM3_SLOW_WRITE_REG(par, PM3VsEnd, vsend - 1); + PM3_SLOW_WRITE_REG(par, PM3VsStart, vsstart - 1); + PM3_SLOW_WRITE_REG(par, PM3VbEnd, vbend); + + switch (info->var.bits_per_pixel) { case 8: - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_8BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_8BIT); break; @@ -1100,15 +247,15 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) case 15: case 16: #ifndef __BIG_ENDIAN - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_16BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_16BIT); #else - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_16BIT | PM3ByApertureMode_BYTESWAP_BADC); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_16BIT | PM3ByApertureMode_BYTESWAP_BADC); #endif /* ! __BIG_ENDIAN */ @@ -1116,23 +263,23 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) case 32: #ifndef __BIG_ENDIAN - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_32BIT); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_32BIT); #else - PM3_SLOW_WRITE_REG(PM3ByAperture1Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture1Mode, PM3ByApertureMode_PIXELSIZE_32BIT | PM3ByApertureMode_BYTESWAP_DCBA); - PM3_SLOW_WRITE_REG(PM3ByAperture2Mode, + PM3_SLOW_WRITE_REG(par, PM3ByAperture2Mode, PM3ByApertureMode_PIXELSIZE_32BIT | PM3ByApertureMode_BYTESWAP_DCBA); #endif /* ! __BIG_ENDIAN */ break; default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); + DPRINTK("Unsupported depth %d\n", + info->var.bits_per_pixel); break; } @@ -1143,95 +290,86 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) * sync options in PM3RD_SyncControl. --rmk */ { - unsigned int video = l_fb_info->current_par->video; + unsigned int video = par->video; video &= ~(PM3VideoControl_HSYNC_MASK | PM3VideoControl_VSYNC_MASK); video |= PM3VideoControl_HSYNC_ACTIVE_HIGH | PM3VideoControl_VSYNC_ACTIVE_HIGH; - PM3_SLOW_WRITE_REG(PM3VideoControl, video); + PM3_SLOW_WRITE_REG(par, PM3VideoControl, video); } - PM3_SLOW_WRITE_REG(PM3VClkCtl, - (PM3_READ_REG(PM3VClkCtl) & 0xFFFFFFFC)); - PM3_SLOW_WRITE_REG(PM3ScreenBase, l_fb_info->current_par->base); - PM3_SLOW_WRITE_REG(PM3ChipConfig, - (PM3_READ_REG(PM3ChipConfig) & 0xFFFFFFFD)); + PM3_SLOW_WRITE_REG(par, PM3VClkCtl, + (PM3_READ_REG(par, PM3VClkCtl) & 0xFFFFFFFC)); + PM3_SLOW_WRITE_REG(par, PM3ScreenBase, par->base); + PM3_SLOW_WRITE_REG(par, PM3ChipConfig, + (PM3_READ_REG(par, PM3ChipConfig) & 0xFFFFFFFD)); { - unsigned char m; /* ClkPreScale */ - unsigned char n; /* ClkFeedBackScale */ - unsigned char p; /* ClkPostScale */ - (void)pm3fb_CalculateClock(l_fb_info, l_fb_info->current_par->pixclock, PM3_REF_CLOCK, &m, &n, &p); - - DPRINTK(2, - "Pixclock: %d, Pre: %d, Feedback: %d, Post: %d\n", - l_fb_info->current_par->pixclock, (int) m, (int) n, - (int) p); - - PM3_WRITE_DAC_REG(PM3RD_DClk0PreScale, m); - PM3_WRITE_DAC_REG(PM3RD_DClk0FeedbackScale, n); - PM3_WRITE_DAC_REG(PM3RD_DClk0PostScale, p); + unsigned char uninitialized_var(m); /* ClkPreScale */ + unsigned char uninitialized_var(n); /* ClkFeedBackScale */ + unsigned char uninitialized_var(p); /* ClkPostScale */ + unsigned long pixclock = PICOS2KHZ(info->var.pixclock); + + (void)pm3fb_calculate_clock(pixclock, &m, &n, &p); + + DPRINTK("Pixclock: %ld, Pre: %d, Feedback: %d, Post: %d\n", + pixclock, (int) m, (int) n, (int) p); + + PM3_WRITE_DAC_REG(par, PM3RD_DClk0PreScale, m); + PM3_WRITE_DAC_REG(par, PM3RD_DClk0FeedbackScale, n); + PM3_WRITE_DAC_REG(par, PM3RD_DClk0PostScale, p); } /* - PM3_WRITE_DAC_REG(PM3RD_IndexControl, 0x00); + PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00); */ /* - PM3_SLOW_WRITE_REG(PM3RD_IndexControl, 0x00); + PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00); */ - if ((l_fb_info->current_par->video & PM3VideoControl_HSYNC_MASK) == + if ((par->video & PM3VideoControl_HSYNC_MASK) == PM3VideoControl_HSYNC_ACTIVE_HIGH) tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH; - if ((l_fb_info->current_par->video & PM3VideoControl_VSYNC_MASK) == + if ((par->video & PM3VideoControl_VSYNC_MASK) == PM3VideoControl_VSYNC_ACTIVE_HIGH) tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH; - - PM3_WRITE_DAC_REG(PM3RD_SyncControl, tempsync); - DPRINTK(2, "PM3RD_SyncControl: %d\n", tempsync); - - if (flatpanel[l_fb_info->board_num]) - { - PM3_WRITE_DAC_REG(PM3RD_DACControl, PM3RD_DACControl_BLANK_PEDESTAL_ENABLE); - PM3_WAIT(2); - PM3_WRITE_REG(PM3VSConfiguration, 0x06); - PM3_WRITE_REG(0x5a00, 1 << 14); /* black magic... */ - tempmisc = PM3RD_MiscControl_VSB_OUTPUT_ENABLE; - } - else - PM3_WRITE_DAC_REG(PM3RD_DACControl, 0x00); - switch (l_fb_info->current_par->depth) { + PM3_WRITE_DAC_REG(par, PM3RD_SyncControl, tempsync); + DPRINTK("PM3RD_SyncControl: %d\n", tempsync); + + PM3_WRITE_DAC_REG(par, PM3RD_DACControl, 0x00); + + switch (info->var.bits_per_pixel) { case 8: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_8_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_CI8_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW); tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; case 12: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_4444_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; - break; + break; case 15: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_5551_FRONT_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; - break; + break; case 16: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_16_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_565_FRONT_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW | PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE); @@ -1239,1936 +377,280 @@ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info) PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; case 32: - PM3_WRITE_DAC_REG(PM3RD_PixelSize, + PM3_WRITE_DAC_REG(par, PM3RD_PixelSize, PM3RD_PixelSize_32_BIT_PIXELS); - PM3_WRITE_DAC_REG(PM3RD_ColorFormat, + PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat, PM3RD_ColorFormat_8888_COLOR | PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW); tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE | PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE; break; } - PM3_WRITE_DAC_REG(PM3RD_MiscControl, tempmisc); - - PM3_SHOW_CUR_MODE; + PM3_WRITE_DAC_REG(par, PM3RD_MiscControl, tempmisc); } -static void pm3fb_read_mode(struct pm3fb_info *l_fb_info, - struct pm3fb_par *curpar) -{ - unsigned long pixsize1, pixsize2, clockused; - unsigned long pre, feedback, post; - - DTRACE; - - clockused = PM3_READ_REG(PM3VClkCtl); +/* + * hardware independent functions + */ +int pm3fb_init(void); +int pm3fb_setup(char*); - switch (clockused) { - case 3: - pre = PM3_READ_DAC_REG(PM3RD_DClk3PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk3FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk3PostScale); +static int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 lpitch; - DPRINTK(2, - "DClk3 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + var->transp.offset = 0; + var->transp.length = 0; + switch(var->bits_per_pixel) { + case 8: + var->red.length = var->green.length = var->blue.length = 8; + var->red.offset = var->green.offset = var->blue.offset = 0; break; - case 2: - pre = PM3_READ_DAC_REG(PM3RD_DClk2PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk2FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk2PostScale); - - DPRINTK(2, - "DClk2 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + case 12: + var->red.offset = 8; + var->red.length = 4; + var->green.offset = 4; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + var->transp.offset = 12; + var->transp.length = 4; + case 15: + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 15; + var->transp.length = 1; break; - case 1: - pre = PM3_READ_DAC_REG(PM3RD_DClk1PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk1FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk1PostScale); - - DPRINTK(2, - "DClk1 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + case 16: + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; break; - case 0: - pre = PM3_READ_DAC_REG(PM3RD_DClk0PreScale); - feedback = PM3_READ_DAC_REG(PM3RD_DClk0FeedbackScale); - post = PM3_READ_DAC_REG(PM3RD_DClk0PostScale); - - DPRINTK(2, - "DClk0 parameter: Pre: %ld, Feedback: %ld, Post: %ld ; giving pixclock: %ld\n", - pre, feedback, post, PM3_SCALE_TO_CLOCK(pre, - feedback, - post)); + case 32: + var->transp.offset = 24; + var->transp.length = 8; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; break; default: - pre = feedback = post = 0; - DPRINTK(1, "Unknowk D clock used : %ld\n", clockused); - break; - } - - curpar->pixclock = PM3_SCALE_TO_CLOCK(pre, feedback, post); - - pixsize1 = - PM3ByApertureMode_PIXELSIZE_MASK & - (PM3_READ_REG(PM3ByAperture1Mode)); - pixsize2 = - PM3ByApertureMode_PIXELSIZE_MASK & - (PM3_READ_REG(PM3ByAperture2Mode)); - - DASSERT((pixsize1 == pixsize2), - "pixsize the same in both aperture\n"); - - if (pixsize1 & PM3ByApertureMode_PIXELSIZE_32BIT) - curpar->depth = 32; - else if (pixsize1 & PM3ByApertureMode_PIXELSIZE_16BIT) - { - curpar->depth = 16; - } - else - curpar->depth = 8; - - /* not sure if I need to add one on the next ; it give better result with */ - curpar->htotal = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - 1 + PM3_READ_REG(PM3HTotal)); - curpar->hsend = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HsEnd)); - curpar->hsstart = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HsStart)); - curpar->hbend = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3HbEnd)); - - curpar->stride = - pm3fb_Unshiftbpp(l_fb_info, curpar->depth, - PM3_READ_REG(PM3ScreenStride)); - - curpar->vtotal = 1 + PM3_READ_REG(PM3VTotal); - curpar->vsend = 1 + PM3_READ_REG(PM3VsEnd); - curpar->vsstart = 1 + PM3_READ_REG(PM3VsStart); - curpar->vbend = PM3_READ_REG(PM3VbEnd); - - curpar->video = PM3_READ_REG(PM3VideoControl); - - curpar->base = PM3_READ_REG(PM3ScreenBase); - curpar->width = curpar->htotal - curpar->hbend; /* make virtual == displayed resolution */ - curpar->height = curpar->vtotal - curpar->vbend; - - DPRINTK(2, "Found : %d * %d, %d Khz, stride is %08x\n", - curpar->width, curpar->height, curpar->pixclock, - curpar->stride); -} - -static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info) -{ - unsigned long memsize = 0, tempBypass, i, temp1, temp2; - u16 subvendor, subdevice; - pm3fb_timing_result ptr; - - DTRACE; - - l_fb_info->fb_size = 64 * 1024 * 1024; /* pm3 aperture always 64 MB */ - pm3fb_mapIO(l_fb_info); /* temporary map IO */ - - DASSERT((l_fb_info->vIOBase != NULL), - "IO successfully mapped before mem detect\n"); - DASSERT((l_fb_info->v_fb != NULL), - "FB successfully mapped before mem detect\n"); - - /* card-specific stuff, *before* accessing *any* FB memory */ - if ((!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - && - (!pci_read_config_word - (l_fb_info->dev, PCI_SUBSYSTEM_ID, &subdevice))) { - i = 0; l_fb_info->board_type = 0; - while ((cardbase[i].cardname[0]) && !(l_fb_info->board_type)) { - if ((cardbase[i].subvendor == subvendor) && - (cardbase[i].subdevice == subdevice) && - (cardbase[i].func == PCI_FUNC(l_fb_info->dev->devfn))) { - DPRINTK(2, "Card #%ld is an %s\n", - l_fb_info->board_num, - cardbase[i].cardname); - if (cardbase[i].specific_setup) - cardbase[i].specific_setup(l_fb_info); - l_fb_info->board_type = i; - } - i++; - } - if (!l_fb_info->board_type) { - DPRINTK(1, "Card #%ld is an unknown 0x%04x / 0x%04x\n", - l_fb_info->board_num, subvendor, subdevice); - } - } else { - printk(KERN_ERR "pm3fb: Error: pci_read_config_word failed, board #%ld\n", - l_fb_info->board_num); - } - - if (printtimings) - pm3fb_show_cur_timing(l_fb_info); - - /* card-specific setup is done, we preserve the final - memory timing for future reference */ - if ((ptr = pm3fb_preserve_memory_timings(l_fb_info)) == pm3fb_timing_problem) { /* memory timings were wrong ! oops.... */ - return(0); - } - - tempBypass = PM3_READ_REG(PM3MemBypassWriteMask); - - DPRINTK(2, "PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass); - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xFFFFFFFF); - - /* pm3 split up memory, replicates, and do a lot of nasty stuff IMHO ;-) */ - for (i = 0; i < 32; i++) { - fb_writel(i * 0x00345678, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - temp1 = fb_readl((l_fb_info->v_fb + (i * 1048576))); - - /* Let's check for wrapover, write will fail at 16MB boundary */ - if (temp1 == (i * 0x00345678)) - memsize = i; - else - break; - } - - DPRINTK(2, "First detect pass already got %ld MB\n", memsize + 1); - - if (memsize == i) { - for (i = 0; i < 32; i++) { - /* Clear first 32MB ; 0 is 0, no need to byteswap */ - writel(0x0000000, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - } - - for (i = 32; i < 64; i++) { - fb_writel(i * 0x00345678, - (l_fb_info->v_fb + (i * 1048576))); - mb(); - temp1 = - fb_readl((l_fb_info->v_fb + (i * 1048576))); - temp2 = - fb_readl((l_fb_info->v_fb + - ((i - 32) * 1048576))); - if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) /* different value, different RAM... */ - memsize = i; - else - break; - } - } - - DPRINTK(2, "Second detect pass got %ld MB\n", memsize + 1); - - PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, tempBypass); - - pm3fb_unmapIO(l_fb_info); - memsize = 1048576 * (memsize + 1); - - DPRINTK(2, "Returning 0x%08lx bytes\n", memsize); - - if (forcesize[l_fb_info->board_num] && ((forcesize[l_fb_info->board_num] * 1048576) != memsize)) - { - printk(KERN_WARNING "pm3fb: mismatch between probed (%ld MB) and specified (%hd MB) memory size, using SPECIFIED !\n", memsize, forcesize[l_fb_info->board_num]); - memsize = 1048576 * forcesize[l_fb_info->board_num]; - } - - l_fb_info->fb_size = memsize; - - if (ptr == pm3fb_timing_retry) - { - printk(KERN_WARNING "pm3fb: retrying memory timings check"); - if (pm3fb_try_memory_timings(l_fb_info) == pm3fb_timing_problem) - return(0); - } - - return (memsize); -} - -static void pm3fb_clear_memory(struct pm3fb_info *l_fb_info, u32 cc) -{ - int i; - - DTRACE; - - for (i = 0; i < (l_fb_info->fb_size / sizeof(u32)) ; i++) /* clear entire FB memory to black */ - { - fb_writel(cc, (l_fb_info->v_fb + (i * sizeof(u32)))); + DPRINTK("depth not supported: %u\n", var->bits_per_pixel); + return -EINVAL; } -} - -static void pm3fb_clear_colormap(struct pm3fb_info *l_fb_info, unsigned char r, unsigned char g, unsigned char b) -{ - int i; - - DTRACE; - - for (i = 0; i < 256 ; i++) /* fill color map with white */ - pm3fb_set_color(l_fb_info, i, r, g, b); - -} - -/* common initialisation */ -static void pm3fb_common_init(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - DPRINTK(2, "Initializing board #%ld @ %lx\n", l_fb_info->board_num, - (unsigned long) l_fb_info); - - strcpy(l_fb_info->gen.info.modename, permedia3_name); - disp[l_fb_info->board_num].scrollmode = 0; /* SCROLL_YNOMOVE; *//* 0 means "let fbcon choose" */ - l_fb_info->gen.parsize = sizeof(struct pm3fb_par); - l_fb_info->gen.info.changevar = NULL; - l_fb_info->gen.info.fbops = &pm3fb_ops; - l_fb_info->gen.info.disp = &(disp[l_fb_info->board_num]); - if (fontn[l_fb_info->board_num][0]) - strcpy(l_fb_info->gen.info.fontname, - fontn[l_fb_info->board_num]); - l_fb_info->gen.info.switch_con = &fbgen_switch; - l_fb_info->gen.info.updatevar = &fbgen_update_var; /* */ - l_fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT; - - pm3fb_mapIO(l_fb_info); - - pm3fb_clear_memory(l_fb_info, 0); - pm3fb_clear_colormap(l_fb_info, 0, 0, 0); - - (void) fbgen_get_var(&(disp[l_fb_info->board_num]).var, -1, - &l_fb_info->gen.info); + var->height = var->width = -1; - if (depth[l_fb_info->board_num]) /* override mode-defined depth */ - { - pm3fb_encode_depth(&(disp[l_fb_info->board_num]).var, depth[l_fb_info->board_num]); - (disp[l_fb_info->board_num]).var.bits_per_pixel = depth2bpp(depth[l_fb_info->board_num]); + if (var->xres != var->xres_virtual) { + DPRINTK("virtual x resolution != physical x resolution not supported\n"); + return -EINVAL; } - (void) fbgen_do_set_var(&(disp[l_fb_info->board_num]).var, 1, - &l_fb_info->gen); - - fbgen_set_disp(-1, &l_fb_info->gen); - - do_install_cmap(0, &l_fb_info->gen.info); - - if (register_framebuffer(&l_fb_info->gen.info) < 0) { - DPRINTK(1, "Couldn't register framebuffer\n"); - return; + if (var->yres > var->yres_virtual) { + DPRINTK("virtual y resolution < physical y resolution not possible\n"); + return -EINVAL; } - PM3_WRITE_DAC_REG(PM3RD_CursorMode, - PM3RD_CursorMode_CURSOR_DISABLE); - - PM3_SHOW_CUR_MODE; - - pm3fb_write_mode(l_fb_info); - - printk("fb%d: %s, using %uK of video memory (%s)\n", - l_fb_info->gen.info.node, - permedia3_name, (u32) (l_fb_info->fb_size >> 10), - cardbase[l_fb_info->board_type].cardname); -} - -/* **************************************************** */ -/* ***** accelerated permedia3-specific functions ***** */ -/* **************************************************** */ -#ifdef PM3FB_USE_ACCEL -static void pm3fb_wait_pm3(struct pm3fb_info *l_fb_info) -{ - DTRACE; - - PM3_SLOW_WRITE_REG(PM3FilterMode, PM3FilterModeSync); - PM3_SLOW_WRITE_REG(PM3Sync, 0); - mb(); - do { - while ((PM3_READ_REG(PM3OutFIFOWords)) == 0); - rmb(); - } while ((PM3_READ_REG(PM3OutputFifo)) != PM3Sync_Tag); -} - -static void pm3fb_init_engine(struct pm3fb_info *l_fb_info) -{ - PM3_SLOW_WRITE_REG(PM3FilterMode, PM3FilterModeSync); - PM3_SLOW_WRITE_REG(PM3StatisticMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DeltaMode, 0x0); - PM3_SLOW_WRITE_REG(PM3RasterizerMode, 0x0); - PM3_SLOW_WRITE_REG(PM3ScissorMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LineStippleMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AreaStippleMode, 0x0); - PM3_SLOW_WRITE_REG(PM3GIDMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DepthMode, 0x0); - PM3_SLOW_WRITE_REG(PM3StencilMode, 0x0); - PM3_SLOW_WRITE_REG(PM3StencilData, 0x0); - PM3_SLOW_WRITE_REG(PM3ColorDDAMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCoordMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureIndexMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureIndexMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LUTMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureFilterMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureApplicationMode, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeColorMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeAlphaMode1, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeColorMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3TextureCompositeAlphaMode0, 0x0); - PM3_SLOW_WRITE_REG(PM3FogMode, 0x0); - PM3_SLOW_WRITE_REG(PM3ChromaTestMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaTestMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AntialiasMode, 0x0); - PM3_SLOW_WRITE_REG(PM3YUVMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaBlendColorMode, 0x0); - PM3_SLOW_WRITE_REG(PM3AlphaBlendAlphaMode, 0x0); - PM3_SLOW_WRITE_REG(PM3DitherMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LogicalOpMode, 0x0); - PM3_SLOW_WRITE_REG(PM3RouterMode, 0x0); - PM3_SLOW_WRITE_REG(PM3Window, 0x0); - - PM3_SLOW_WRITE_REG(PM3Config2D, 0x0); - - PM3_SLOW_WRITE_REG(PM3SpanColorMask, 0xffffffff); - - PM3_SLOW_WRITE_REG(PM3XBias, 0x0); - PM3_SLOW_WRITE_REG(PM3YBias, 0x0); - PM3_SLOW_WRITE_REG(PM3DeltaControl, 0x0); - - PM3_SLOW_WRITE_REG(PM3BitMaskPattern, 0xffffffff); - - PM3_SLOW_WRITE_REG(PM3FBDestReadEnables, - PM3FBDestReadEnables_E(0xff) | - PM3FBDestReadEnables_R(0xff) | - PM3FBDestReadEnables_ReferenceAlpha(0xff)); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferAddr0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferOffset0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBDestReadBufferWidth0, - PM3FBDestReadBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_SLOW_WRITE_REG(PM3FBDestReadMode, - PM3FBDestReadMode_ReadEnable | - PM3FBDestReadMode_Enable0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferAddr, 0x0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferOffset, 0x0); - PM3_SLOW_WRITE_REG(PM3FBSourceReadBufferWidth, - PM3FBSourceReadBufferWidth_Width(l_fb_info-> - current_par-> - width)); - PM3_SLOW_WRITE_REG(PM3FBSourceReadMode, - PM3FBSourceReadMode_Blocking | - PM3FBSourceReadMode_ReadEnable); - - { - unsigned long rm = 1; - switch (l_fb_info->current_par->depth) { - case 8: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_8BIT); - break; - case 12: - case 15: - case 16: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_16BIT); - break; - case 32: - PM3_SLOW_WRITE_REG(PM3PixelSize, - PM3PixelSize_GLOBAL_32BIT); - break; - default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); - break; - } - PM3_SLOW_WRITE_REG(PM3RasterizerMode, rm); + if (var->xoffset) { + DPRINTK("xoffset not supported\n"); + return -EINVAL; } - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff); - PM3_SLOW_WRITE_REG(PM3FBWriteMode, - PM3FBWriteMode_WriteEnable | - PM3FBWriteMode_OpaqueSpan | - PM3FBWriteMode_Enable0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferAddr0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferOffset0, 0x0); - PM3_SLOW_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 0x0); - { - unsigned long sofb = (8UL * l_fb_info->fb_size) / - ((depth2bpp(l_fb_info->current_par->depth)) - * l_fb_info->current_par->width); /* size in lines of FB */ - if (sofb > 4095) - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 4095); - else - PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, sofb); - - switch (l_fb_info->current_par->depth) { - case 8: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (2 << 3)); - break; - case 12: - case 15: - case 16: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (1 << 3)); - break; - case 32: - PM3_SLOW_WRITE_REG(PM3DitherMode, - (1 << 10) | (0 << 3)); - break; - default: - DPRINTK(1, "Unsupported depth %d\n", - l_fb_info->current_par->depth); - break; - } + if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + DPRINTK("interlace not supported\n"); + return -EINVAL; } - PM3_SLOW_WRITE_REG(PM3dXDom, 0x0); - PM3_SLOW_WRITE_REG(PM3dXSub, 0x0); - PM3_SLOW_WRITE_REG(PM3dY, (1 << 16)); - PM3_SLOW_WRITE_REG(PM3StartXDom, 0x0); - PM3_SLOW_WRITE_REG(PM3StartXSub, 0x0); - PM3_SLOW_WRITE_REG(PM3StartY, 0x0); - PM3_SLOW_WRITE_REG(PM3Count, 0x0); - -/* Disable LocalBuffer. better safe than sorry */ - PM3_SLOW_WRITE_REG(PM3LBDestReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LBDestReadEnables, 0x0); - PM3_SLOW_WRITE_REG(PM3LBSourceReadMode, 0x0); - PM3_SLOW_WRITE_REG(PM3LBWriteMode, 0x0); - - pm3fb_wait_pm3(l_fb_info); -} + var->xres = (var->xres + 31) & ~31; /* could sometimes be 8 */ + lpitch = var->xres * ((var->bits_per_pixel + 7)>>3); -#ifdef FBCON_HAS_CFB32 -static void pm3fb_cfb32_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - - /* block fills in 32bpp are hard, but in low res (width <= 1600 :-) - we can use 16bpp operations, but not if NoWriteMask is on (SDRAM) */ - if ((l_fb_info->current_par->width > 1600) || - (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)) { - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - } else { - PM3_WAIT(8); - - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3PixelSize, PM3PixelSize_GLOBAL_16BIT); - - PM3_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width << 1)); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx << 1)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(width << 1)) | - (PM3Render2D_Height(height))); - - PM3_WRITE_REG(PM3FBWriteBufferWidth0, - PM3FBWriteBufferWidth_Width(l_fb_info-> - current_par-> - width)); - - PM3_WRITE_REG(PM3PixelSize, PM3PixelSize_GLOBAL_32BIT); + if (var->xres < 200 || var->xres > 2048) { + DPRINTK("width not supported: %u\n", var->xres); + return -EINVAL; } - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb32_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); + if (var->yres < 200 || var->yres > 4095) { + DPRINTK("height not supported: %u\n", var->yres); + return -EINVAL; } - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB32 */ -#ifdef FBCON_HAS_CFB16 -static void pm3fb_cfb16_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - c = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - c = c | (c << 16); - - PM3_WAIT(4); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb16_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = ((u16 *) p->dispsw_data)[attr_bgcol_ec(p, conp)]; - c = c | (c << 16); - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); + if (lpitch * var->yres_virtual > info->fix.smem_len) { + DPRINTK("no memory for screen (%ux%ux%u)\n", + var->xres, var->yres_virtual, var->bits_per_pixel); + return -EINVAL; } - - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3ForegroundColor, c); - else - PM3_WRITE_REG(PM3FBBlockColor, c); - - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - else - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); - - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB16 */ -#ifdef FBCON_HAS_CFB8 -static void pm3fb_cfb8_clear(struct vc_data *conp, - struct display *p, - int sy, int sx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u32 c; - - DTRACE; - - sx = sx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - height = height * fontheight(p); - - c = attr_bgcol_ec(p, conp); - c |= c << 8; - c |= c << 16; - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(width)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfb8_clear_margins(struct vc_data *conp, - struct display *p, int bottom_only) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int sx, sy; - u32 c; - - DTRACE; - - sx = conp->vc_cols * fontwidth(p); /* right margin */ - sy = conp->vc_rows * fontheight(p); /* bottom margin */ - c = attr_bgcol_ec(p, conp); - c |= c << 8; - c |= c << 16; - - if (!bottom_only) { /* right margin top->bottom */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset - (p->var.xoffset + - sx)) | (PM3RectanglePosition_YOffset(p-> - var. - yoffset))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres - sx)) | - (PM3Render2D_Height(p->var.yres))); + if (PICOS2KHZ(var->pixclock) > PM3_MAX_PIXCLOCK) { + DPRINTK("pixclock too high (%ldKHz)\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; } - /* bottom margin left -> right */ - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ForegroundColor, c); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(p->var.xoffset)) | - (PM3RectanglePosition_YOffset(p->var.yoffset + sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(p->var.xres)) | - (PM3Render2D_Height(p->var.yres - sy))); + var->accel_flags = 0; /* Can't mmap if this is on */ - pm3fb_wait_pm3(l_fb_info); -} -#endif /* FBCON_HAS_CFB8 */ -#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32) -static void pm3fb_cfbX_bmove(struct display *p, - int sy, int sx, - int dy, int dx, int height, int width) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - int x_align, o_x, o_y; - - DTRACE; - - sx = sx * fontwidth(p); - dx = dx * fontwidth(p); - width = width * fontwidth(p); - sy = sy * fontheight(p); - dy = dy * fontheight(p); - height = height * fontheight(p); - - o_x = sx - dx; /*(sx > dx ) ? (sx - dx) : (dx - sx); */ - o_y = sy - dy; /*(sy > dy ) ? (sy - dy) : (dy - sy); */ - - x_align = (sx & 0x1f); - - PM3_WAIT(6); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UserScissorEnable | - PM3Config2D_ForegroundROPEnable | - PM3Config2D_Blocking | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3ScissorMinXY, - ((dy & 0x0fff) << 16) | (dx & 0x0fff)); - PM3_WRITE_REG(PM3ScissorMaxXY, - (((dy + height) & 0x0fff) << 16) | - ((dx + width) & 0x0fff)); - - PM3_WRITE_REG(PM3FBSourceReadBufferOffset, - PM3FBSourceReadBufferOffset_XOffset(o_x) | - PM3FBSourceReadBufferOffset_YOffset(o_y)); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(dx - x_align)) | - (PM3RectanglePosition_YOffset(dy))); - - PM3_WRITE_REG(PM3Render2D, - ((sx > dx) ? PM3Render2D_XPositive : 0) | - ((sy > dy) ? PM3Render2D_YPositive : 0) | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - PM3Render2D_FBSourceReadEnable | - (PM3Render2D_Width(width + x_align)) | - (PM3Render2D_Height(height))); - - pm3fb_wait_pm3(l_fb_info); + DPRINTK("Checking graphics mode at %dx%d depth %d\n", + var->xres, var->yres, var->bits_per_pixel); + return 0; } -static void pm3fb_cfbX_putc(struct vc_data *conp, struct display *p, - int c, int yy, int xx) +static int pm3fb_set_par(struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0; - u32 fgx, bgx, ldat; - int sx, sy, i; + struct pm3_par *par = info->par; + const u32 xres = (info->var.xres + 31) & ~31; + const int depth = (info->var.bits_per_pixel + 7) & ~7; - DTRACE; + par->base = pm3fb_shift_bpp(info->var.bits_per_pixel, + (info->var.yoffset * xres) + + info->var.xoffset); + par->video = 0; - if (l_fb_info->current_par->depth == 8) - fgx = attr_fgcol(p, c); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, c)]; + if (info->var.sync & FB_SYNC_HOR_HIGH_ACT) + par->video |= PM3VideoControl_HSYNC_ACTIVE_HIGH; else - fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, c)]; + par->video |= PM3VideoControl_HSYNC_ACTIVE_LOW; - PM3_COLOR(fgx); - - if (l_fb_info->current_par->depth == 8) - bgx = attr_bgcol(p, c); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, c)]; + if (info->var.sync & FB_SYNC_VERT_HIGH_ACT) + par->video |= PM3VideoControl_VSYNC_ACTIVE_HIGH; else - bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, c)]; - - PM3_COLOR(bgx); - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable | PM3Config2D_OpaqueSpan); - - PM3_WRITE_REG(PM3ForegroundColor, fgx); - PM3_WRITE_REG(PM3FillBackgroundColor, bgx); - - /* WARNING : address select X need to specify 8 bits for fontwidth <= 8 */ - /* and 16 bits for fontwidth <= 16 */ - /* same in _putcs, same for Y and fontheight */ - if (fontwidth(p) <= 8) - asx = 2; - else if (fontwidth(p) <= 16) - asx = 3; /* look OK */ - if (fontheight(p) <= 8) - asy = 2; - else if (fontheight(p) <= 16) - asy = 3; /* look OK */ - else if (fontheight(p) <= 32) - asy = 4; /* look OK */ - - sx = xx * fontwidth(p); - sy = yy * fontheight(p); - - if (fontwidth(p) <= 8) - o_x = (8 - (sx & 0x7)) & 0x7; - else if (fontwidth(p) <= 16) - o_x = (16 - (sx & 0xF)) & 0xF; - if (fontheight(p) <= 8) - o_y = (8 - (sy & 0x7)) & 0x7; - else if (fontheight(p) <= 16) - o_y = (16 - (sy & 0xF)) & 0xF; - else if (fontheight(p) <= 32) - o_y = (32 - (sy & 0x1F)) & 0x1F; - - PM3_WRITE_REG(PM3AreaStippleMode, (o_x << 7) | (o_y << 12) | /* x_offset, y_offset in pattern */ - (1 << 18) | /* BE */ - 1 | (asx << 1) | (asy << 4) | /* address select x/y */ - (1 << 20)); /* OpaqueSpan */ - - if (fontwidth(p) <= 8) { - cdat = p->fontdata + (c & p->charmask) * fontheight(p); - } else { - cdat = - p->fontdata + - ((c & p->charmask) * (fontheight(p) << 1)); - } - - PM3_WAIT(2 + fontheight(p)); - - for (i = 0; i < fontheight(p); i++) { /* assume fontheight <= 32 */ - if (fontwidth(p) <= 8) { - ldat = *cdat++; - } else { /* assume fontwidth <= 16 ATM */ - - ldat = ((*cdat++) << 8); - ldat |= *cdat++; - } - PM3_WRITE_REG(AreaStipplePattern_indexed(i), ldat); - } - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); + par->video |= PM3VideoControl_VSYNC_ACTIVE_LOW; - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_AreaStippleEnable | - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - - pm3fb_wait_pm3(l_fb_info); -} - -static void pm3fb_cfbX_putcs(struct vc_data *conp, struct display *p, - const unsigned short *s, int count, int yy, - int xx) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0; - u32 fgx, bgx, ldat; - int sx, sy, i, j; - u16 sc; - - DTRACE; - - sc = scr_readw(s); - if (l_fb_info->current_par->depth == 8) - fgx = attr_fgcol(p, sc); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - fgx = ((u16 *) p->dispsw_data)[attr_fgcol(p, sc)]; - else - fgx = ((u32 *) p->dispsw_data)[attr_fgcol(p, sc)]; - - PM3_COLOR(fgx); - - if (l_fb_info->current_par->depth == 8) - bgx = attr_bgcol(p, sc); - else if (depth2bpp(l_fb_info->current_par->depth) == 16) - bgx = ((u16 *) p->dispsw_data)[attr_bgcol(p, sc)]; + if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) + par->video |= PM3VideoControl_LINE_DOUBLE_ON; else - bgx = ((u32 *) p->dispsw_data)[attr_bgcol(p, sc)]; - - PM3_COLOR(bgx); - - PM3_WAIT(4); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0x3)) | /* Ox3 is GXcopy */ - PM3Config2D_FBWriteEnable | - PM3Config2D_OpaqueSpan); - - PM3_WRITE_REG(PM3ForegroundColor, fgx); - PM3_WRITE_REG(PM3FillBackgroundColor, bgx); - - /* WARNING : address select X need to specify 8 bits for fontwidth <= 8 */ - /* and 16 bits for fontwidth <= 16 */ - /* same in _putc, same for Y and fontheight */ - if (fontwidth(p) <= 8) - asx = 2; - else if (fontwidth(p) <= 16) - asx = 3; /* look OK */ - if (fontheight(p) <= 8) - asy = 2; - else if (fontheight(p) <= 16) - asy = 3; /* look OK */ - else if (fontheight(p) <= 32) - asy = 4; /* look OK */ - - sy = yy * fontheight(p); - - if (fontheight(p) <= 8) - o_y = (8 - (sy & 0x7)) & 0x7; - else if (fontheight(p) <= 16) - o_y = (16 - (sy & 0xF)) & 0xF; - else if (fontheight(p) <= 32) - o_y = (32 - (sy & 0x1F)) & 0x1F; - - for (j = 0; j < count; j++) { - sc = scr_readw(s + j); - if (fontwidth(p) <= 8) - cdat = p->fontdata + - (sc & p->charmask) * fontheight(p); - else - cdat = p->fontdata + - ((sc & p->charmask) * fontheight(p) << 1); - - sx = (xx + j) * fontwidth(p); - - if (fontwidth(p) <= 8) - o_x = (8 - (sx & 0x7)) & 0x7; - else if (fontwidth(p) <= 16) - o_x = (16 - (sx & 0xF)) & 0xF; - - PM3_WAIT(3 + fontheight(p)); - - PM3_WRITE_REG(PM3AreaStippleMode, (o_x << 7) | (o_y << 12) | /* x_offset, y_offset in pattern */ - (1 << 18) | /* BE */ - 1 | (asx << 1) | (asy << 4) | /* address select x/y */ - (1 << 20)); /* OpaqueSpan */ - - for (i = 0; i < fontheight(p); i++) { /* assume fontheight <= 32 */ - if (fontwidth(p) <= 8) { - ldat = *cdat++; - } else { /* assume fontwidth <= 16 ATM */ - ldat = ((*cdat++) << 8); - ldat |= *cdat++; - } - PM3_WRITE_REG(AreaStipplePattern_indexed(i), ldat); - } - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(sx)) | - (PM3RectanglePosition_YOffset(sy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_AreaStippleEnable | - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - } - pm3fb_wait_pm3(l_fb_info); -} + par->video |= PM3VideoControl_LINE_DOUBLE_OFF; -static void pm3fb_cfbX_revc(struct display *p, int xx, int yy) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info; - - xx = xx * fontwidth(p); - yy = yy * fontheight(p); - - if (l_fb_info->current_par->depth == 8) - { - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0x0F0F0F0F); - else - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0x0F0F0F0F); + if (info->var.activate == FB_ACTIVATE_NOW) + par->video |= PM3VideoControl_ENABLE; + else { + par->video |= PM3VideoControl_DISABLE; + DPRINTK("PM3Video disabled\n"); } - - PM3_WAIT(3); - - PM3_WRITE_REG(PM3Config2D, - PM3Config2D_UseConstantSource | - PM3Config2D_ForegroundROPEnable | - (PM3Config2D_ForegroundROP(0xa)) | /* Oxa is GXinvert */ - PM3Config2D_FBDestReadEnable | - PM3Config2D_FBWriteEnable); - - PM3_WRITE_REG(PM3RectanglePosition, - (PM3RectanglePosition_XOffset(xx)) | - (PM3RectanglePosition_YOffset(yy))); - - PM3_WRITE_REG(PM3Render2D, - PM3Render2D_XPositive | - PM3Render2D_YPositive | - PM3Render2D_Operation_Normal | - PM3Render2D_SpanOperation | - (PM3Render2D_Width(fontwidth(p))) | - (PM3Render2D_Height(fontheight(p)))); - - pm3fb_wait_pm3(l_fb_info); - - if (l_fb_info->current_par->depth == 8) - { - if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask) - PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xFFFFFFFF); - else - PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xFFFFFFFF); - } -} - -#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */ -#endif /* PM3FB_USE_ACCEL */ -/* *********************************** */ -/* ***** pre-init board(s) setup ***** */ -/* *********************************** */ - -static void pm3fb_mode_setup(char *mode, unsigned long board_num) -{ - struct pm3fb_info *l_fb_info = &(fb_info[board_num]); - struct pm3fb_par *l_fb_par = &(current_par[board_num]); - unsigned long i = 0; - - current_par_valid[board_num] = 0; - - if (!strncmp(mode, "current", 7)) { - l_fb_info->use_current = 1; /* default w/ OpenFirmware */ - } else { - while ((mode_base[i].name[0]) - && (!current_par_valid[board_num])) { - if (! - (strncmp - (mode, mode_base[i].name, - strlen(mode_base[i].name)))) { - memcpy(l_fb_par, &(mode_base[i].user_mode), - sizeof(struct pm3fb_par)); - current_par_valid[board_num] = 1; - DPRINTK(2, "Mode set to %s\n", - mode_base[i].name); - } - i++; - } - DASSERT(current_par_valid[board_num], - "Valid mode on command line\n"); - } -} - -static void pm3fb_pciid_setup(char *pciid, unsigned long board_num) -{ - short l_bus = -1, l_slot = -1, l_func = -1; - char *next; - - if (pciid) { - l_bus = simple_strtoul(pciid, &next, 10); - if (next && (next[0] == ':')) { - pciid = next + 1; - l_slot = simple_strtoul(pciid, &next, 10); - if (next && (next[0] == ':')) { - pciid = next + 1; - l_func = - simple_strtoul(pciid, (char **) NULL, - 10); - } - } - } else - return; - - if ((l_bus >= 0) && (l_slot >= 0) && (l_func >= 0)) { - bus[board_num] = l_bus; - slot[board_num] = l_slot; - func[board_num] = l_func; - DPRINTK(2, "Board #%ld will be PciId: %hd:%hd:%hd\n", - board_num, l_bus, l_slot, l_func); - } else { - DPRINTK(1, "Invalid PciId: %hd:%hd:%hd for board #%ld\n", - l_bus, l_slot, l_func, board_num); - } -} - -static void pm3fb_font_setup(char *lf, unsigned long board_num) -{ - unsigned long lfs = strlen(lf); - - if (lfs > (PM3_FONTNAME_SIZE - 1)) { - DPRINTK(1, "Fontname %s too long\n", lf); - return; - } - strlcpy(fontn[board_num], lf, lfs + 1); -} - -static void pm3fb_bootdepth_setup(char *bds, unsigned long board_num) -{ - unsigned long bd = simple_strtoul(bds, (char **) NULL, 10); - - if (!(depth_supported(bd))) { - printk(KERN_WARNING "pm3fb: ignoring invalid depth %s for board #%ld\n", - bds, board_num); - return; - } - depth[board_num] = bd; -} - -static void pm3fb_forcesize_setup(char *bds, unsigned long board_num) -{ - unsigned long bd = simple_strtoul(bds, (char **) NULL, 10); - - if (bd > 64) { - printk(KERN_WARNING "pm3fb: ignoring invalid memory size %s for board #%ld\n", - bds, board_num); - return; - } - forcesize[board_num] = bd; -} - -static char *pm3fb_boardnum_setup(char *options, unsigned long *bn) -{ - char *next; - - if (!(isdigit(options[0]))) { - (*bn) = 0; - return (options); - } - - (*bn) = simple_strtoul(options, &next, 10); - - if (next && (next[0] == ':') && ((*bn) >= 0) - && ((*bn) <= PM3_MAX_BOARD)) { - DPRINTK(2, "Board_num seen as %ld\n", (*bn)); - return (next + 1); - } else { - (*bn) = 0; - DPRINTK(2, "Board_num default to %ld\n", (*bn)); - return (options); - } -} - -static void pm3fb_real_setup(char *options) -{ - char *next; - unsigned long i, bn; - struct pm3fb_info *l_fb_info; - - DTRACE; - - DPRINTK(2, "Options : %s\n", options); - - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - memset(l_fb_info, 0, sizeof(struct pm3fb_info)); - l_fb_info->gen.fbhw = &pm3fb_switch; - l_fb_info->board_num = i; - current_par_valid[i] = 0; - slot[i] = -1; - func[i] = -1; - bus[i] = -1; - disable[i] = 0; - noaccel[i] = 0; - fontn[i][0] = '\0'; - depth[i] = 0; - l_fb_info->current_par = &(current_par[i]); - } - - /* eat up prefix pm3fb and whatever is used as separator i.e. :,= */ - if (!strncmp(options, "pm3fb", 5)) { - options += 5; - while (((*options) == ',') || ((*options) == ':') - || ((*options) == '=')) - options++; - } - - while (options) { - bn = 0; - if ((next = strchr(options, ','))) { - (*next) = '\0'; - next++; - } - - if (!strncmp(options, "mode:", 5)) { - options = pm3fb_boardnum_setup(options + 5, &bn); - DPRINTK(2, "Setting mode for board #%ld\n", bn); - pm3fb_mode_setup(options, bn); - } else if (!strncmp(options, "off:", 4)) { - options = pm3fb_boardnum_setup(options + 4, &bn); - DPRINTK(2, "Disabling board #%ld\n", bn); - disable[bn] = 1; - } else if (!strncmp(options, "off", 3)) { /* disable everything */ - for (i = 0; i < PM3_MAX_BOARD; i++) - disable[i] = 1; - } else if (!strncmp(options, "disable:", 8)) { - options = pm3fb_boardnum_setup(options + 8, &bn); - DPRINTK(2, "Disabling board #%ld\n", bn); - disable[bn] = 1; - } else if (!strncmp(options, "pciid:", 6)) { - options = pm3fb_boardnum_setup(options + 6, &bn); - DPRINTK(2, "Setting PciID for board #%ld\n", bn); - pm3fb_pciid_setup(options, bn); - } else if (!strncmp(options, "noaccel:", 8)) { - options = pm3fb_boardnum_setup(options + 8, &bn); - noaccel[bn] = 1; - } else if (!strncmp(options, "font:", 5)) { - options = pm3fb_boardnum_setup(options + 5, &bn); - pm3fb_font_setup(options, bn); - } else if (!strncmp(options, "depth:", 6)) { - options = pm3fb_boardnum_setup(options + 6, &bn); - pm3fb_bootdepth_setup(options, bn); - } else if (!strncmp(options, "printtimings", 12)) { - printtimings = 1; - } else if (!strncmp(options, "flatpanel:", 10)) { - options = pm3fb_boardnum_setup(options + 10, &bn); - flatpanel[bn] = 1; - } else if (!strncmp(options, "forcesize:", 10)) { - options = pm3fb_boardnum_setup(options + 10, &bn); - pm3fb_forcesize_setup(options, bn); - } - options = next; - } -} - -/* ********************************************** */ -/* ***** framebuffer API standard functions ***** */ -/* ********************************************** */ - -static int pm3fb_encode_fix(struct fb_fix_screeninfo *fix, - const void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - - DTRACE; - - strcpy(fix->id, permedia3_name); - fix->smem_start = (unsigned long)l_fb_info->p_fb; - fix->smem_len = l_fb_info->fb_size; - fix->mmio_start = (unsigned long)l_fb_info->pIOBase; - fix->mmio_len = PM3_REGS_SIZE; -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - fix->accel = FB_ACCEL_3DLABS_PERMEDIA3; - else -#endif /* PM3FB_USE_ACCEL */ - fix->accel = FB_ACCEL_NONE; - fix->type = FB_TYPE_PACKED_PIXELS; - fix->visual = - (p->depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; - if (current_par_valid[l_fb_info->board_num]) - fix->line_length = - l_fb_info->current_par->width * - depth2ByPP(l_fb_info->current_par->depth); - else - fix->line_length = 0; - fix->xpanstep = 64 / depth2bpp(p->depth); - fix->ypanstep = 1; - fix->ywrapstep = 0; - return (0); -} - -static int pm3fb_decode_var(const struct fb_var_screeninfo *var, - void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - struct pm3fb_par temp_p; - u32 xres; - - DTRACE; - - DASSERT((var != NULL), "fb_var_screeninfo* not NULL"); - DASSERT((p != NULL), "pm3fb_par* not NULL"); - DASSERT((l_fb_info != NULL), "pm3fb_info* not NULL"); - - memset(&temp_p, 0, sizeof(struct pm3fb_par)); - temp_p.width = (var->xres_virtual + 7) & ~7; - temp_p.height = var->yres_virtual; - - if (!(depth_supported(var->bits_per_pixel))) /* round unsupported up to a multiple of 8 */ - temp_p.depth = depth2bpp(var->bits_per_pixel); - else - temp_p.depth = var->bits_per_pixel; - - temp_p.depth = (temp_p.depth > 32) ? 32 : temp_p.depth; /* max 32 */ - temp_p.depth = (temp_p.depth == 24) ? 32 : temp_p.depth; /* 24 unsupported, round-up to 32 */ - - if ((temp_p.depth == 16) && (var->red.length == 5) && (var->green.length == 5) && (var->blue.length == 5)) - temp_p.depth = 15; /* RGBA 5551 is stored as depth 15 */ - - if ((temp_p.depth == 16) && (var->red.length == 4) && (var->green.length == 4) && (var->blue.length == 4)) - temp_p.depth = 12; /* RGBA 4444 is stored as depth 12 */ - - - DPRINTK(2, - "xres: %d, yres: %d, vxres: %d, vyres: %d ; xoffset:%d, yoffset: %d\n", - var->xres, var->yres, var->xres_virtual, var->yres_virtual, - var->xoffset, var->yoffset); - - xres = (var->xres + 31) & ~31; - if (temp_p.width < xres + var->xoffset) - temp_p.width = xres + var->xoffset; - if (temp_p.height < var->yres + var->yoffset) - temp_p.height = var->yres + var->yoffset; - - if (temp_p.width > 2048) { - DPRINTK(1, "virtual width not supported: %u\n", - temp_p.width); - return (-EINVAL); - } - if (var->yres < 200) { - DPRINTK(1, "height not supported: %u\n", (u32) var->yres); - return (-EINVAL); - } - if (temp_p.height < 200 || temp_p.height > 4095) { - DPRINTK(1, "virtual height not supported: %u\n", - temp_p.height); - return (-EINVAL); - } - if (!(depth_supported(temp_p.depth))) { - DPRINTK(1, "depth not supported: %u\n", temp_p.depth); - return (-EINVAL); - } - if ((temp_p.width * temp_p.height * depth2ByPP(temp_p.depth)) > - l_fb_info->fb_size) { - DPRINTK(1, "no memory for screen (%ux%ux%u)\n", - temp_p.width, temp_p.height, temp_p.depth); - return (-EINVAL); - } - - if ((!var->pixclock) || - (!var->right_margin) || - (!var->hsync_len) || - (!var->left_margin) || - (!var->lower_margin) || - (!var->vsync_len) || (!var->upper_margin) - ) { - unsigned long i = 0, done = 0; - printk(KERN_WARNING "pm3fb: refusing to use a likely wrong timing\n"); - - while ((mode_base[i].user_mode.width) && !done) { - if ((mode_base[i].user_mode.width == temp_p.width) - && (mode_base[i].user_mode.height == - temp_p.height)) { - printk(KERN_NOTICE "pm3fb: using close match %s\n", - mode_base[i].name); - temp_p = mode_base[i].user_mode; - done = 1; - } - i++; - } - if (!done) - return (-EINVAL); - } else { - temp_p.pixclock = PICOS2KHZ(var->pixclock); - if (temp_p.pixclock > PM3_MAX_PIXCLOCK) { - DPRINTK(1, "pixclock too high (%uKHz)\n", - temp_p.pixclock); - return (-EINVAL); - } - - temp_p.hsstart = var->right_margin; - temp_p.hsend = var->right_margin + var->hsync_len; - temp_p.hbend = - var->right_margin + var->hsync_len + var->left_margin; - temp_p.htotal = xres + temp_p.hbend; - - temp_p.vsstart = var->lower_margin; - temp_p.vsend = var->lower_margin + var->vsync_len; - temp_p.vbend = - var->lower_margin + var->vsync_len + var->upper_margin; - temp_p.vtotal = var->yres + temp_p.vbend; - - temp_p.stride = temp_p.width; - - DPRINTK(2, "Using %d * %d, %d Khz, stride is %08x\n", - temp_p.width, temp_p.height, temp_p.pixclock, - temp_p.stride); - - temp_p.base = - pm3fb_Shiftbpp(l_fb_info, temp_p.depth, - (var->yoffset * xres) + var->xoffset); - - temp_p.video = 0; - - if (var->sync & FB_SYNC_HOR_HIGH_ACT) - temp_p.video |= PM3VideoControl_HSYNC_ACTIVE_HIGH; - else - temp_p.video |= PM3VideoControl_HSYNC_ACTIVE_LOW; - - if (var->sync & FB_SYNC_VERT_HIGH_ACT) - temp_p.video |= PM3VideoControl_VSYNC_ACTIVE_HIGH; - else - temp_p.video |= PM3VideoControl_VSYNC_ACTIVE_LOW; - - if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { - DPRINTK(1, "Interlaced mode not supported\n\n"); - return (-EINVAL); - } - - if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) - temp_p.video |= PM3VideoControl_LINE_DOUBLE_ON; - else - temp_p.video |= PM3VideoControl_LINE_DOUBLE_OFF; - - if (var->activate == FB_ACTIVATE_NOW) - temp_p.video |= PM3VideoControl_ENABLE; - else { - temp_p.video |= PM3VideoControl_DISABLE; - DPRINTK(2, "PM3Video disabled\n"); - } - - switch (temp_p.depth) { - case 8: - temp_p.video |= PM3VideoControl_PIXELSIZE_8BIT; - break; - case 12: - case 15: - case 16: - temp_p.video |= PM3VideoControl_PIXELSIZE_16BIT; - break; - case 32: - temp_p.video |= PM3VideoControl_PIXELSIZE_32BIT; - break; - default: - DPRINTK(1, "Unsupported depth\n"); - break; - } - } - (*p) = temp_p; - -#ifdef PM3FB_USE_ACCEL - if (var->accel_flags & FB_ACCELF_TEXT) - noaccel[l_fb_info->board_num] = 0; - else - noaccel[l_fb_info->board_num] = 1; -#endif /* PM3FB_USE_ACCEL */ - - return (0); -} - -static void pm3fb_encode_depth(struct fb_var_screeninfo *var, long d) -{ - switch (d) { + switch (depth) { case 8: - var->red.length = var->green.length = var->blue.length = 8; - var->red.offset = var->green.offset = var->blue.offset = 0; - var->transp.offset = var->transp.length = 0; + par->video |= PM3VideoControl_PIXELSIZE_8BIT; break; - case 12: - var->red.offset = 8; - var->red.length = 4; - var->green.offset = 4; - var->green.length = 4; - var->blue.offset = 0; - var->blue.length = 4; - var->transp.offset = 12; - var->transp.length = 4; - break; - case 15: - var->red.offset = 10; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 5; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = 15; - var->transp.length = 1; - break; - case 16: - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = var->transp.length = 0; + par->video |= PM3VideoControl_PIXELSIZE_16BIT; break; - case 32: - var->transp.offset = 24; - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - var->red.length = var->green.length = - var->blue.length = var->transp.length = 8; + par->video |= PM3VideoControl_PIXELSIZE_32BIT; break; - default: - DPRINTK(1, "Unsupported depth %ld\n", d); + DPRINTK("Unsupported depth\n"); break; } -} -static int pm3fb_encode_var(struct fb_var_screeninfo *var, - const void *par, struct fb_info_gen *info) -{ - struct pm3fb_par *p = (struct pm3fb_par *) par; - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - u32 base; - - DTRACE; - - DASSERT((var != NULL), "fb_var_screeninfo* not NULL"); - DASSERT((p != NULL), "pm3fb_par* not NULL"); - DASSERT((info != NULL), "fb_info_gen* not NULL"); - - memset(var, 0, sizeof(struct fb_var_screeninfo)); - -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - var->accel_flags |= FB_ACCELF_TEXT; -#endif /* PM3FB_USE_ACCEL */ - - var->xres_virtual = p->width; - var->yres_virtual = p->height; - var->xres = p->htotal - p->hbend; - var->yres = p->vtotal - p->vbend; - - DPRINTK(2, "xres = %d, yres : %d\n", var->xres, var->yres); - - var->right_margin = p->hsstart; - var->hsync_len = p->hsend - p->hsstart; - var->left_margin = p->hbend - p->hsend; - var->lower_margin = p->vsstart; - var->vsync_len = p->vsend - p->vsstart; - var->upper_margin = p->vbend - p->vsend; - var->bits_per_pixel = depth2bpp(p->depth); - - pm3fb_encode_depth(var, p->depth); - - base = pm3fb_Unshiftbpp(l_fb_info, p->depth, p->base); - - var->xoffset = base % var->xres; - var->yoffset = base / var->xres; - - var->height = var->width = -1; - - var->pixclock = KHZ2PICOS(p->pixclock); - - if ((p->video & PM3VideoControl_HSYNC_MASK) == - PM3VideoControl_HSYNC_ACTIVE_HIGH) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if ((p->video & PM3VideoControl_VSYNC_MASK) == - PM3VideoControl_VSYNC_ACTIVE_HIGH) - var->sync |= FB_SYNC_VERT_HIGH_ACT; - if (p->video & PM3VideoControl_LINE_DOUBLE_ON) - var->vmode = FB_VMODE_DOUBLE; - - return (0); -} - -static void pm3fb_get_par(void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; + info->fix.visual = + (depth == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + info->fix.line_length = ((info->var.xres_virtual + 7) & ~7) + * depth / 8; - if (!current_par_valid[l_fb_info->board_num]) { - if (l_fb_info->use_current) - pm3fb_read_mode(l_fb_info, l_fb_info->current_par); - else - memcpy(l_fb_info->current_par, - &(mode_base[0].user_mode), - sizeof(struct pm3fb_par)); - current_par_valid[l_fb_info->board_num] = 1; - } - *((struct pm3fb_par *) par) = *(l_fb_info->current_par); -} - -static void pm3fb_set_par(const void *par, struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - *(l_fb_info->current_par) = *((struct pm3fb_par *) par); - current_par_valid[l_fb_info->board_num] = 1; - - pm3fb_write_mode(l_fb_info); - -#ifdef PM3FB_USE_ACCEL - pm3fb_init_engine(l_fb_info); -#endif /* PM3FB_USE_ACCEL */ -} - -static void pm3fb_set_color(struct pm3fb_info *l_fb_info, - unsigned char regno, unsigned char r, - unsigned char g, unsigned char b) -{ - DTRACE; - - PM3_SLOW_WRITE_REG(PM3RD_PaletteWriteAddress, regno); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, r); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, g); - PM3_SLOW_WRITE_REG(PM3RD_PaletteData, b); -} - -static int pm3fb_getcolreg(unsigned regno, unsigned *red, unsigned *green, - unsigned *blue, unsigned *transp, - struct fb_info *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; - - if (regno < 256) { - *red = - l_fb_info->palette[regno].red << 8 | l_fb_info-> - palette[regno].red; - *green = - l_fb_info->palette[regno].green << 8 | l_fb_info-> - palette[regno].green; - *blue = - l_fb_info->palette[regno].blue << 8 | l_fb_info-> - palette[regno].blue; - *transp = - l_fb_info->palette[regno].transp << 8 | l_fb_info-> - palette[regno].transp; - } - return (regno > 255); +/* pm3fb_clear_memory(info, 0);*/ + pm3fb_clear_colormap(par, 0, 0, 0); + PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, + PM3RD_CursorMode_CURSOR_DISABLE); + pm3fb_write_mode(info); + return 0; } static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - - DTRACE; + struct pm3_par *par = info->par; + + if (regno >= 256) /* no. of hw registers */ + return -EINVAL; + + /* grayscale works only partially under directcolor */ + if (info->var.grayscale) { + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + /* Directcolor: + * var->{color}.offset contains start of bitfield + * var->{color}.length contains length of bitfield + * {hardwarespecific} contains width of DAC + * pseudo_palette[X] is programmed to (X << red.offset) | + * (X << green.offset) | + * (X << blue.offset) + * RAMDAC[X] is programmed to (red, green, blue) + * color depth = SUM(var->{color}.length) + * + * Pseudocolor: + * var->{color}.offset is 0 + * var->{color}.length contains width of DAC or the number of unique + * colors available (color depth) + * pseudo_palette is not used + * RAMDAC[X] is programmed to (red, green, blue) + * color depth = var->{color}.length + */ - if (regno < 16) { - switch (l_fb_info->current_par->depth) { -#ifdef FBCON_HAS_CFB8 + /* + * This is the point where the color is converted to something that + * is acceptable by the hardware. + */ +#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); +#undef CNVT_TOHW + + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + u32 v; + + if (regno >= 16) + return -EINVAL; + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + switch (info->var.bits_per_pixel) { case 8: break; -#endif -#ifdef FBCON_HAS_CFB16 - case 12: - l_fb_info->cmap.cmap12[regno] = - (((u32) red & 0xf000) >> 4) | - (((u32) green & 0xf000) >> 8) | - (((u32) blue & 0xf000) >> 12); - break; - - case 15: - l_fb_info->cmap.cmap15[regno] = - (((u32) red & 0xf800) >> 1) | - (((u32) green & 0xf800) >> 6) | - (((u32) blue & 0xf800) >> 11); - break; - case 16: - l_fb_info->cmap.cmap16[regno] = - ((u32) red & 0xf800) | - (((u32) green & 0xfc00) >> 5) | - (((u32) blue & 0xf800) >> 11); - break; -#endif -#ifdef FBCON_HAS_CFB32 + case 24: case 32: - l_fb_info->cmap.cmap32[regno] = - (((u32) transp & 0xff00) << 16) | - (((u32) red & 0xff00) << 8) | - (((u32) green & 0xff00)) | - (((u32) blue & 0xff00) >> 8); - break; -#endif - default: - DPRINTK(1, "bad depth %u\n", - l_fb_info->current_par->depth); + ((u32*)(info->pseudo_palette))[regno] = v; break; } + return 0; } - if (regno < 256) { - l_fb_info->palette[regno].red = red >> 8; - l_fb_info->palette[regno].green = green >> 8; - l_fb_info->palette[regno].blue = blue >> 8; - l_fb_info->palette[regno].transp = transp >> 8; - if (l_fb_info->current_par->depth == 8) - pm3fb_set_color(l_fb_info, regno, red >> 8, - green >> 8, blue >> 8); - } - return (regno > 255); + else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) + pm3fb_set_color(par, regno, red, green, blue); + + return 0; } -static int pm3fb_blank(int blank_mode, struct fb_info_gen *info) +static int pm3fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - u32 video; - - DTRACE; + struct pm3_par *par = info->par; + const u32 xres = (var->xres + 31) & ~31; - if (!current_par_valid[l_fb_info->board_num]) - return (1); + par->base = pm3fb_shift_bpp(var->bits_per_pixel, + (var->yoffset * xres) + + var->xoffset); + PM3_SLOW_WRITE_REG(par, PM3ScreenBase, par->base); + return 0; +} - video = l_fb_info->current_par->video; +static int pm3fb_blank(int blank_mode, struct fb_info *info) +{ + struct pm3_par *par = info->par; + u32 video = par->video; /* * Oxygen VX1 - it appears that setting PM3VideoControl and @@ -3181,454 +663,345 @@ static int pm3fb_blank(int blank_mode, struct fb_info_gen *info) video |= PM3VideoControl_HSYNC_ACTIVE_HIGH | PM3VideoControl_VSYNC_ACTIVE_HIGH; - if (blank_mode > 0) { - switch (blank_mode - 1) { - - case VESA_NO_BLANKING: /* FIXME */ - video = video & ~(PM3VideoControl_ENABLE); - break; - - case VESA_HSYNC_SUSPEND: - video = video & ~(PM3VideoControl_HSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - case VESA_VSYNC_SUSPEND: - video = video & ~(PM3VideoControl_VSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - case VESA_POWERDOWN: - video = video & ~(PM3VideoControl_HSYNC_MASK | - PM3VideoControl_VSYNC_MASK | - PM3VideoControl_BLANK_ACTIVE_LOW); - break; - default: - DPRINTK(1, "Unsupported blanking %d\n", - blank_mode); - return (1); - break; - } - } - - PM3_SLOW_WRITE_REG(PM3VideoControl, video); - - return (0); -} - -static void pm3fb_set_disp(const void *par, struct display *disp, - struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - struct pm3fb_par *p = (struct pm3fb_par *) par; - u32 flags; - - DTRACE; - - local_irq_save(flags); - info->info.screen_base = l_fb_info->v_fb; - switch (p->depth) { -#ifdef FBCON_HAS_CFB8 - case 8: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb8; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb8; + switch (blank_mode) { + case FB_BLANK_UNBLANK: + video = video | PM3VideoControl_ENABLE; break; -#endif -#ifdef FBCON_HAS_CFB16 - case 12: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap12; + case FB_BLANK_NORMAL: /* FIXME */ + video = video & ~(PM3VideoControl_ENABLE); break; - case 15: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap15; + case FB_BLANK_HSYNC_SUSPEND: + video = video & ~(PM3VideoControl_HSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; - case 16: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb16; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb16; - disp->dispsw_data = l_fb_info->cmap.cmap16; + case FB_BLANK_VSYNC_SUSPEND: + video = video & ~(PM3VideoControl_VSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; -#endif -#ifdef FBCON_HAS_CFB32 - case 32: -#ifdef PM3FB_USE_ACCEL - if (!(noaccel[l_fb_info->board_num])) - disp->dispsw = &pm3fb_cfb32; - else -#endif /* PM3FB_USE_ACCEL */ - disp->dispsw = &fbcon_cfb32; - disp->dispsw_data = l_fb_info->cmap.cmap32; + case FB_BLANK_POWERDOWN: + video = video & ~(PM3VideoControl_HSYNC_MASK | + PM3VideoControl_VSYNC_MASK | + PM3VideoControl_BLANK_ACTIVE_LOW); break; -#endif /* FBCON_HAS_CFB32 */ default: - disp->dispsw = &fbcon_dummy; - DPRINTK(1, "Invalid depth, using fbcon_dummy\n"); - break; + DPRINTK("Unsupported blanking %d\n", blank_mode); + return 1; } - local_irq_restore(flags); + + PM3_SLOW_WRITE_REG(par,PM3VideoControl, video); + + return 0; } -/* */ -static void pm3fb_detect(void) -{ - struct pci_dev *dev_array[PM3_MAX_BOARD]; - struct pci_dev *dev = NULL; - struct pm3fb_info *l_fb_info = &(fb_info[0]); - unsigned long i, j, done; + /* + * Frame buffer operations + */ - DTRACE; +static struct fb_ops pm3fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = pm3fb_check_var, + .fb_set_par = pm3fb_set_par, + .fb_setcolreg = pm3fb_setcolreg, + .fb_pan_display = pm3fb_pan_display, + .fb_fillrect = cfb_fillrect, /* Needed !!! */ + .fb_copyarea = cfb_copyarea, /* Needed !!! */ + .fb_imageblit = cfb_imageblit, /* Needed !!! */ + .fb_blank = pm3fb_blank, +}; - for (i = 0; i < PM3_MAX_BOARD; i++) { - dev_array[i] = NULL; - fb_info[i].dev = NULL; - } +/* ------------------------------------------------------------------------- */ - dev = pci_get_device(PCI_VENDOR_ID_3DLABS, - PCI_DEVICE_ID_3DLABS_PERMEDIA3, dev); + /* + * Initialization + */ - for (i = 0; ((i < PM3_MAX_BOARD) && dev); i++) { - dev_array[i] = dev; - dev = pci_get_device(PCI_VENDOR_ID_3DLABS, - PCI_DEVICE_ID_3DLABS_PERMEDIA3, dev); - } +/* mmio register are already mapped when this function is called */ +/* the pm3fb_fix.smem_start is also set */ +static unsigned long pm3fb_size_memory(struct pm3_par *par) +{ + unsigned long memsize = 0, tempBypass, i, temp1, temp2; + unsigned char __iomem *screen_mem; - if (dev) { /* more than PM3_MAX_BOARD */ - printk(KERN_WARNING "pm3fb: Warning: more than %d boards found\n", - PM3_MAX_BOARD); + pm3fb_fix.smem_len = 64 * 1024 * 1024; /* request full aperture size */ + /* Linear frame buffer - request region and map it. */ + if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len, + "pm3fb smem")) { + printk(KERN_WARNING "pm3fb: Can't reserve smem.\n"); + return 0; } - - if (!dev_array[0]) { /* not a single board, abort */ - return; + screen_mem = + ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + if (!screen_mem) { + printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n"); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + return 0; } - /* allocate user-defined boards */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - if ((bus[i] >= 0) && (slot[i] >= 0) && (func[i] >= 0)) { - for (j = 0; j < PM3_MAX_BOARD; j++) { - if ((dev_array[j] != NULL) && - (dev_array[j]->bus->number == bus[i]) - && (PCI_SLOT(dev_array[j]->devfn) == - slot[i]) - && (PCI_FUNC(dev_array[j]->devfn) == - func[i])) { - fb_info[i].dev = dev_array[j]; - dev_array[j] = NULL; - } - } - } + /* TODO: card-specific stuff, *before* accessing *any* FB memory */ + /* For Appian Jeronimo 2000 board second head */ + + tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask); + + DPRINTK("PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass); + + PM3_SLOW_WRITE_REG(par, PM3MemBypassWriteMask, 0xFFFFFFFF); + + /* pm3 split up memory, replicates, and do a lot of nasty stuff IMHO ;-) */ + for (i = 0; i < 32; i++) { + fb_writel(i * 0x00345678, + (screen_mem + (i * 1048576))); + mb(); + temp1 = fb_readl((screen_mem + (i * 1048576))); + + /* Let's check for wrapover, write will fail at 16MB boundary */ + if (temp1 == (i * 0x00345678)) + memsize = i; + else + break; } - /* allocate remaining boards */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - if (fb_info[i].dev == NULL) { - done = 0; - for (j = 0; ((j < PM3_MAX_BOARD) && (!done)); j++) { - if (dev_array[j] != NULL) { - fb_info[i].dev = dev_array[j]; - dev_array[j] = NULL; - done = 1; - } - } + + DPRINTK("First detect pass already got %ld MB\n", memsize + 1); + + if (memsize + 1 == i) { + for (i = 0; i < 32; i++) { + /* Clear first 32MB ; 0 is 0, no need to byteswap */ + writel(0x0000000, + (screen_mem + (i * 1048576))); + mb(); } - } - /* at that point, all PCI Permedia3 are detected and allocated */ - /* now, initialize... or not */ - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - if (l_fb_info->dev && !disable[i]) { /* PCI device was found and not disabled by user */ - DPRINTK(2, - "found @%lx Vendor %lx Device %lx ; base @ : %lx - %lx - %lx - %lx - %lx - %lx, irq %ld\n", - (unsigned long) l_fb_info->dev, - (unsigned long) l_fb_info->dev->vendor, - (unsigned long) l_fb_info->dev->device, - (unsigned long) - pci_resource_start(l_fb_info->dev, 0), - (unsigned long) - pci_resource_start(l_fb_info->dev, 1), - (unsigned long) - pci_resource_start(l_fb_info->dev, 2), - (unsigned long) - pci_resource_start(l_fb_info->dev, 3), - (unsigned long) - pci_resource_start(l_fb_info->dev, 4), - (unsigned long) - pci_resource_start(l_fb_info->dev, 5), - (unsigned long) l_fb_info->dev->irq); - - l_fb_info->pIOBase = - (unsigned char *) - pci_resource_start(l_fb_info->dev, 0); -#ifdef __BIG_ENDIAN - l_fb_info->pIOBase += PM3_REGS_SIZE; -#endif - l_fb_info->vIOBase = (unsigned char *) -1; - l_fb_info->p_fb = - (unsigned char *) - pci_resource_start(l_fb_info->dev, 1); - l_fb_info->v_fb = (unsigned char *) -1; - - if (!request_mem_region - ((unsigned long)l_fb_info->p_fb, 64 * 1024 * 1024, /* request full aperture size */ - "pm3fb")) { - printk - (KERN_ERR "pm3fb: Error: couldn't request framebuffer memory, board #%ld\n", - l_fb_info->board_num); - continue; - } - if (!request_mem_region - ((unsigned long)l_fb_info->pIOBase, PM3_REGS_SIZE, - "pm3fb I/O regs")) { - printk - (KERN_ERR "pm3fb: Error: couldn't request IObase memory, board #%ld\n", - l_fb_info->board_num); - continue; - } - if (forcesize[l_fb_info->board_num]) - l_fb_info->fb_size = forcesize[l_fb_info->board_num]; - - l_fb_info->fb_size = - pm3fb_size_memory(l_fb_info); - if (l_fb_info->fb_size) { - (void) pci_enable_device(l_fb_info->dev); - pm3fb_common_init(l_fb_info); - } else - printk(KERN_ERR "pm3fb: memory problem, not enabling board #%ld\n", l_fb_info->board_num); + for (i = 32; i < 64; i++) { + fb_writel(i * 0x00345678, + (screen_mem + (i * 1048576))); + mb(); + temp1 = + fb_readl((screen_mem + (i * 1048576))); + temp2 = + fb_readl((screen_mem + ((i - 32) * 1048576))); + /* different value, different RAM... */ + if ((temp1 == (i * 0x00345678)) && (temp2 == 0)) + memsize = i; + else + break; } } -} + DPRINTK("Second detect pass got %ld MB\n", memsize + 1); -static int pm3fb_pan_display(const struct fb_var_screeninfo *var, - struct fb_info_gen *info) -{ - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; + PM3_SLOW_WRITE_REG(par, PM3MemBypassWriteMask, tempBypass); - DTRACE; + iounmap(screen_mem); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + memsize = 1048576 * (memsize + 1); - if (!current_par_valid[l_fb_info->board_num]) - return -EINVAL; + DPRINTK("Returning 0x%08lx bytes\n", memsize); - l_fb_info->current_par->base = /* in 128 bits chunk - i.e. AFTER Shiftbpp */ - pm3fb_Shiftbpp(l_fb_info, - l_fb_info->current_par->depth, - (var->yoffset * l_fb_info->current_par->width) + - var->xoffset); - PM3_SLOW_WRITE_REG(PM3ScreenBase, l_fb_info->current_par->base); - return 0; + return memsize; } -static int pm3fb_ioctl(struct fb_info *info, u_int cmd, u_long arg) +static int __devinit pm3fb_probe(struct pci_dev *dev, + const struct pci_device_id *ent) { - struct pm3fb_info *l_fb_info = (struct pm3fb_info *) info; - u32 cm, i; -#ifdef PM3FB_MASTER_DEBUG - char cc[3]; -#endif /* PM3FB_MASTER_DEBUG */ + struct fb_info *info; + struct pm3_par *par; + struct device* device = &dev->dev; /* for pci drivers */ + int err, retval = -ENXIO; - switch(cmd) - { -#ifdef PM3FB_MASTER_DEBUG - case PM3FBIO_CLEARMEMORY: - if (copy_from_user(&cm, (void *)arg, sizeof(u32))) - return(-EFAULT); - pm3fb_clear_memory(l_fb_info, cm); - return(0); - break; + err = pci_enable_device(dev); + if (err) { + printk(KERN_WARNING "pm3fb: Can't enable PCI dev: %d\n", err); + return err; + } + /* + * Dynamically allocate info and par + */ + info = framebuffer_alloc(sizeof(struct pm3_par), device); - case PM3FBIO_CLEARCMAP: - if (copy_from_user(cc, (void*)arg, 3 * sizeof(char))) - return(-EFAULT); - pm3fb_clear_colormap(l_fb_info, cc[0], cc[1], cc[2]); - return(0); - break; -#endif /* PM3FB_MASTER_DEBUG */ - - case PM3FBIO_RESETCHIP: - cm = 1; - PM3_SLOW_WRITE_REG(PM3ResetStatus, 1); - for (i = 0 ; (i < 10000) && cm ; i++) - { - PM3_DELAY(10); - cm = PM3_READ_REG(PM3ResetStatus); - } - if (cm) - { - printk(KERN_ERR "pm3fb: chip reset failed with status 0x%x\n", cm); - return(-EIO); - } - /* first thing first, reload memory timings */ - pm3fb_write_memory_timings(l_fb_info); -#ifdef PM3FB_USE_ACCEL - pm3fb_init_engine(l_fb_info); -#endif /* PM3FB_USE_ACCEL */ - pm3fb_write_mode(l_fb_info); - return(0); - break; + if (!info) + return -ENOMEM; + par = info->par; - default: - DPRINTK(2, "unknown ioctl: %d (%x)\n", cmd, cmd); - return(-EINVAL); + /* + * Here we set the screen_base to the virtual memory address + * for the framebuffer. + */ + pm3fb_fix.mmio_start = pci_resource_start(dev, 0); + pm3fb_fix.mmio_len = PM3_REGS_SIZE; + + /* Registers - request region and map it. */ + if (!request_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len, + "pm3fb regbase")) { + printk(KERN_WARNING "pm3fb: Can't reserve regbase.\n"); + goto err_exit_neither; + } + par->v_regs = + ioremap_nocache(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + if (!par->v_regs) { + printk(KERN_WARNING "pm3fb: Can't remap %s register area.\n", + pm3fb_fix.id); + release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + goto err_exit_neither; + } + +#if defined(__BIG_ENDIAN) + pm3fb_fix.mmio_start += PM3_REGS_SIZE; + DPRINTK("Adjusting register base for big-endian.\n"); +#endif + /* Linear frame buffer - request region and map it. */ + pm3fb_fix.smem_start = pci_resource_start(dev, 1); + pm3fb_fix.smem_len = pm3fb_size_memory(par); + if (!pm3fb_fix.smem_len) + { + printk(KERN_WARNING "pm3fb: Can't find memory on board.\n"); + goto err_exit_mmio; } -} - -/* ****************************************** */ -/* ***** standard FB API init functions ***** */ -/* ****************************************** */ + if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len, + "pm3fb smem")) { + printk(KERN_WARNING "pm3fb: Can't reserve smem.\n"); + goto err_exit_mmio; + } + info->screen_base = + ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + if (!info->screen_base) { + printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n"); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + goto err_exit_mmio; + } + info->screen_size = pm3fb_fix.smem_len; -int __init pm3fb_setup(char *options) -{ - long opsi = strlen(options); + info->fbops = &pm3fb_ops; - DTRACE; + par->video = PM3_READ_REG(par, PM3VideoControl); - memcpy(g_options, options, - ((opsi + 1) > - PM3_OPTIONS_SIZE) ? PM3_OPTIONS_SIZE : (opsi + 1)); - g_options[PM3_OPTIONS_SIZE - 1] = 0; + info->fix = pm3fb_fix; + info->pseudo_palette = par->palette; + info->flags = FBINFO_DEFAULT;/* | FBINFO_HWACCEL_YPAN;*/ - return (0); -} + /* + * This should give a reasonable default video mode. The following is + * done when we can set a video mode. + */ + if (!mode_option) + mode_option = "640x480@60"; -int __init pm3fb_init(void) -{ - DTRACE; + retval = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); - DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $"); + if (!retval || retval == 4) { + retval = -EINVAL; + goto err_exit_both; + } - pm3fb_real_setup(g_options); + /* This has to been done !!! */ + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + retval = -ENOMEM; + goto err_exit_both; + } - pm3fb_detect(); + /* + * For drivers that can... + */ + pm3fb_check_var(&info->var, info); - if (!fb_info[0].dev) { /* not even one board ??? */ - DPRINTK(1, "No PCI Permedia3 board detected\n"); + if (register_framebuffer(info) < 0) { + retval = -EINVAL; + goto err_exit_all; } - return (0); + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + pci_set_drvdata(dev, info); /* or dev_set_drvdata(device, info) */ + return 0; + + err_exit_all: + fb_dealloc_cmap(&info->cmap); + err_exit_both: + iounmap(info->screen_base); + release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len); + err_exit_mmio: + iounmap(par->v_regs); + release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len); + err_exit_neither: + framebuffer_release(info); + return retval; } -/* ************************* */ -/* **** Module support ***** */ -/* ************************* */ + /* + * Cleanup + */ +static void __devexit pm3fb_remove(struct pci_dev *dev) +{ + struct fb_info *info = pci_get_drvdata(dev); -#ifdef MODULE -MODULE_AUTHOR("Romain Dolbeau"); -MODULE_DESCRIPTION("Permedia3 framebuffer device driver"); -static char *mode[PM3_MAX_BOARD]; -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode,"video mode"); -module_param_array(disable, short, NULL, 0); -MODULE_PARM_DESC(disable,"disable board"); -static short off[PM3_MAX_BOARD]; -module_param_array(off, short, NULL, 0); -MODULE_PARM_DESC(off,"disable board"); -static char *pciid[PM3_MAX_BOARD]; -module_param_array(pciid, charp, NULL, 0); -MODULE_PARM_DESC(pciid,"board PCI Id"); -module_param_array(noaccel, short, NULL, 0); -MODULE_PARM_DESC(noaccel,"disable accel"); -static char *font[PM3_MAX_BOARD]; -module_param_array(font, charp, NULL, 0); -MODULE_PARM_DESC(font,"choose font"); -module_param(depth, short, NULL, 0); -MODULE_PARM_DESC(depth,"boot-time depth"); -module_param(printtimings, short, NULL, 0); -MODULE_PARM_DESC(printtimings, "print the memory timings of the card(s)"); -module_param(forcesize, short, NULL, 0); -MODULE_PARM_DESC(forcesize, "force specified memory size"); -/* -MODULE_SUPPORTED_DEVICE("Permedia3 PCI boards") -MODULE_GENERIC_TABLE(gtype,name) -MODULE_DEVICE_TABLE(type,name) -*/ + if (info) { + struct fb_fix_screeninfo *fix = &info->fix; + struct pm3_par *par = info->par; -void pm3fb_build_options(void) -{ - int i; - char ts[128]; + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); - strcpy(g_options, "pm3fb"); - for (i = 0; i < PM3_MAX_BOARD ; i++) - { - if (mode[i]) - { - sprintf(ts, ",mode:%d:%s", i, mode[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (disable[i] || off[i]) - { - sprintf(ts, ",disable:%d:", i); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (pciid[i]) - { - sprintf(ts, ",pciid:%d:%s", i, pciid[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (noaccel[i]) - { - sprintf(ts, ",noaccel:%d:", i); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (font[i]) - { - sprintf(ts, ",font:%d:%s", i, font[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } - if (depth[i]) - { - sprintf(ts, ",depth:%d:%d", i, depth[i]); - strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options)); - } + iounmap(info->screen_base); + release_mem_region(fix->smem_start, fix->smem_len); + iounmap(par->v_regs); + release_mem_region(fix->mmio_start, fix->mmio_len); + + pci_set_drvdata(dev, NULL); + framebuffer_release(info); } - g_options[PM3_OPTIONS_SIZE - 1] = '\0'; - DPRINTK(1, "pm3fb use options: %s\n", g_options); } -int init_module(void) +static struct pci_device_id pm3fb_id_table[] = { + { PCI_VENDOR_ID_3DLABS, 0x0a, + PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, + 0xff0000, 0 }, + { 0, } +}; + +/* For PCI drivers */ +static struct pci_driver pm3fb_driver = { + .name = "pm3fb", + .id_table = pm3fb_id_table, + .probe = pm3fb_probe, + .remove = __devexit_p(pm3fb_remove), +}; + +MODULE_DEVICE_TABLE(pci, pm3fb_id_table); + +int __init pm3fb_init(void) { - DTRACE; + /* + * For kernel boot options (in 'video=pm3fb:<options>' format) + */ +#ifndef MODULE + char *option = NULL; - pm3fb_build_options(); + if (fb_get_options("pm3fb", &option)) + return -ENODEV; + pm3fb_setup(option); +#endif - pm3fb_init(); + return pci_register_driver(&pm3fb_driver); +} - return 0; +static void __exit pm3fb_exit(void) +{ + pci_unregister_driver(&pm3fb_driver); } -void cleanup_module(void) +#ifdef MODULE + /* + * Setup + */ + +/* + * Only necessary if your driver takes special options, + * otherwise we fall back on the generic fb_setup(). + */ +int __init pm3fb_setup(char *options) { - DTRACE; - { - unsigned long i; - struct pm3fb_info *l_fb_info; - for (i = 0; i < PM3_MAX_BOARD; i++) { - l_fb_info = &(fb_info[i]); - pci_dev_put(l_fb_info->dev); - if (l_fb_info->dev != NULL && !(disable[l_fb_info->board_num])) { - if (l_fb_info->vIOBase != (unsigned char *) -1) { - pm3fb_unmapIO(l_fb_info); - release_mem_region(l_fb_info->p_fb, - l_fb_info->fb_size); - release_mem_region(l_fb_info->pIOBase, - PM3_REGS_SIZE); - } - unregister_framebuffer(&l_fb_info->gen.info); - } - } - } + /* Parse user speficied options (`video=pm3fb:') */ + return 0; } #endif /* MODULE */ + +module_init(pm3fb_init); +module_exit(pm3fb_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/video/riva/rivafb-i2c.c b/drivers/video/riva/rivafb-i2c.c index 76e6ce3..a0e22ac 100644 --- a/drivers/video/riva/rivafb-i2c.c +++ b/drivers/video/riva/rivafb-i2c.c @@ -70,8 +70,6 @@ static int riva_gpio_getscl(void* data) if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x04) val = 1; - val = VGA_RD08(par->riva.PCIO, 0x3d5); - return val; } |