diff --git a/packages/linux/meta b/packages/linux/meta index 7ef67b2fae..cdd0ea125f 100644 --- a/packages/linux/meta +++ b/packages/linux/meta @@ -19,13 +19,13 @@ ################################################################################ PKG_NAME="linux" -PKG_VERSION="2.6.38-rc8" +PKG_VERSION="2.6.38" PKG_REV="1" PKG_ARCH="any" PKG_LICENSE="GPL" PKG_SITE="http://www.kernel.org" -#PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/$PKG_NAME-$PKG_VERSION.tar.bz2" -PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/testing/$PKG_NAME-$PKG_VERSION.tar.bz2" +PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/$PKG_NAME-$PKG_VERSION.tar.bz2" +#PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/testing/$PKG_NAME-$PKG_VERSION.tar.bz2" PKG_DEPENDS="busybox linux-drivers linux-firmware" PKG_BUILD_DEPENDS="toolchain busybox-hosttools xz" PKG_PRIORITY="optional" @@ -37,6 +37,7 @@ PKG_IS_ADDON="no" PKG_AUTORECONF="no" if [ "$LINUX_NEXT" = "yes" ]; then - PKG_VERSION="2.6.38-rc7" - PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/testing/$PKG_NAME-$PKG_VERSION.tar.bz2" + PKG_VERSION="2.6.38" + PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/$PKG_NAME-$PKG_VERSION.tar.bz2" +# PKG_URL="http://www.kernel.org/pub/linux/kernel/v2.6/testing/$PKG_NAME-$PKG_VERSION.tar.bz2" fi diff --git a/packages/linux/patches/linux-2.6.38-000_crosscompile.patch b/packages/linux/patches/linux-2.6.38-000_crosscompile.patch new file mode 100644 index 0000000000..b4fc575828 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-000_crosscompile.patch @@ -0,0 +1,22 @@ +--- linux-2.6.24-rc2.orig/arch/x86/boot/tools/build.c 2007-10-06 12:26:14.000000000 +0200 ++++ linux-2.6.24-rc2/arch/x86/boot/tools/build.c 2007-10-06 12:27:36.000000000 +0200 +@@ -29,7 +29,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -42,6 +41,11 @@ + #define DEFAULT_MAJOR_ROOT 0 + #define DEFAULT_MINOR_ROOT 0 + ++#undef major ++#define major(dev) ((int)(((dev) >> 8) & 0xff)) ++#undef minor ++#define minor(dev) ((int)((dev) & 0xff)) ++ + /* Minimal number of setup sectors */ + #define SETUP_SECT_MIN 5 + #define SETUP_SECT_MAX 64 diff --git a/packages/linux/patches/linux-2.6.38-002_bash_only_feature.patch b/packages/linux/patches/linux-2.6.38-002_bash_only_feature.patch new file mode 100644 index 0000000000..a1028d15aa --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-002_bash_only_feature.patch @@ -0,0 +1,15 @@ +Index: linux-2.6.16/scripts/gen_initramfs_list.sh +=================================================================== +--- linux-2.6.16.orig/scripts/gen_initramfs_list.sh 2006-03-20 18:41:34.000000000 +0100 ++++ linux-2.6.16/scripts/gen_initramfs_list.sh 2006-03-20 18:42:40.000000000 +0100 +@@ -56,9 +56,7 @@ + + parse() { + local location="$1" +- local name="${location/${srcdir}//}" +- # change '//' into '/' +- name="${name//\/\///}" ++ local name="$(echo "$location" | sed -e 's%$srcdir%%' -e 's%//*%/%g')" + local mode="$2" + local uid="$3" + local gid="$4" diff --git a/packages/linux/patches/linux-2.6.38-003-no_dev_console.patch b/packages/linux/patches/linux-2.6.38-003-no_dev_console.patch new file mode 100644 index 0000000000..9b5e51437d --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-003-no_dev_console.patch @@ -0,0 +1,20 @@ +diff -Naur linux-2.6.34-rc7/init/main.c linux-2.6.34-rc7.patch/init/main.c +--- linux-2.6.34-rc7/init/main.c 2010-05-10 03:36:28.000000000 +0200 ++++ linux-2.6.34-rc7.patch/init/main.c 2010-05-15 12:28:34.767241760 +0200 +@@ -886,8 +886,14 @@ + do_basic_setup(); + + /* Open the /dev/console on the rootfs, this should never fail */ +- if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) +- printk(KERN_WARNING "Warning: unable to open an initial console.\n"); ++ char *console = "/dev_console"; ++ ++ if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) { ++ sys_mknod(console, S_IFCHR|0600, (TTYAUX_MAJOR<<8)|1); ++ if (sys_open(console, O_RDWR, 0) < 0) ++ printk(KERN_WARNING "Warning: unable to open an initial console.\n"); ++ sys_unlink(console); ++ } + + (void) sys_dup(0); + (void) sys_dup(0); diff --git a/packages/linux/patches/linux-2.6.38-004_lower_undefined_mode_timeout.patch b/packages/linux/patches/linux-2.6.38-004_lower_undefined_mode_timeout.patch new file mode 100644 index 0000000000..a0aca61d23 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-004_lower_undefined_mode_timeout.patch @@ -0,0 +1,24 @@ +diff -Naur linux-2.6.23-rc9.orig/arch/i386/boot/tty.c linux-2.6.23-rc9/arch/i386/boot/tty.c +--- linux-2.6.23-rc9.orig/arch/x86/boot/tty.c 2007-10-06 12:26:14.000000000 +0200 ++++ linux-2.6.23-rc9/arch/x86/boot/tty.c 2007-10-06 12:37:47.000000000 +0200 +@@ -92,7 +92,7 @@ + + int getchar_timeout(void) + { +- int cnt = 30; ++ int cnt = 3; + int t0, t1; + + t0 = gettime(); +diff -Naur linux-2.6.23-rc9.orig/arch/i386/boot/video.c linux-2.6.23-rc9/arch/i386/boot/video.c +--- linux-2.6.23-rc9.orig/arch/x86/boot/video.c 2007-10-06 12:26:14.000000000 +0200 ++++ linux-2.6.23-rc9/arch/x86/boot/video.c 2007-10-06 12:36:05.000000000 +0200 +@@ -329,7 +329,7 @@ + unsigned int sel; + + puts("Press to see video modes available, " +- " to continue, or wait 30 sec\n"); ++ " to continue, or wait 3 sec\n"); + + kbd_flush(); + while (1) { diff --git a/packages/linux/patches/linux-2.6.38-005_kconfig_no_timestamp.patch b/packages/linux/patches/linux-2.6.38-005_kconfig_no_timestamp.patch new file mode 100644 index 0000000000..332e553831 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-005_kconfig_no_timestamp.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.16/scripts/kconfig/confdata.c +=================================================================== +--- linux-2.6.16.orig/scripts/kconfig/confdata.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16/scripts/kconfig/confdata.c 2006-03-20 18:47:06.000000000 +0100 +@@ -340,7 +340,7 @@ + int type, l; + const char *str; + time_t now; +- int use_timestamp = 1; ++ int use_timestamp = 0; + char *env; + + dirname[0] = 0; diff --git a/packages/linux/patches/linux-2.6.38-006_enable_utf8.patch b/packages/linux/patches/linux-2.6.38-006_enable_utf8.patch new file mode 100644 index 0000000000..bee1cf3da8 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-006_enable_utf8.patch @@ -0,0 +1,25 @@ +diff -Naur linux-2.6.31-rc4.orig/fs/fat/inode.c linux-2.6.31-rc4/fs/fat/inode.c +--- linux-2.6.31-rc4.orig/fs/fat/inode.c 2009-07-25 12:47:41.000000000 +0200 ++++ linux-2.6.31-rc4/fs/fat/inode.c 2009-07-25 13:38:18.000000000 +0200 +@@ -979,7 +979,8 @@ + } + opts->name_check = 'n'; + opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0; +- opts->utf8 = opts->unicode_xlate = 0; ++ opts->utf8 = 1; ++ opts->unicode_xlate = 0; + opts->numtail = 1; + opts->usefree = opts->nocase = 0; + opts->tz_utc = 0; +diff -Naur linux-2.6.31-rc4.orig/fs/isofs/inode.c linux-2.6.31-rc4/fs/isofs/inode.c +--- linux-2.6.31-rc4.orig/fs/isofs/inode.c 2009-07-25 12:47:41.000000000 +0200 ++++ linux-2.6.31-rc4/fs/isofs/inode.c 2009-07-25 13:38:49.000000000 +0200 +@@ -377,7 +377,7 @@ + popt->gid = 0; + popt->uid = 0; + popt->iocharset = NULL; +- popt->utf8 = 0; ++ popt->utf8 = 1; + popt->overriderockperm = 0; + popt->session=-1; + popt->sbsector=-1; diff --git a/packages/linux/patches/linux-2.6.38-007_die_floppy_die.patch b/packages/linux/patches/linux-2.6.38-007_die_floppy_die.patch new file mode 100644 index 0000000000..76db312182 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-007_die_floppy_die.patch @@ -0,0 +1,30 @@ +From 4ff58b642f80dedb20533978123d89b5ac9b1ed5 Mon Sep 17 00:00:00 2001 +From: Kyle McMartin +Date: Tue, 30 Mar 2010 00:04:29 -0400 +Subject: die-floppy-die + +Kill the floppy.ko pnp modalias. We were surviving just fine without +autoloading floppy drivers, tyvm. + +Please feel free to register all complaints in the wastepaper bin. +--- + drivers/block/floppy.c | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c +index 90c4038..f4a0b90 100644 +--- a/drivers/block/floppy.c ++++ b/drivers/block/floppy.c +@@ -4619,8 +4619,7 @@ static const struct pnp_device_id floppy_pnpids[] = { + {"PNP0700", 0}, + {} + }; +- +-MODULE_DEVICE_TABLE(pnp, floppy_pnpids); ++/* MODULE_DEVICE_TABLE(pnp, floppy_pnpids); */ + + #else + +-- +1.7.0.1 + diff --git a/packages/linux/patches/linux-2.6.38-008-hda_intel_prealloc_4mb_dmabuffer.patch b/packages/linux/patches/linux-2.6.38-008-hda_intel_prealloc_4mb_dmabuffer.patch new file mode 100644 index 0000000000..36e6aca4fa --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-008-hda_intel_prealloc_4mb_dmabuffer.patch @@ -0,0 +1,47 @@ +From c69fcbd1f60b0842f7c1ad2c95692ffd19c4932b Mon Sep 17 00:00:00 2001 +From: Kyle McMartin +Date: Mon, 29 Mar 2010 23:56:08 -0400 +Subject: hda_intel-prealloc-4mb-dmabuffer + +--- + sound/pci/hda/hda_intel.c | 14 +++++++++++++- + 1 files changed, 13 insertions(+), 1 deletions(-) + +diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c +index 4bb9067..37db515 100644 +--- a/sound/pci/hda/hda_intel.c ++++ b/sound/pci/hda/hda_intel.c +@@ -1986,6 +1986,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, + struct azx_pcm *apcm; + int pcm_dev = cpcm->device; + int s, err; ++ size_t prealloc_min = 64*1024; /* 64KB */ + + if (pcm_dev >= HDA_MAX_PCMS) { + snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n", +@@ -2019,10 +2020,21 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, + if (cpcm->stream[s].substreams) + snd_pcm_set_ops(pcm, s, &azx_pcm_ops); + } ++ + /* buffer pre-allocation */ ++ ++ /* subtle, don't allocate a big buffer for modems... ++ * also, don't just test 32BIT_MASK, since azx supports ++ * 64-bit DMA in some cases. ++ */ ++ /* lennart wants a 2.2MB buffer for 2sec of 48khz */ ++ if (pcm->dev_class == SNDRV_PCM_CLASS_GENERIC && ++ chip->pci->dma_mask >= DMA_32BIT_MASK) ++ prealloc_min = 4 * 1024 * 1024; /* 4MB */ ++ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), +- 1024 * 64, 32 * 1024 * 1024); ++ prealloc_min, 32 * 1024 * 1024); + return 0; + } + +-- +1.7.0.1 + diff --git a/packages/linux/patches/linux-2.6.38-009_disable_i8042_check_on_apple_mac.patch b/packages/linux/patches/linux-2.6.38-009_disable_i8042_check_on_apple_mac.patch new file mode 100644 index 0000000000..f99d0f900c --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-009_disable_i8042_check_on_apple_mac.patch @@ -0,0 +1,59 @@ +From 2a79554c864ac58fa2ad982f0fcee2cc2aa33eb5 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Thu, 20 May 2010 10:30:31 -0400 +Subject: Disable i8042 checks on Intel Apple Macs + +As those computers never had any i8042 controllers, and the +current lookup code could potentially lock up/hang/wait for +timeout for long periods of time. + +Fixes intermittent hangs on boot on a MacbookAir1,1 + +Signed-off-by: Bastien Nocera +--- + drivers/input/serio/i8042.c | 22 ++++++++++++++++++++++ + 1 files changed, 22 insertions(+), 0 deletions(-) + +diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c +index 6440a8f..4d7cf98 100644 +--- a/drivers/input/serio/i8042.c ++++ b/drivers/input/serio/i8042.c +@@ -1451,6 +1451,22 @@ static struct platform_driver i8042_driver = { + .shutdown = i8042_shutdown, + }; + ++#ifdef CONFIG_DMI ++static struct dmi_system_id __initdata dmi_system_table[] = { ++ { ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR, "Apple Computer, Inc.") ++ }, ++ }, ++ { ++ .matches = { ++ DMI_MATCH(DMI_BIOS_VENDOR, "Apple Inc.") ++ }, ++ }, ++ {} ++}; ++#endif /*CONFIG_DMI*/ ++ + static int __init i8042_init(void) + { + struct platform_device *pdev; +@@ -1458,6 +1474,12 @@ static int __init i8042_init(void) + + dbg_init(); + ++#ifdef CONFIG_DMI ++ /* Intel Apple Macs never have an i8042 controller */ ++ if (dmi_check_system(dmi_system_table) > 0) ++ return -ENODEV; ++#endif /*CONFIG_DMI*/ ++ + err = i8042_platform_init(); + if (err) + return err; +-- +1.7.0.1 + diff --git a/packages/linux/patches/linux-2.6.38-050_add_appleir_usb_driver.patch b/packages/linux/patches/linux-2.6.38-050_add_appleir_usb_driver.patch new file mode 100644 index 0000000000..61edb8061e --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-050_add_appleir_usb_driver.patch @@ -0,0 +1,702 @@ +From e11e9e78799a7641fe0dc5289f35f2604a4b71a3 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Sun, 17 Jan 2010 00:40:15 +0000 +Subject: [PATCH] Input: add appleir USB driver + +This driver was originally written by James McKenzie, updated by +Greg Kroah-Hartman, further updated by myself, with suspend support +added. + +More recent versions of the IR receiver are also supported through +a patch by Alex Karpenko. The patch also adds support for the 2nd +and 5th generation of the controller, and the menu key on newer +brushed metal remotes. + +Tested on a MacbookAir1,1 + +Signed-off-by: Bastien Nocera +--- + Documentation/input/appleir.txt | 46 ++++ + drivers/hid/hid-apple.c | 4 - + drivers/hid/hid-core.c | 7 +- + drivers/hid/hid-ids.h | 5 +- + drivers/input/misc/Kconfig | 13 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/appleir.c | 519 +++++++++++++++++++++++++++++++++++++++ + 7 files changed, 588 insertions(+), 7 deletions(-) + create mode 100644 Documentation/input/appleir.txt + create mode 100644 drivers/input/misc/appleir.c + +diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt +new file mode 100644 +index 0000000..db637fb +--- /dev/null ++++ b/Documentation/input/appleir.txt +@@ -0,0 +1,46 @@ ++Apple IR receiver Driver (appleir) ++---------------------------------- ++ Copyright (C) 2009 Bastien Nocera ++ ++The appleir driver is a kernel input driver to handle Apple's IR ++receivers (and associated remotes) in the kernel. ++ ++The driver is an input driver which only handles "official" remotes ++as built and sold by Apple. ++ ++Authors ++------- ++ ++James McKenzie (original driver) ++Alex Karpenko (05ac:8242 support) ++Greg Kroah-Hartman (cleanups and original submission) ++Bastien Nocera (further cleanups, brushed metal "enter" ++button support and suspend support) ++ ++Supported hardware ++------------------ ++ ++- All Apple laptops and desktops from 2005 onwards, except: ++ - the unibody Macbook (2009) ++ - Mac Pro (all versions) ++- Apple TV (all revisions prior to September 2010) ++ ++The remote will only support the 6 (old white) or 7 (brushed metal) buttons ++of the remotes as sold by Apple. See the next section if you want to use ++other remotes or want to use lirc with the device instead of the kernel driver. ++ ++Using lirc (native) instead of the kernel driver ++------------------------------------------------ ++ ++First, you will need to disable the kernel driver for the receiver. ++ ++This can be achieved by passing quirks to the usbhid driver. ++The quirk line would be: ++usbhid.quirks=0x05ac:0x8242:0x40000010 ++ ++With 0x05ac being the vendor ID (Apple, you shouldn't need to change this) ++With 0x8242 being the product ID (check the output of lsusb for your hardware) ++And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE" ++ ++This should force the creation of a hiddev device for the receiver, and ++make it usable under lirc. +diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c +index bba05d0..0059d5a 100644 +--- a/drivers/hid/hid-apple.c ++++ b/drivers/hid/hid-apple.c +@@ -361,10 +361,6 @@ static void apple_remove(struct hid_device *hdev) + } + + static const struct hid_device_id apple_devices[] = { +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL), +- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4), +- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), + .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index baa25ad..abc5bd7 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1244,8 +1244,6 @@ static const struct hid_device_id hid_blacklist[] = { + #if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) + { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, + #endif +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, +@@ -1577,6 +1575,11 @@ static const struct hid_device_id hid_ignore_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)}, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)}, + { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index 11af537..360a5ca 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -100,8 +100,11 @@ + #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b + #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a + #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +-#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 ++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 ++#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 ++#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 + #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 ++#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 + + #define USB_VENDOR_ID_ASUS 0x0486 + #define USB_DEVICE_ID_ASUS_T91MT 0x0185 +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 60de906..2f2f2e7 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -209,6 +209,19 @@ config INPUT_KEYSPAN_REMOTE + To compile this driver as a module, choose M here: the module will + be called keyspan_remote. + ++config INPUT_APPLEIR ++ tristate "Apple infrared receiver (built in)" ++ depends on USB_ARCH_HAS_HCD ++ select USB ++ help ++ Say Y here if you want to use a Apple infrared remote control. All ++ the Apple computers from 2005 onwards include such a port, except ++ the unibody Macbook (2009), and Mac Pros. This receiver is also ++ used in the Apple TV set-top box prior to the 2010 model. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called appleir. ++ + config INPUT_POWERMATE + tristate "Griffin PowerMate and Contour Jog support" + depends on USB_ARCH_HAS_HCD +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 1fe1f6c..d5ef2b9 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -13,6 +13,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o + obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o + obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o + obj-$(CONFIG_INPUT_APANEL) += apanel.o ++obj-$(CONFIG_INPUT_APPLEIR) += appleir.o + obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o + obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o + obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o +diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c +new file mode 100644 +index 0000000..3817a3c +--- /dev/null ++++ b/drivers/input/misc/appleir.c +@@ -0,0 +1,519 @@ ++/* ++ * appleir: USB driver for the apple ir device ++ * ++ * Original driver written by James McKenzie ++ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman ++ * ++ * Copyright (C) 2006 James McKenzie ++ * Copyright (C) 2008 Greg Kroah-Hartman ++ * Copyright (C) 2008 Novell Inc. ++ * ++ * 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, version 2. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "v1.2" ++#define DRIVER_AUTHOR "James McKenzie" ++#define DRIVER_DESC "Apple infrared receiver driver" ++#define DRIVER_LICENSE "GPL" ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE(DRIVER_LICENSE); ++ ++#define USB_VENDOR_ID_APPLE 0x05ac ++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 ++#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 ++#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 ++#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 ++#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 ++ ++#define URB_SIZE 32 ++ ++#define MAX_KEYS 9 ++#define MAX_KEYS_MASK (MAX_KEYS - 1) ++ ++#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); ++ ++/* I have two devices both of which report the following */ ++/* 25 87 ee 83 0a + */ ++/* 25 87 ee 83 0c - */ ++/* 25 87 ee 83 09 << */ ++/* 25 87 ee 83 06 >> */ ++/* 25 87 ee 83 05 >" */ ++/* 25 87 ee 83 03 menu */ ++/* 26 00 00 00 00 for key repeat*/ ++ ++/* Thomas Glanzmann reports the following responses */ ++/* 25 87 ee ca 0b + */ ++/* 25 87 ee ca 0d - */ ++/* 25 87 ee ca 08 << */ ++/* 25 87 ee ca 07 >> */ ++/* 25 87 ee ca 04 >" */ ++/* 25 87 ee ca 02 menu */ ++/* 26 00 00 00 00 for key repeat*/ ++/* He also observes the following event sometimes */ ++/* sent after a key is release, which I interpret */ ++/* as a flat battery message */ ++/* 25 87 e0 ca 06 flat battery */ ++ ++/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */ ++/* 25 87 ee 47 0b + */ ++/* 25 87 ee 47 0d - */ ++/* 25 87 ee 47 08 << */ ++/* 25 87 ee 47 07 >> */ ++/* 25 87 ee 47 04 >" */ ++/* 25 87 ee 47 02 menu */ ++/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */ ++ ++/* Bastien Nocera's "new" remote */ ++/* 25 87 ee 91 5f followed by ++ * 25 87 ee 91 05 gives you >" ++ * ++ * 25 87 ee 91 5c followed by ++ * 25 87 ee 91 05 gives you the middle button */ ++ ++static const unsigned short appleir_key_table[] = { ++ KEY_RESERVED, ++ KEY_MENU, ++ KEY_PLAYPAUSE, ++ KEY_FORWARD, ++ KEY_BACK, ++ KEY_VOLUMEUP, ++ KEY_VOLUMEDOWN, ++ KEY_ENTER, ++ KEY_RESERVED, ++}; ++ ++struct appleir { ++ struct input_dev *input_dev; ++ unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; ++ u8 *data; ++ dma_addr_t dma_buf; ++ struct usb_device *usbdev; ++ unsigned int flags; ++ struct urb *urb; ++ struct timer_list key_up_timer; ++ int current_key; ++ int prev_key_idx; ++ char phys[32]; ++}; ++ ++static DEFINE_MUTEX(appleir_mutex); ++ ++enum { ++ APPLEIR_OPENED = 0x1, ++ APPLEIR_SUSPENDED = 0x2, ++}; ++ ++static struct usb_device_id appleir_ids[] = { ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, ++ {} ++}; ++MODULE_DEVICE_TABLE(usb, appleir_ids); ++ ++static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len) ++{ ++ int i; ++ ++ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len); ++ ++ for (i = 0; i < len; ++i) ++ printk(" %02x", data[i]); ++ printk(" (should be command %d)\n", (data[4] >> 1) & MAX_KEYS_MASK); ++} ++ ++static int get_key(int data) ++{ ++ switch (data) { ++ case 0x02: ++ case 0x03: ++ /* menu */ ++ return 1; ++ case 0x04: ++ case 0x05: ++ /* >" */ ++ return 2; ++ case 0x06: ++ case 0x07: ++ /* >> */ ++ return 3; ++ case 0x08: ++ case 0x09: ++ /* << */ ++ return 4; ++ case 0x0a: ++ case 0x0b: ++ /* + */ ++ return 5; ++ case 0x0c: ++ case 0x0d: ++ /* - */ ++ return 6; ++ case 0x5c: ++ /* Middle button, on newer remotes, ++ * part of a 2 packet-command */ ++ return -7; ++ default: ++ return -1; ++ } ++} ++ ++static void key_up(struct appleir *appleir, int key) ++{ ++ dbginfo(&appleir->input_dev->dev, "key %d up\n", key); ++ input_report_key(appleir->input_dev, key, 0); ++ input_sync(appleir->input_dev); ++} ++ ++static void key_down(struct appleir *appleir, int key) ++{ ++ dbginfo(&appleir->input_dev->dev, "key %d down\n", key); ++ input_report_key(appleir->input_dev, key, 1); ++ input_sync(appleir->input_dev); ++} ++ ++static void battery_flat(struct appleir *appleir) ++{ ++ dev_err(&appleir->input_dev->dev, "possible flat battery?\n"); ++} ++ ++static void key_up_tick(unsigned long data) ++{ ++ struct appleir *appleir = (struct appleir *)data; ++ ++ if (appleir->current_key) { ++ key_up(appleir, appleir->current_key); ++ appleir->current_key = 0; ++ } ++} ++ ++static void new_data(struct appleir *appleir, u8 *data, int len) ++{ ++ static const u8 keydown[] = { 0x25, 0x87, 0xee }; ++ static const u8 keyrepeat[] = { 0x26, }; ++ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 }; ++ ++ if (debug) ++ dump_packet(appleir, "received", data, len); ++ ++ if (len != 5) ++ return; ++ ++ if (!memcmp(data, keydown, sizeof(keydown))) { ++ int index; ++ ++ /* If we already have a key down, take it up before marking ++ this one down */ ++ if (appleir->current_key) ++ key_up(appleir, appleir->current_key); ++ ++ /* Handle dual packet commands */ ++ if (appleir->prev_key_idx > 0) ++ index = appleir->prev_key_idx; ++ else ++ index = get_key(data[4]); ++ ++ if (index > 0) { ++ appleir->current_key = appleir->keymap[index]; ++ ++ key_down(appleir, appleir->current_key); ++ /* Remote doesn't do key up, either pull them up, in the test ++ above, or here set a timer which pulls them up after 1/8 s */ ++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); ++ appleir->prev_key_idx = 0; ++ return; ++ } else if (index == -7) { ++ /* Remember key for next packet */ ++ appleir->prev_key_idx = 0 - index; ++ return; ++ } ++ } ++ ++ appleir->prev_key_idx = 0; ++ ++ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { ++ key_down(appleir, appleir->current_key); ++ /* Remote doesn't do key up, either pull them up, in the test ++ above, or here set a timer which pulls them up after 1/8 s */ ++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); ++ return; ++ } ++ ++ if (!memcmp(data, flatbattery, sizeof(flatbattery))) { ++ battery_flat(appleir); ++ /* Fall through */ ++ } ++ ++ dump_packet(appleir, "unknown packet", data, len); ++} ++ ++static void appleir_urb(struct urb *urb) ++{ ++ struct appleir *appleir = urb->context; ++ int status = urb->status; ++ int retval; ++ ++ switch (status) { ++ case 0: ++ new_data(appleir, urb->transfer_buffer, urb->actual_length); ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* This urb is terminated, clean up */ ++ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__, ++ urb->status); ++ return; ++ default: ++ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__, ++ urb->status); ++ } ++ ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) ++ err("%s - usb_submit_urb failed with result %d", __func__, ++ retval); ++} ++ ++static int appleir_open(struct input_dev *dev) ++{ ++ struct appleir *appleir = input_get_drvdata(dev); ++ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0); ++ int r; ++ ++ r = usb_autopm_get_interface(intf); ++ if (r) { ++ dev_err(&intf->dev, ++ "%s(): usb_autopm_get_interface() = %d\n", __func__, r); ++ return r; ++ } ++ ++ mutex_lock(&appleir_mutex); ++ ++ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) { ++ r = -EIO; ++ goto fail; ++ } ++ ++ appleir->flags |= APPLEIR_OPENED; ++ ++ mutex_unlock(&appleir_mutex); ++ ++ usb_autopm_put_interface(intf); ++ ++ return 0; ++fail: ++ mutex_unlock(&appleir_mutex); ++ usb_autopm_put_interface(intf); ++ return r; ++} ++ ++static void appleir_close(struct input_dev *dev) ++{ ++ struct appleir *appleir = input_get_drvdata(dev); ++ ++ mutex_lock(&appleir_mutex); ++ ++ if (!(appleir->flags & APPLEIR_SUSPENDED)) { ++ usb_kill_urb(appleir->urb); ++ del_timer_sync(&appleir->key_up_timer); ++ } ++ ++ appleir->flags &= ~APPLEIR_OPENED; ++ ++ mutex_unlock(&appleir_mutex); ++} ++ ++static int appleir_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct usb_endpoint_descriptor *endpoint; ++ struct appleir *appleir = NULL; ++ struct input_dev *input_dev; ++ int retval = -ENOMEM; ++ int i; ++ ++ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL); ++ if (!appleir) ++ goto allocfail; ++ ++ appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL, ++ &appleir->dma_buf); ++ if (!appleir->data) ++ goto usbfail; ++ ++ appleir->urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!appleir->urb) ++ goto urbfail; ++ ++ appleir->usbdev = dev; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) ++ goto inputfail; ++ ++ appleir->input_dev = input_dev; ++ ++ usb_make_path(dev, appleir->phys, sizeof(appleir->phys)); ++ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys)); ++ ++ input_dev->name = "Apple Infrared Remote Controller"; ++ input_dev->phys = appleir->phys; ++ usb_to_input_id(dev, &input_dev->id); ++ input_dev->dev.parent = &intf->dev; ++ input_dev->keycode = appleir->keymap; ++ input_dev->keycodesize = sizeof(unsigned short); ++ input_dev->keycodemax = ARRAY_SIZE(appleir->keymap); ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); ++ ++ memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap)); ++ for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) ++ set_bit(appleir->keymap[i], input_dev->keybit); ++ clear_bit(KEY_RESERVED, input_dev->keybit); ++ ++ input_set_drvdata(input_dev, appleir); ++ input_dev->open = appleir_open; ++ input_dev->close = appleir_close; ++ ++ endpoint = &intf->cur_altsetting->endpoint[0].desc; ++ ++ usb_fill_int_urb(appleir->urb, dev, ++ usb_rcvintpipe(dev, endpoint->bEndpointAddress), ++ appleir->data, 8, ++ appleir_urb, appleir, endpoint->bInterval); ++ ++ appleir->urb->transfer_dma = appleir->dma_buf; ++ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ setup_timer(&appleir->key_up_timer, ++ key_up_tick, (unsigned long) appleir); ++ ++ retval = input_register_device(appleir->input_dev); ++ if (retval) ++ goto inputfail; ++ ++ usb_set_intfdata(intf, appleir); ++ ++ return 0; ++ ++inputfail: ++ input_free_device(appleir->input_dev); ++ ++urbfail: ++ usb_free_urb(appleir->urb); ++ ++usbfail: ++ usb_free_coherent(dev, URB_SIZE, appleir->data, ++ appleir->dma_buf); ++ ++allocfail: ++ kfree(appleir); ++ ++ return retval; ++} ++ ++static void appleir_disconnect(struct usb_interface *intf) ++{ ++ struct appleir *appleir = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ input_unregister_device(appleir->input_dev); ++ usb_free_urb(appleir->urb); ++ usb_free_coherent(interface_to_usbdev(intf), URB_SIZE, ++ appleir->data, appleir->dma_buf); ++ kfree(appleir); ++} ++ ++static int appleir_suspend(struct usb_interface *interface, ++ pm_message_t message) ++{ ++ struct appleir *appleir = usb_get_intfdata(interface); ++ ++ mutex_lock(&appleir_mutex); ++ if (appleir->flags & APPLEIR_OPENED) ++ usb_kill_urb(appleir->urb); ++ ++ appleir->flags |= APPLEIR_SUSPENDED; ++ ++ mutex_unlock(&appleir_mutex); ++ ++ return 0; ++} ++ ++static int appleir_resume(struct usb_interface *interface) ++{ ++ struct appleir *appleir; ++ int r = 0; ++ ++ appleir = usb_get_intfdata(interface); ++ ++ mutex_lock(&appleir_mutex); ++ if (appleir->flags & APPLEIR_OPENED) { ++ struct usb_endpoint_descriptor *endpoint; ++ ++ endpoint = &interface->cur_altsetting->endpoint[0].desc; ++ usb_fill_int_urb(appleir->urb, appleir->usbdev, ++ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress), ++ appleir->data, 8, ++ appleir_urb, appleir, endpoint->bInterval); ++ appleir->urb->transfer_dma = appleir->dma_buf; ++ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ /* And reset the USB device */ ++ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) ++ r = -EIO; ++ } ++ ++ appleir->flags &= ~APPLEIR_SUSPENDED; ++ ++ mutex_unlock(&appleir_mutex); ++ ++ return r; ++} ++ ++static struct usb_driver appleir_driver = { ++ .name = "appleir", ++ .probe = appleir_probe, ++ .disconnect = appleir_disconnect, ++ .suspend = appleir_suspend, ++ .resume = appleir_resume, ++ .reset_resume = appleir_resume, ++ .id_table = appleir_ids, ++}; ++ ++static int __init appleir_init(void) ++{ ++ return usb_register(&appleir_driver); ++} ++ ++static void __exit appleir_exit(void) ++{ ++ usb_deregister(&appleir_driver); ++} ++ ++module_init(appleir_init); ++module_exit(appleir_exit); +-- +1.7.2.2 + diff --git a/packages/linux/patches/linux-2.6.38-051_add_ite-cir_driver-0.1.patch b/packages/linux/patches/linux-2.6.38-051_add_ite-cir_driver-0.1.patch new file mode 100644 index 0000000000..338523f3d9 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-051_add_ite-cir_driver-0.1.patch @@ -0,0 +1,2998 @@ +diff -Naur linux-2.6.38-rc5/drivers/media/rc/ite-cir.c linux-2.6.38-rc5.patch/drivers/media/rc/ite-cir.c +--- linux-2.6.38-rc5/drivers/media/rc/ite-cir.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/media/rc/ite-cir.c 2011-02-22 03:19:26.753636264 +0100 +@@ -0,0 +1,1334 @@ ++/* ++ * Driver for ITE Tech Inc. IT8712F/IT8512 CIR ++ * ++ * Copyright (C) 2010 Juan J. Garcia de Soria ++ * ++ * Based on nuvoton-cir and lirc_it87 drivers. ++ * ++ * 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. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ * USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ite-cir.h" ++ ++/* module parameters */ ++ ++/* debug level */ ++static int debug; ++module_param(debug, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Enable debugging output"); ++ ++/* low limit for RX carrier freq, Hz, 0 for no RX demodulation */ ++static int rx_low_carrier_freq; ++module_param(rx_low_carrier_freq, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(rx_low_carrier_freq, "Override low RX carrier frequency, Hz, \ ++0 for no RX demodulation"); ++ ++/* high limit for RX carrier freq, Hz, 0 for no RX demodulation */ ++static int rx_high_carrier_freq; ++module_param(rx_high_carrier_freq, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(rx_high_carrier_freq, "Override high RX carrier frequency, \ ++Hz, 0 for no RX demodulation"); ++ ++/* override tx carrier frequency */ ++static int tx_carrier_freq; ++module_param(tx_carrier_freq, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(tx_carrier_freq, "Override TX carrier frequency, Hz"); ++ ++/* override tx duty cycle */ ++static int tx_duty_cycle; ++module_param(tx_duty_cycle, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(tx_duty_cycle, "Override TX duty cycle, 1-100"); ++ ++/* override default sample period */ ++static long sample_period; ++module_param(sample_period, long, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(sample_period, "Override carrier sample period, \ ++us"); ++ ++/* override detected model id */ ++static int model_number = -1; ++module_param(model_number, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(model_number, "Use this model number, don't \ ++autodetect"); ++ ++ ++/* forward declaration */ ++static void ite_set_carrier_params(struct ite_dev *dev); ++ ++ ++/* IT8712F HW-specific functions */ ++ ++/* retrieve a bitmask of the current causes for a pending interrupt; this may ++ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN ++ * */ ++static int it87_get_irq_causes(struct ite_dev *dev) ++{ ++ u8 iflags; ++ int ret = 0; ++ ++ /* read the interrupt flags */ ++ iflags = inb(dev->cir_addr + IT87_IIR) & IT87_II; ++ ++ switch (iflags) { ++ case IT87_II_RXDS: ++ ret = ITE_IRQ_RX_FIFO; ++ break; ++ case IT87_II_RXFO: ++ ret = ITE_IRQ_RX_FIFO_OVERRUN; ++ break; ++ case IT87_II_TXLDL: ++ ret = ITE_IRQ_TX_FIFO; ++ break; ++ } ++ ++ return ret; ++} ++ ++/* set the carrier parameters; to be called with the spinlock held */ ++static void it87_set_carrier_params(struct ite_dev *dev, bool high_freq, ++ bool use_demodulator, u8 carrier_freq_bits, u8 allowance_bits, ++ u8 pulse_width_bits) ++{ ++ u8 val; ++ ++ /* program the RCR register */ ++ val = inb(dev->cir_addr + IT87_RCR) ++ & ~(IT87_HCFS | IT87_RXEND | IT87_RXDCR); ++ ++ if (high_freq) ++ val |= IT87_HCFS; ++ ++ if (use_demodulator) ++ val |= IT87_RXEND; ++ ++ val |= allowance_bits; ++ ++ outb(val, dev->cir_addr + IT87_RCR); ++ ++ /* program the TCR2 register */ ++ outb((carrier_freq_bits << IT87_CFQ_SHIFT) | pulse_width_bits, ++ dev->cir_addr + IT87_TCR2); ++} ++ ++/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock ++ * held */ ++static int it87_get_rx_bytes(struct ite_dev *dev, u8 *buf, int buf_size) ++{ ++ int fifo, read = 0; ++ ++ /* read how many bytes are still in the FIFO */ ++ fifo = inb(dev->cir_addr + IT87_RSR) & IT87_RXBFC; ++ ++ while (fifo > 0 && buf_size > 0) { ++ *(buf++) = inb(dev->cir_addr + IT87_DR); ++ fifo--; ++ read++; ++ buf_size--; ++ } ++ ++ return read; ++} ++ ++/* return how many bytes we can send through the FIFO; this will be called ++ * with the device spinlock NOT HELD while waiting for the TX FIFO to get ++ * empty; let's expect this won't be a problem */ ++static int it87_get_tx_free_slots(struct ite_dev *dev) ++{ ++ return inb(dev->cir_addr + IT87_TSR) & IT87_TXBFC; ++} ++ ++/* put a byte to the TX fifo; this should be called with the spinlock held */ ++static void it87_put_tx_byte(struct ite_dev *dev, u8 value) ++{ ++ outb(value, dev->cir_addr + IT87_DR); ++} ++ ++/* idle the receiver so that we won't receive samples until another ++ pulse is detected; this must be called with the device spinlock held */ ++static void it87_idle_rx(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable streaming by clearing RXACT writing it as 1 */ ++ outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXACT, ++ dev->cir_addr + IT87_RCR); ++ ++ /* clear the FIFO */ ++ outb(inb(dev->cir_addr + IT87_TCR1) | IT87_FIFOCLR, ++ dev->cir_addr + IT87_TCR1); ++} ++ ++/* disable the receiver; this must be called with the device spinlock held */ ++static void it87_disable_rx(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable the receiver interrupts */ ++ outb(inb(dev->cir_addr + IT87_IER) & ~(IT87_RDAIE | IT87_RFOIE), ++ dev->cir_addr + IT87_IER); ++ ++ /* disable the receiver */ ++ outb(inb(dev->cir_addr + IT87_RCR) & ~IT87_RXEN, ++ dev->cir_addr + IT87_RCR); ++ ++ /* clear the FIFO and RXACT (actually RXACT should have been cleared ++ * in the previous outb() call) */ ++ it87_idle_rx(dev); ++} ++ ++/* enable the receiver; this must be called with the device spinlock held */ ++static void it87_enable_rx(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* enable the receiver by setting RXEN */ ++ outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXEN, ++ dev->cir_addr + IT87_RCR); ++ ++ /* just prepare it to idle for the next reception */ ++ it87_idle_rx(dev); ++ ++ /* enable the receiver interrupts and master enable flag */ ++ outb(inb(dev->cir_addr + IT87_IER) ++ | IT87_RDAIE | IT87_RFOIE | IT87_IEC, ++ dev->cir_addr + IT87_IER); ++} ++ ++/* disable the transmitter interrupt; this must be called with the device ++ * spinlock held */ ++static void it87_disable_tx_interrupt(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable the transmitter interrupts */ ++ outb(inb(dev->cir_addr + IT87_IER) & ~IT87_TLDLIE, ++ dev->cir_addr + IT87_IER); ++} ++ ++/* enable the transmitter interrupt; this must be called with the device ++ * spinlock held */ ++static void it87_enable_tx_interrupt(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* enable the transmitter interrupts and master enable flag */ ++ outb(inb(dev->cir_addr + IT87_IER) ++ | IT87_TLDLIE | IT87_IEC, ++ dev->cir_addr + IT87_IER); ++} ++ ++/* disable the device; this must be called with the device spinlock held */ ++static void it87_disable(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* clear out all interrupt enable flags */ ++ outb(inb(dev->cir_addr + IT87_IER) & ~(IT87_IEC | IT87_RFOIE | ++ IT87_RDAIE | IT87_TLDLIE), dev->cir_addr + IT87_IER); ++ ++ /* disable the receiver */ ++ it87_disable_rx(dev); ++ ++ /* erase the FIFO */ ++ outb(IT87_FIFOCLR | inb(dev->cir_addr + IT87_TCR1), ++ dev->cir_addr + IT87_TCR1); ++} ++ ++/* initialize the hardware */ ++static void it87_init_hardware(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* enable just the baud rate divisor register, ++ disabling all the interrupts at the same time */ ++ outb((inb(dev->cir_addr + IT87_IER) & ~(IT87_IEC | IT87_RFOIE | ++ IT87_RDAIE | IT87_TLDLIE)) | IT87_BR, ++ dev->cir_addr + IT87_IER); ++ ++ /* write out the baud rate divisor */ ++ outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT87_BDLR); ++ outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff, dev->cir_addr + IT87_BDHR); ++ ++ /* disable the baud rate divisor register again */ ++ outb(inb(dev->cir_addr + IT87_IER) & ~IT87_BR, ++ dev->cir_addr + IT87_IER); ++ ++ /* program the RCR register defaults */ ++ outb(ITE_RXDCR_DEFAULT, dev->cir_addr + IT87_RCR); ++ ++ /* program the TCR1 register */ ++ outb(IT87_TXMPM_DEFAULT ++ | IT87_TXENDF ++ | IT87_TXRLE ++ | IT87_FIFOTL_DEFAULT ++ | IT87_FIFOCLR, dev->cir_addr + IT87_TCR1); ++ ++ /* program the carrier parameters */ ++ ite_set_carrier_params(dev); ++} ++ ++/* IT8512F on ITE8708 HW-specific functions */ ++ ++/* retrieve a bitmask of the current causes for a pending interrupt; this may ++ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN ++ * */ ++static int it8708_get_irq_causes(struct ite_dev *dev) ++{ ++ u8 iflags; ++ int ret = 0; ++ ++ /* read the interrupt flags */ ++ iflags = inb(dev->cir_addr + IT8708_C0IIR); ++ ++ if (iflags & IT85_TLDLI) ++ ret |= ITE_IRQ_TX_FIFO; ++ if (iflags & IT85_RDAI) ++ ret |= ITE_IRQ_RX_FIFO; ++ if (iflags & IT85_RFOI) ++ ret |= ITE_IRQ_RX_FIFO_OVERRUN; ++ ++ return ret; ++} ++ ++/* set the carrier parameters; to be called with the spinlock held */ ++static void it8708_set_carrier_params(struct ite_dev *dev, bool high_freq, ++ bool use_demodulator, u8 carrier_freq_bits, u8 allowance_bits, ++ u8 pulse_width_bits) ++{ ++ u8 val; ++ ++ /* program the C0CFR register, with HRAE=1 */ ++ outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE, dev->cir_addr ++ + IT8708_BANKSEL); ++ ++ val = (inb(dev->cir_addr + IT8708_C0CFR) ++ & ~(IT85_HCFS | IT85_CFQ)) | carrier_freq_bits; ++ ++ if (high_freq) ++ val |= IT85_HCFS; ++ ++ outb(val, dev->cir_addr + IT8708_C0CFR); ++ ++ outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE, dev->cir_addr ++ + IT8708_BANKSEL); ++ ++ /* program the C0RCR register */ ++ val = inb(dev->cir_addr + IT8708_C0RCR) ++ & ~(IT85_RXEND | IT85_RXDCR); ++ ++ if (use_demodulator) ++ val |= IT85_RXEND; ++ ++ val |= allowance_bits; ++ ++ outb(val, dev->cir_addr + IT8708_C0RCR); ++ ++ /* program the C0TCR register */ ++ val = inb(dev->cir_addr + IT8708_C0TCR) & ~IT85_TXMPW; ++ val |= pulse_width_bits; ++ outb(val, dev->cir_addr + IT8708_C0TCR); ++} ++ ++/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock ++ * held */ ++static int it8708_get_rx_bytes(struct ite_dev *dev, u8 *buf, int buf_size) ++{ ++ int fifo, read = 0; ++ ++ /* read how many bytes are still in the FIFO */ ++ fifo = inb(dev->cir_addr + IT8708_C0RFSR) & IT85_RXFBC; ++ ++ while (fifo > 0 && buf_size > 0) { ++ *(buf++) = inb(dev->cir_addr + IT8708_C0DR); ++ fifo--; ++ read++; ++ buf_size--; ++ } ++ ++ return read; ++} ++ ++/* return how many bytes we can send through the FIFO; this will be called ++ * with the device spinlock NOT HELD while waiting for the TX FIFO to get ++ * empty; let's expect this won't be a problem */ ++static int it8708_get_tx_free_slots(struct ite_dev *dev) ++{ ++ return inb(dev->cir_addr + IT8708_C0TFSR) & IT85_TXFBC; ++} ++ ++/* put a byte to the TX fifo; this should be called with the spinlock held */ ++static void it8708_put_tx_byte(struct ite_dev *dev, u8 value) ++{ ++ outb(value, dev->cir_addr + IT8708_C0DR); ++} ++ ++/* idle the receiver so that we won't receive samples until another ++ pulse is detected; this must be called with the device spinlock held */ ++static void it8708_idle_rx(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable streaming by clearing RXACT writing it as 1 */ ++ outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXACT, ++ dev->cir_addr + IT8708_C0RCR); ++ ++ /* clear the FIFO */ ++ outb(inb(dev->cir_addr + IT8708_C0MSTCR) | IT85_FIFOCLR, ++ dev->cir_addr + IT8708_C0MSTCR); ++} ++ ++/* disable the receiver; this must be called with the device spinlock held */ ++static void it8708_disable_rx(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable the receiver interrupts */ ++ outb(inb(dev->cir_addr + IT8708_C0IER) & ~(IT85_RDAIE | IT85_RFOIE), ++ dev->cir_addr + IT8708_C0IER); ++ ++ /* disable the receiver */ ++ outb(inb(dev->cir_addr + IT8708_C0RCR) & ~IT85_RXEN, ++ dev->cir_addr + IT8708_C0RCR); ++ ++ /* clear the FIFO and RXACT (actually RXACT should have been cleared ++ * in the previous outb() call) */ ++ it8708_idle_rx(dev); ++} ++ ++/* enable the receiver; this must be called with the device spinlock held */ ++static void it8708_enable_rx(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* enable the receiver by setting RXEN */ ++ outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXEN, ++ dev->cir_addr + IT8708_C0RCR); ++ ++ /* just prepare it to idle for the next reception */ ++ it8708_idle_rx(dev); ++ ++ /* enable the receiver interrupts and master enable flag */ ++ outb(inb(dev->cir_addr + IT8708_C0IER) ++ | IT85_RDAIE | IT85_RFOIE | IT85_IEC, ++ dev->cir_addr + IT8708_C0IER); ++} ++ ++/* disable the transmitter interrupt; this must be called with the device ++ * spinlock held */ ++static void it8708_disable_tx_interrupt(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable the transmitter interrupts */ ++ outb(inb(dev->cir_addr + IT8708_C0IER) & ~IT85_TLDLIE, ++ dev->cir_addr + IT8708_C0IER); ++} ++ ++/* enable the transmitter interrupt; this must be called with the device ++ * spinlock held */ ++static void it8708_enable_tx_interrupt(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* enable the transmitter interrupts and master enable flag */ ++ outb(inb(dev->cir_addr + IT8708_C0IER) ++ | IT85_TLDLIE | IT85_IEC, ++ dev->cir_addr + IT8708_C0IER); ++} ++ ++/* disable the device; this must be called with the device spinlock held */ ++static void it8708_disable(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* clear out all interrupt enable flags */ ++ outb(inb(dev->cir_addr + IT8708_C0IER) & ~(IT85_IEC | IT85_RFOIE | ++ IT85_RDAIE | IT85_TLDLIE), dev->cir_addr + IT8708_C0IER); ++ ++ /* disable the receiver */ ++ it8708_disable_rx(dev); ++ ++ /* erase the FIFO */ ++ outb(IT85_FIFOCLR | inb(dev->cir_addr + IT8708_C0MSTCR), ++ dev->cir_addr + IT8708_C0MSTCR); ++} ++ ++/* initialize the hardware */ ++static void it8708_init_hardware(struct ite_dev *dev) ++{ ++ ite_dbg("%s called", __func__); ++ ++ /* disable all the interrupts */ ++ outb(inb(dev->cir_addr + IT8708_C0IER) & ~(IT85_IEC | IT85_RFOIE | ++ IT85_RDAIE | IT85_TLDLIE), dev->cir_addr + IT8708_C0IER); ++ ++ /* program the baud rate divisor */ ++ outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE, dev->cir_addr ++ + IT8708_BANKSEL); ++ ++ outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT8708_C0BDLR); ++ outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff, ++ dev->cir_addr + IT8708_C0BDHR); ++ ++ outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE, dev->cir_addr ++ + IT8708_BANKSEL); ++ ++ ++ /* program the C0MSTCR register defaults */ ++ outb((inb(dev->cir_addr + IT8708_C0MSTCR) & ~(IT85_ILSEL | IT85_ILE | ++ IT85_FIFOTL | IT85_FIFOCLR | IT85_RESET)) | ++ IT85_FIFOTL_DEFAULT, dev->cir_addr + IT8708_C0MSTCR); ++ ++ /* program the C0RCR register defaults */ ++ outb((inb(dev->cir_addr + IT8708_C0RCR) & ~(IT85_RXEN | IT85_RDWOS | ++ IT85_RXEND | IT85_RXACT | IT85_RXDCR)) | ++ ITE_RXDCR_DEFAULT, dev->cir_addr + IT8708_C0RCR); ++ ++ /* program the C0TCR register defaults */ ++ outb((inb(dev->cir_addr + IT8708_C0TCR) & ~(IT85_TXMPM | IT85_TXMPW)) ++ | IT85_TXRLE | IT85_TXENDF | IT85_TXMPM_DEFAULT | ++ IT85_TXMPW_DEFAULT, dev->cir_addr + IT8708_C0TCR); ++ ++ /* program the carrier parameters */ ++ ite_set_carrier_params(dev); ++} ++ ++ ++ ++ ++/* supported models and their parameters */ ++static const struct ite_dev_params ite_dev_descs[] = { ++ { /* 0: ITE8704, ITE8713 */ ++ .model = "ITE8704/ITE8713 CIR transceiver", ++ .io_region_size = IT87_IOREG_LENGTH, ++ .hw_tx_capable = true, ++ .sample_period = (u32)(1000000000ULL / 115200), ++ .tx_carrier_freq = 38000, ++ .tx_duty_cycle = 33, ++ .rx_low_carrier_freq = 0, ++ .rx_high_carrier_freq = 0, ++ ++ /* operations */ ++ .get_irq_causes = it87_get_irq_causes, ++ .enable_rx = it87_enable_rx, ++ .idle_rx = it87_idle_rx, ++ .disable_rx = it87_idle_rx, ++ .get_rx_bytes = it87_get_rx_bytes, ++ .enable_tx_interrupt = it87_enable_tx_interrupt, ++ .disable_tx_interrupt = it87_disable_tx_interrupt, ++ .get_tx_free_slots = it87_get_tx_free_slots, ++ .put_tx_byte = it87_put_tx_byte, ++ .disable = it87_disable, ++ .init_hardware = it87_init_hardware, ++ .set_carrier_params = it87_set_carrier_params, ++ }, ++ { /* 1: ITE8708 */ ++ .model = "ITE8708 CIR transceiver", ++ .io_region_size = IT8708_IOREG_LENGTH, ++ .hw_tx_capable = true, ++ .sample_period = (u32)(1000000000ULL / 115200), ++ .tx_carrier_freq = 38000, ++ .tx_duty_cycle = 33, ++ .rx_low_carrier_freq = 0, ++ .rx_high_carrier_freq = 0, ++ ++ /* operations */ ++ .get_irq_causes = it8708_get_irq_causes, ++ .enable_rx = it8708_enable_rx, ++ .idle_rx = it8708_idle_rx, ++ .disable_rx = it8708_idle_rx, ++ .get_rx_bytes = it8708_get_rx_bytes, ++ .enable_tx_interrupt = it8708_enable_tx_interrupt, ++ .disable_tx_interrupt = it8708_disable_tx_interrupt, ++ .get_tx_free_slots = it8708_get_tx_free_slots, ++ .put_tx_byte = it8708_put_tx_byte, ++ .disable = it8708_disable, ++ .init_hardware = it8708_init_hardware, ++ .set_carrier_params = it8708_set_carrier_params, ++ }, ++}; ++ ++/* HW-independent code functions */ ++ ++/* check whether carrier frequency is high frequency */ ++static inline bool ite_is_high_carrier_freq(unsigned int freq) ++{ ++ return freq >= ITE_HCF_MIN_CARRIER_FREQ; ++} ++ ++/* get the bits required to program the carrier frequency in CFQ bits, ++ * unshifted */ ++static u8 ite_get_carrier_freq_bits(unsigned int freq) ++{ ++ if (ite_is_high_carrier_freq(freq)) { ++ if (freq < 425000) ++ return ITE_CFQ_400; ++ else if (freq < 465000) ++ return ITE_CFQ_450; ++ else if (freq < 490000) ++ return ITE_CFQ_480; ++ else ++ return ITE_CFQ_500; ++ } else { ++ /* trim to limits */ ++ if (freq < ITE_LCF_MIN_CARRIER_FREQ) ++ freq = ITE_LCF_MIN_CARRIER_FREQ; ++ if (freq > ITE_LCF_MAX_CARRIER_FREQ) ++ freq = ITE_LCF_MAX_CARRIER_FREQ; ++ ++ /* convert to kHz and subtract the base freq */ ++ freq = DIV_ROUND_CLOSEST(freq - ITE_LCF_MIN_CARRIER_FREQ, ++ 1000); ++ ++ return (u8) freq; ++ } ++} ++ ++/* get the bits required to program the pulse with in TXMPW */ ++static u8 ite_get_pulse_width_bits(unsigned int freq, int duty_cycle) ++{ ++ unsigned long period_ns, on_ns; ++ ++ /* sanitize freq into range */ ++ if (freq < ITE_LCF_MIN_CARRIER_FREQ) ++ freq = ITE_LCF_MIN_CARRIER_FREQ; ++ if (freq > ITE_HCF_MAX_CARRIER_FREQ) ++ freq = ITE_HCF_MAX_CARRIER_FREQ; ++ ++ period_ns = 1000000000UL / freq; ++ on_ns = period_ns * duty_cycle / 100; ++ ++ if (ite_is_high_carrier_freq(freq)) { ++ if (on_ns < 750) ++ return ITE_TXMPW_A; ++ else if (on_ns < 850) ++ return ITE_TXMPW_B; ++ else if (on_ns < 950) ++ return ITE_TXMPW_C; ++ else if (on_ns < 1080) ++ return ITE_TXMPW_D; ++ else ++ return ITE_TXMPW_E; ++ } else { ++ if (on_ns < 6500) ++ return ITE_TXMPW_A; ++ else if (on_ns < 7850) ++ return ITE_TXMPW_B; ++ else if (on_ns < 9650) ++ return ITE_TXMPW_C; ++ else if (on_ns < 11950) ++ return ITE_TXMPW_D; ++ else ++ return ITE_TXMPW_E; ++ } ++} ++ ++/* set all the rx/tx carrier parameters; this must be called with the device ++ * spinlock held */ ++static void ite_set_carrier_params(struct ite_dev *dev) ++{ ++ unsigned int freq, low_freq, high_freq; ++ int allowance; ++ bool use_demodulator; ++ bool for_tx = dev->transmitting; ++ ++ ite_dbg("%s called", __func__); ++ ++ if (for_tx) { ++ /* we don't need no stinking calculations */ ++ freq = dev->params.tx_carrier_freq; ++ allowance = ITE_RXDCR_DEFAULT; ++ use_demodulator = false; ++ } else { ++ low_freq = dev->params.rx_low_carrier_freq; ++ high_freq = dev->params.rx_high_carrier_freq; ++ ++ if (low_freq == 0) { ++ /* don't demodulate */ ++ freq = ITE_DEFAULT_CARRIER_FREQ; ++ allowance = ITE_RXDCR_DEFAULT; ++ use_demodulator = false; ++ } else { ++ /* calculate the middle freq */ ++ freq = (low_freq + high_freq) / 2; ++ ++ /* calculate the allowance */ ++ allowance = DIV_ROUND_CLOSEST( ++ 10000 * (high_freq - low_freq), ++ ITE_RXDCR_PER_10000_STEP * ++ (high_freq + low_freq)); ++ ++ if (allowance < 1) ++ allowance = 1; ++ if (allowance > ITE_RXDCR_MAX) ++ allowance = ITE_RXDCR_MAX; ++ } ++ } ++ ++ /* set the carrier parameters in a device-dependent way */ ++ dev->params.set_carrier_params(dev, ite_is_high_carrier_freq(freq), ++ use_demodulator, ite_get_carrier_freq_bits(freq), allowance, ++ ite_get_pulse_width_bits(freq, dev->params.tx_duty_cycle)); ++} ++ ++/* decode raw bytes as received by the hardware, and push them to the ir-core ++ * layer */ ++static void ite_decode_bytes(struct ite_dev *dev, const u8 *data, int ++ length) ++{ ++ u32 sample_period; ++ unsigned long *ldata; ++ unsigned int next_one, next_zero, size; ++ DEFINE_IR_RAW_EVENT(ev); ++ ++ if (length == 0) ++ return; ++ ++ sample_period = dev->params.sample_period; ++ ldata = (unsigned long *) data; ++ size = length << 3; ++ next_one = generic_find_next_le_bit(ldata, size, 0); ++ if (next_one > 0) { ++ ev.pulse = true; ++ ev.duration = ITE_BITS_TO_NS(next_one, sample_period); ++ ir_raw_event_store_with_filter(dev->rdev, &ev); ++ } ++ ++ while (next_one < size) { ++ next_zero = generic_find_next_zero_le_bit(ldata, ++ size, next_one + 1); ++ ev.pulse = false; ++ ev.duration = ITE_BITS_TO_NS(next_zero - next_one, ++ sample_period); ++ ir_raw_event_store_with_filter(dev->rdev, &ev); ++ ++ if (next_zero < size) { ++ next_one = generic_find_next_le_bit(ldata, ++ size, next_zero + 1); ++ ev.pulse = true; ++ ev.duration = ITE_BITS_TO_NS(next_one - next_zero, ++ sample_period); ++ ir_raw_event_store_with_filter(dev->rdev, &ev); ++ } else ++ next_one = size; ++ } ++ ++ ir_raw_event_handle(dev->rdev); ++ ++ ite_dbg_verbose("decoded %d bytes.", length); ++} ++ ++/* interrupt service routine for incoming and outgoing CIR data */ ++static irqreturn_t ite_cir_isr(int irq, void *data) ++{ ++ struct ite_dev *dev = data; ++ unsigned long flags; ++ irqreturn_t ret = IRQ_RETVAL(IRQ_NONE); ++ u8 rx_buf[ITE_RX_FIFO_LEN]; ++ int rx_bytes; ++ int iflags; ++ ++ ite_dbg_verbose("%s firing", __func__); ++ ++ /* grab the spinlock */ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* read the interrupt flags */ ++ iflags = dev->params.get_irq_causes(dev); ++ ++ /* check for the receive interrupt */ ++ if (iflags & (ITE_IRQ_RX_FIFO | ITE_IRQ_RX_FIFO_OVERRUN)) { ++ /* read the FIFO bytes */ ++ rx_bytes = dev->params.get_rx_bytes(dev, rx_buf, ++ ITE_RX_FIFO_LEN); ++ ++ if (rx_bytes > 0) { ++ /* drop the spinlock, since the ir-core layer ++ * may call us back again through ++ * ite_s_idle() */ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* decode the data we've just received */ ++ ite_decode_bytes(dev, rx_buf, rx_bytes); ++ ++ /* reacquire the spinlock */ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* mark the interrupt as serviced */ ++ ret = IRQ_RETVAL(IRQ_HANDLED); ++ } ++ } else if (iflags & ITE_IRQ_TX_FIFO) { ++ /* FIFO space available interrupt */ ++ ite_dbg_verbose("got interrupt for TX FIFO"); ++ ++ /* wake any sleeping transmitter */ ++ wake_up_interruptible(&dev->tx_queue); ++ ++ /* mark the interrupt as serviced */ ++ ret = IRQ_RETVAL(IRQ_HANDLED); ++ } ++ ++ /* drop the spinlock */ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ ite_dbg_verbose("%s done returning %d", __func__, (int) ret); ++ ++ return ret; ++} ++ ++ ++/* set the rx carrier freq range, guess it's in Hz... */ ++static int ite_set_rx_carrier_range(struct rc_dev *rcdev, u32 carrier_low, u32 ++ carrier_high) ++{ ++ unsigned long flags; ++ struct ite_dev *dev = rcdev->priv; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->params.rx_low_carrier_freq = carrier_low; ++ dev->params.rx_high_carrier_freq = carrier_high; ++ ite_set_carrier_params(dev); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++ ++/* set the tx carrier freq, guess it's in Hz... */ ++static int ite_set_tx_carrier(struct rc_dev *rcdev, u32 carrier) ++{ ++ unsigned long flags; ++ struct ite_dev *dev = rcdev->priv; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->params.tx_carrier_freq = carrier; ++ ite_set_carrier_params(dev); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++/* set the tx duty cycle by controlling the pulse width */ ++static int ite_set_tx_duty_cycle(struct rc_dev *rcdev, u32 duty_cycle) ++{ ++ unsigned long flags; ++ struct ite_dev *dev = rcdev->priv; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->params.tx_duty_cycle = duty_cycle; ++ ite_set_carrier_params(dev); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++/* transmit out IR pulses; what you get here is a batch of alternating ++ * pulse/space/pulse/space lengths that we should write out completely through ++ * the FIFO, blocking on a full FIFO */ ++static int ite_tx_ir(struct rc_dev *rcdev, int *txbuf, u32 n) ++{ ++ unsigned long flags; ++ struct ite_dev *dev = rcdev->priv; ++ bool is_pulse = false; ++ int remaining_us, fifo_avail, fifo_remaining, last_idx = 0; ++ int max_rle_us, next_rle_us; ++ int ret = n; ++ u8 last_sent[ITE_TX_FIFO_LEN]; ++ u8 val; ++ ++ ite_dbg("%s called", __func__); ++ ++ /* clear the array just in case */ ++ memset(last_sent, 0, ARRAY_SIZE(last_sent)); ++ ++ /* n comes in bytes; convert to ints */ ++ n /= sizeof(int); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* let everybody know we're now transmitting */ ++ dev->transmitting = true; ++ ++ /* and set the carrier values for transmission */ ++ ite_set_carrier_params(dev); ++ ++ /* calculate how much time we can send in one byte */ ++ max_rle_us = (ITE_BAUDRATE_DIVISOR * dev->params.sample_period * ++ ITE_TX_MAX_RLE) / 1000; ++ ++ /* disable the receiver */ ++ dev->params.disable_rx(dev); ++ ++ /* this is where we'll begin filling in the FIFO, until it's full. ++ * then we'll just activate the interrupt, wait for it to wake us up ++ * again, disable it, continue filling the FIFO... until everything ++ * has been pushed out */ ++ fifo_avail = dev->params.get_tx_free_slots(dev); ++ ++ while (n > 0 && dev->in_use) { ++ /* transmit the next sample */ ++ is_pulse = !is_pulse; ++ remaining_us = *(txbuf++); ++ n--; ++ ++ /* repeat while the pulse is non-zero length */ ++ while (remaining_us > 0 && dev->in_use) { ++ if (remaining_us > max_rle_us) ++ next_rle_us = max_rle_us; ++ else ++ next_rle_us = remaining_us; ++ remaining_us -= next_rle_us; ++ ++ /* check what's the length we have to pump out */ ++ val = (ITE_TX_MAX_RLE * next_rle_us) / max_rle_us; ++ ++ /* put it into the sent buffer */ ++ last_sent[last_idx++] = val; ++ last_idx &= (ITE_TX_FIFO_LEN); ++ ++ /* encode it for 7 bits */ ++ val = (val - 1) & ITE_TX_RLE_MASK; ++ ++ /* take into account pulse/space prefix */ ++ if (is_pulse) ++ val |= ITE_TX_PULSE; ++ else ++ val |= ITE_TX_SPACE; ++ ++ /* if we get to 0 available, read again, just in case ++ * some other slot got freed */ ++ if (fifo_avail <= 0) { ++ fifo_avail = ++ dev->params.get_tx_free_slots(dev); ++ } ++ ++ /* if it's still full */ ++ if (fifo_avail <= 0) { ++ /* enable the tx interrupt */ ++ dev->params.enable_tx_interrupt(dev); ++ ++ /* drop the spinlock */ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* wait for the FIFO to empty enough */ ++ wait_event_interruptible(dev->tx_queue, ++ (fifo_avail = ++ dev->params.get_tx_free_slots(dev)) ++ >= 8); ++ ++ /* get the spinlock again */ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* disable the tx interrupt again. */ ++ dev->params.disable_tx_interrupt(dev); ++ } ++ ++ /* now send the byte through the FIFO */ ++ dev->params.put_tx_byte(dev, val); ++ fifo_avail--; ++ } ++ } ++ ++ /* wait and don't return until the whole FIFO has been sent out; ++ * otherwise we could configure the RX carrier params instead of the ++ * TX ones while the transmission is still being performed! */ ++ fifo_remaining = ITE_TX_FIFO_LEN - ++ dev->params.get_tx_free_slots(dev); ++ remaining_us = 0; ++ while (fifo_remaining > 0) { ++ fifo_remaining--; ++ last_idx--; ++ last_idx &= (ITE_TX_FIFO_LEN - 1); ++ remaining_us += last_sent[last_idx]; ++ } ++ remaining_us = (remaining_us * max_rle_us) / (ITE_TX_MAX_RLE); ++ ++ /* drop the spinlock while we sleep */ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* sleep remaining_us microseconds */ ++ mdelay(DIV_ROUND_UP(remaining_us, 1000)); ++ ++ /* reacquire the spinlock */ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* now we're not transmitting anymore */ ++ dev->transmitting = false; ++ ++ /* and set the carrier values for reception */ ++ ite_set_carrier_params(dev); ++ ++ /* reenable the receiver */ ++ if (dev->in_use) ++ dev->params.enable_rx(dev); ++ ++ /* notify transmission end */ ++ wake_up_interruptible(&dev->tx_ended); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return ret; ++} ++ ++/* idle the receiver if needed */ ++static void ite_s_idle(struct rc_dev *rcdev, bool enable) ++{ ++ unsigned long flags; ++ struct ite_dev *dev = rcdev->priv; ++ ++ ite_dbg("%s called", __func__); ++ ++ if (enable) { ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->params.idle_rx(dev); ++ spin_unlock_irqrestore(&dev->lock, flags); ++ } ++} ++ ++/* activate the device for use */ ++static int ite_open(struct rc_dev *rcdev) ++{ ++ struct ite_dev *dev = rcdev->priv; ++ unsigned long flags; ++ ++ ite_dbg("%s called", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->in_use = true; ++ ++ /* enable the receiver */ ++ dev->params.enable_rx(dev); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++/* deactivate the device for use */ ++static void ite_close(struct rc_dev *rcdev) ++{ ++ struct ite_dev *dev = rcdev->priv; ++ unsigned long flags; ++ ++ ite_dbg("%s called", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ dev->in_use = false; ++ ++ /* wait for any transmission to end */ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ wait_event_interruptible(dev->tx_ended, !dev->transmitting); ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ dev->params.disable(dev); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++/* allocate memory, probe hardware, and initialize everything */ ++static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id ++ *dev_id) ++{ ++ const struct ite_dev_params *dev_desc = NULL; ++ struct ite_dev *itdev = NULL; ++ struct rc_dev *rdev = NULL; ++ int ret = -ENOMEM; ++ int model_no; ++ ++ ite_dbg("%s called", __func__); ++ ++ itdev = kzalloc(sizeof(struct ite_dev), GFP_KERNEL); ++ if (!itdev) ++ return ret; ++ ++ /* input device for IR remote (and tx) */ ++ rdev = rc_allocate_device(); ++ if (!rdev) ++ goto failure; ++ ++ ret = -ENODEV; ++ ++ ++ /* get the model number */ ++ model_no = (int) dev_id->driver_data; ++ ite_pr(KERN_NOTICE, "Auto-detected model: %s\n", ++ ite_dev_descs[model_no].model); ++ ++ if (model_number >= 0 && model_number ++ < ARRAY_SIZE(ite_dev_descs)) { ++ model_no = model_number; ++ ite_pr(KERN_NOTICE, "The model has been fixed by a module \ ++parameter."); ++ } ++ ++ ite_pr(KERN_NOTICE, "Using model: %s\n", ++ ite_dev_descs[model_no].model); ++ ++ /* get the description for the device */ ++ dev_desc = &ite_dev_descs[model_no]; ++ ++ ++ /* validate pnp resources */ ++ if (!pnp_port_valid(pdev, 0) || ++ pnp_port_len(pdev, 0) != dev_desc->io_region_size) { ++ dev_err(&pdev->dev, "IR PNP Port not valid!\n"); ++ goto failure; ++ } ++ ++ if (!pnp_irq_valid(pdev, 0)) { ++ dev_err(&pdev->dev, "PNP IRQ not valid!\n"); ++ goto failure; ++ } ++ ++ /* store resource values */ ++ itdev->cir_addr = pnp_port_start(pdev, 0); ++ itdev->cir_irq = pnp_irq(pdev, 0); ++ ++ /* initialize spinlocks */ ++ spin_lock_init(&itdev->lock); ++ ++ /* initialize raw event */ ++ init_ir_raw_event(&itdev->rawir); ++ ++ ret = -EBUSY; ++ /* now claim resources */ ++ if (!request_region(itdev->cir_addr, ++ dev_desc->io_region_size, ITE_DRIVER_NAME)) ++ goto failure; ++ ++ if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ++ ITE_DRIVER_NAME, (void *)itdev)) ++ goto failure; ++ ++ /* set driver data into the pnp device */ ++ pnp_set_drvdata(pdev, itdev); ++ itdev->pdev = pdev; ++ ++ /* initialize waitqueues for transmission */ ++ init_waitqueue_head(&itdev->tx_queue); ++ init_waitqueue_head(&itdev->tx_ended); ++ ++ /* copy model-specific parameters */ ++ itdev->params = *dev_desc; ++ ++ /* apply any overrides */ ++ if (sample_period > 0) ++ itdev->params.sample_period = sample_period; ++ ++ if (tx_carrier_freq > 0) ++ itdev->params.tx_carrier_freq = tx_carrier_freq; ++ ++ if (tx_duty_cycle > 0 && tx_duty_cycle <= 100) ++ itdev->params.tx_duty_cycle = tx_duty_cycle; ++ ++ if (rx_low_carrier_freq > 0) ++ itdev->params.rx_low_carrier_freq = rx_low_carrier_freq; ++ ++ if (rx_high_carrier_freq > 0) ++ itdev->params.rx_high_carrier_freq = rx_high_carrier_freq; ++ ++ /* print out parameters */ ++ ite_pr(KERN_NOTICE, "TX-capable: %d\n", (int) ++ itdev->params.hw_tx_capable); ++ ite_pr(KERN_NOTICE, "Sample period (ns): %ld\n", (long) ++ itdev->params.sample_period); ++ ite_pr(KERN_NOTICE, "TX carrier frequency (Hz): %d\n", (int) ++ itdev->params.tx_carrier_freq); ++ ite_pr(KERN_NOTICE, "TX duty cycle (%%): %d\n", (int) ++ itdev->params.tx_duty_cycle); ++ ite_pr(KERN_NOTICE, "RX low carrier frequency (Hz): %d\n", (int) ++ itdev->params.rx_low_carrier_freq); ++ ite_pr(KERN_NOTICE, "RX high carrier frequency (Hz): %d\n", (int) ++ itdev->params.rx_high_carrier_freq); ++ ++ /* set up hardware initial state */ ++ itdev->params.init_hardware(itdev); ++ ++ /* set up ir-core props */ ++ rdev->priv = itdev; ++ rdev->driver_type = RC_DRIVER_IR_RAW; ++ rdev->allowed_protos = RC_TYPE_ALL; ++ rdev->open = ite_open; ++ rdev->close = ite_close; ++ rdev->s_idle = ite_s_idle; ++ rdev->s_rx_carrier_range = ite_set_rx_carrier_range; ++ rdev->min_timeout = ITE_MIN_IDLE_TIMEOUT; ++ rdev->max_timeout = ITE_MAX_IDLE_TIMEOUT; ++ rdev->timeout = ITE_IDLE_TIMEOUT; ++ rdev->rx_resolution = rdev->tx_resolution = ITE_BAUDRATE_DIVISOR * ++ itdev->params.sample_period; ++ ++ /* set up transmitter related values if needed */ ++ if (itdev->params.hw_tx_capable) { ++ rdev->tx_ir = ite_tx_ir; ++ rdev->s_tx_carrier = ite_set_tx_carrier; ++ rdev->s_tx_duty_cycle = ite_set_tx_duty_cycle; ++ } ++ ++ rdev->input_name = dev_desc->model; ++ rdev->input_id.bustype = BUS_HOST; ++ rdev->input_id.vendor = PCI_VENDOR_ID_ITE; ++ rdev->input_id.product = 0; ++ rdev->input_id.version = 0; ++ rdev->driver_name = ITE_DRIVER_NAME; ++ rdev->map_name = RC_MAP_RC6_MCE; ++ ++ ret = rc_register_device(rdev); ++ if (ret) ++ goto failure; ++ ++ itdev->rdev = rdev; ++ ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); ++ ++ return 0; ++ ++failure: ++ if (itdev->cir_irq) ++ free_irq(itdev->cir_irq, itdev); ++ if (itdev->cir_addr) ++ release_region(itdev->cir_addr, itdev->params.io_region_size); ++ ++ rc_free_device(rdev); ++ kfree(itdev); ++ ++ return ret; ++} ++ ++static void __devexit ite_remove(struct pnp_dev *pdev) ++{ ++ struct ite_dev *dev = pnp_get_drvdata(pdev); ++ unsigned long flags; ++ ++ ite_dbg("%s called", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* disable hardware */ ++ dev->params.disable(dev); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ /* free resources */ ++ free_irq(dev->cir_irq, dev); ++ release_region(dev->cir_addr, dev->params.io_region_size); ++ ++ rc_unregister_device(dev->rdev); ++ ++ kfree(dev); ++} ++ ++static int ite_suspend(struct pnp_dev *pdev, pm_message_t state) ++{ ++ struct ite_dev *dev = pnp_get_drvdata(pdev); ++ unsigned long flags; ++ ++ ite_dbg("%s called", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* disable all interrupts */ ++ dev->params.disable(dev); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++static int ite_resume(struct pnp_dev *pdev) ++{ ++ int ret = 0; ++ struct ite_dev *dev = pnp_get_drvdata(pdev); ++ unsigned long flags; ++ ++ ite_dbg("%s called", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ if (dev->transmitting) { ++ /* wake up the transmitter */ ++ wake_up_interruptible(&dev->tx_queue); ++ } else { ++ /* enable the receiver */ ++ dev->params.enable_rx(dev); ++ } ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return ret; ++} ++ ++static void ite_shutdown(struct pnp_dev *pdev) ++{ ++ struct ite_dev *dev = pnp_get_drvdata(pdev); ++ unsigned long flags; ++ ++ ite_dbg("%s called", __func__); ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ /* disable all interrupts */ ++ dev->params.disable(dev); ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++} ++ ++static const struct pnp_device_id ite_ids[] = { ++ { "ITE8704", 0 }, /* Default model */ ++ { "ITE8713", 0 }, /* CIR found in EEEBox 1501U */ ++ { "ITE8708", 1 }, /* Bridged IT8512 */ ++ { "", 0 }, ++}; ++ ++static struct pnp_driver ite_driver = { ++ .name = ITE_DRIVER_NAME, ++ .id_table = ite_ids, ++ .probe = ite_probe, ++ .remove = __devexit_p(ite_remove), ++ .suspend = ite_suspend, ++ .resume = ite_resume, ++ .shutdown = ite_shutdown, ++}; ++ ++int ite_init(void) ++{ ++ return pnp_register_driver(&ite_driver); ++} ++ ++void ite_exit(void) ++{ ++ pnp_unregister_driver(&ite_driver); ++} ++ ++MODULE_DEVICE_TABLE(pnp, ite_ids); ++MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver"); ++ ++MODULE_AUTHOR("Juan J. Garcia de Soria "); ++MODULE_LICENSE("GPL"); ++ ++module_init(ite_init); ++module_exit(ite_exit); +diff -Naur linux-2.6.38-rc5/drivers/media/rc/ite-cir.h linux-2.6.38-rc5.patch/drivers/media/rc/ite-cir.h +--- linux-2.6.38-rc5/drivers/media/rc/ite-cir.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/media/rc/ite-cir.h 2011-02-22 03:19:26.755636229 +0100 +@@ -0,0 +1,446 @@ ++/* ++ * Driver for ITE Tech Inc. IT8712F/IT8512F CIR ++ * ++ * Copyright (C) 2010 Juan J. Garcia de Soria ++ * ++ * Based on nuvoton-cir and lirc_it87 drivers. ++ * ++ * 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. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ++ * USA ++ */ ++ ++/* platform driver name to register */ ++#define ITE_DRIVER_NAME "ite-cir" ++ ++/* logging macros */ ++#define ite_pr(level, text, ...) \ ++ printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__) ++ ++#define ite_dbg(text, ...) \ ++ if (debug) \ ++ printk(KERN_DEBUG \ ++ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) ++ ++#define ite_dbg_verbose(text, ...) \ ++ if (debug > 1) \ ++ printk(KERN_DEBUG \ ++ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__) ++ ++/* FIFO sizes */ ++#define ITE_TX_FIFO_LEN 32 ++#define ITE_RX_FIFO_LEN 32 ++ ++/* interrupt types */ ++#define ITE_IRQ_TX_FIFO 1 ++#define ITE_IRQ_RX_FIFO 2 ++#define ITE_IRQ_RX_FIFO_OVERRUN 4 ++ ++/* forward declaration */ ++struct ite_dev; ++ ++/* struct for storing the parameters of different recognized devices */ ++struct ite_dev_params { ++ /* model of the device */ ++ const char *model; ++ ++ /* size of the I/O region */ ++ int io_region_size; ++ ++ /* true if the hardware supports transmission */ ++ bool hw_tx_capable; ++ ++ /* base sampling period, in ns */ ++ u32 sample_period; ++ ++ /* rx low carrier frequency, in Hz, 0 means no demodulation */ ++ unsigned int rx_low_carrier_freq; ++ ++ /* tx high carrier frequency, in Hz, 0 means no demodulation */ ++ unsigned int rx_high_carrier_freq; ++ ++ /* tx carrier frequency, in Hz */ ++ unsigned int tx_carrier_freq; ++ ++ /* duty cycle, 0-100 */ ++ int tx_duty_cycle; ++ ++ /* hw-specific operation function pointers; most of these must be ++ * called while holding the spin lock, except for the TX FIFO length ++ * one */ ++ ++ /* get pending interrupt causes */ ++ int (*get_irq_causes)(struct ite_dev *dev); ++ ++ /* enable rx */ ++ void (*enable_rx)(struct ite_dev *dev); ++ ++ /* make rx enter the idle state; keep listening for a pulse, but stop ++ * streaming space bytes */ ++ void (*idle_rx)(struct ite_dev *dev); ++ ++ /* disable rx completely */ ++ void (*disable_rx)(struct ite_dev *dev); ++ ++ /* read bytes from RX FIFO; return read count */ ++ int (*get_rx_bytes)(struct ite_dev *dev, u8 *buf, int buf_size); ++ ++ /* enable tx FIFO space available interrupt */ ++ void (*enable_tx_interrupt)(struct ite_dev *dev); ++ ++ /* disable tx FIFO space available interrupt */ ++ void (*disable_tx_interrupt)(struct ite_dev *dev); ++ ++ /* get number of available TX FIFO slots */ ++ int (*get_tx_free_slots)(struct ite_dev *dev); ++ ++ /* put a byte to the TX FIFO */ ++ void (*put_tx_byte)(struct ite_dev *dev, u8 value); ++ ++ /* disable hardware completely */ ++ void (*disable)(struct ite_dev *dev); ++ ++ /* initialize the hardware */ ++ void (*init_hardware)(struct ite_dev *dev); ++ ++ /* set the carrier parameters */ ++ void (*set_carrier_params)(struct ite_dev *dev, bool high_freq, ++ bool use_demodulator, u8 carrier_freq_bits, ++ u8 allowance_bits, u8 pulse_width_bits); ++}; ++ ++/* ITE CIR device structure */ ++struct ite_dev { ++ struct pnp_dev *pdev; ++ struct rc_dev *rdev; ++ struct ir_raw_event rawir; ++ ++ /* sync data */ ++ spinlock_t lock; ++ bool in_use, transmitting; ++ ++ /* transmit support */ ++ int tx_fifo_allowance; ++ wait_queue_head_t tx_queue, tx_ended; ++ ++ /* hardware I/O settings */ ++ unsigned long cir_addr; ++ int cir_irq; ++ ++ /* overridable copy of model parameters */ ++ struct ite_dev_params params; ++}; ++ ++ ++/* common values for all kinds of hardware */ ++ ++/* baud rate divisor default */ ++#define ITE_BAUDRATE_DIVISOR 1 ++ ++/* low-speed carrier frequency limits (Hz) */ ++#define ITE_LCF_MIN_CARRIER_FREQ 27000 ++#define ITE_LCF_MAX_CARRIER_FREQ 58000 ++ ++/* high-speed carrier frequency limits (Hz) */ ++#define ITE_HCF_MIN_CARRIER_FREQ 400000 ++#define ITE_HCF_MAX_CARRIER_FREQ 500000 ++ ++/* default carrier freq for when demodulator is off (Hz) */ ++#define ITE_DEFAULT_CARRIER_FREQ 38000 ++ ++/* default idling timeout in ns (0.2 seconds) */ ++#define ITE_IDLE_TIMEOUT 200000000UL ++ ++/* limit timeout values */ ++#define ITE_MIN_IDLE_TIMEOUT 100000000UL ++#define ITE_MAX_IDLE_TIMEOUT 1000000000UL ++ ++/* convert bits to us */ ++#define ITE_BITS_TO_NS(bits, sample_period) \ ++((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period)) ++ ++/* ++ * n in IT87_RDCR produces a tolerance of +/- n * 6.25% around the center ++ * carrier frequency... ++ * ++ * From two limit frequencies, L (low) and H (high), we can get both the ++ * center frequency F = (L + H) / 2 and the variation from the center ++ * frequency A = (H - L) / (H + L). We can use this in order to honor the ++ * s_rx_carrier_range() call in ir-core. We'll suppose that any request ++ * setting L=0 means we must shut down the demodulator. ++ */ ++#define ITE_RXDCR_PER_10000_STEP 625 ++ ++/* high speed carrier freq values */ ++#define ITE_CFQ_400 0x03 ++#define ITE_CFQ_450 0x08 ++#define ITE_CFQ_480 0x0b ++#define ITE_CFQ_500 0x0d ++ ++/* values for pulse widths */ ++#define ITE_TXMPW_A 0x02 ++#define ITE_TXMPW_B 0x03 ++#define ITE_TXMPW_C 0x04 ++#define ITE_TXMPW_D 0x05 ++#define ITE_TXMPW_E 0x06 ++ ++/* values for demodulator carrier range allowance */ ++#define ITE_RXDCR_DEFAULT 0x01 /* default carrier range */ ++#define ITE_RXDCR_MAX 0x07 /* default carrier range */ ++ ++/* DR TX bits */ ++#define ITE_TX_PULSE 0x00 ++#define ITE_TX_SPACE 0x80 ++#define ITE_TX_MAX_RLE 0x80 ++#define ITE_TX_RLE_MASK 0x7F ++ ++ ++/* ++ * IT8712F ++ * ++ * hardware data obtained from: ++ * ++ * IT8712F ++ * Environment Control – Low Pin Count Input / Output ++ * (EC - LPC I/O) ++ * Preliminary Specification V0. 81 ++ */ ++ ++/* register offsets */ ++#define IT87_DR 0x00 /* data register */ ++#define IT87_IER 0x01 /* interrupt enable register */ ++#define IT87_RCR 0x02 /* receiver control register */ ++#define IT87_TCR1 0x03 /* transmitter control register 1 */ ++#define IT87_TCR2 0x04 /* transmitter control register 2 */ ++#define IT87_TSR 0x05 /* transmitter status register */ ++#define IT87_RSR 0x06 /* receiver status register */ ++#define IT87_BDLR 0x05 /* baud rate divisor low byte register */ ++#define IT87_BDHR 0x06 /* baud rate divisor high byte register */ ++#define IT87_IIR 0x07 /* interrupt identification register */ ++ ++#define IT87_IOREG_LENGTH 0x08 /* length of register file */ ++ ++/* IER bits */ ++#define IT87_TLDLIE 0x01 /* transmitter low data interrupt enable */ ++#define IT87_RDAIE 0x02 /* receiver data available interrupt enable */ ++#define IT87_RFOIE 0x04 /* receiver FIFO overrun interrupt enable */ ++#define IT87_IEC 0x08 /* interrupt enable control */ ++#define IT87_BR 0x10 /* baud rate register enable */ ++#define IT87_RESET 0x20 /* reset */ ++ ++/* RCR bits */ ++#define IT87_RXDCR 0x07 /* receiver demodulation carrier range mask */ ++#define IT87_RXACT 0x08 /* receiver active */ ++#define IT87_RXEND 0x10 /* receiver demodulation enable */ ++#define IT87_RXEN 0x20 /* receiver enable */ ++#define IT87_HCFS 0x40 /* high-speed carrier frequency select */ ++#define IT87_RDWOS 0x80 /* receiver data without sync */ ++ ++/* TCR1 bits */ ++#define IT87_TXMPM 0x03 /* transmitter modulation pulse mode mask */ ++#define IT87_TXMPM_DEFAULT 0x00 /* modulation pulse mode default */ ++#define IT87_TXENDF 0x04 /* transmitter deferral */ ++#define IT87_TXRLE 0x08 /* transmitter run length enable */ ++#define IT87_FIFOTL 0x30 /* FIFO level threshold mask */ ++#define IT87_FIFOTL_DEFAULT 0x20 /* FIFO level threshold default ++ 0x00 -> 1, 0x10 -> 7, 0x20 -> 17, ++ 0x30 -> 25 */ ++#define IT87_ILE 0x40 /* internal loopback enable */ ++#define IT87_FIFOCLR 0x80 /* FIFO clear bit */ ++ ++/* TCR2 bits */ ++#define IT87_TXMPW 0x07 /* transmitter modulation pulse width mask */ ++#define IT87_TXMPW_DEFAULT 0x04 /* default modulation pulse width */ ++#define IT87_CFQ 0xf8 /* carrier frequency mask */ ++#define IT87_CFQ_SHIFT 3 /* carrier frequency bit shift */ ++ ++/* TSR bits */ ++#define IT87_TXBFC 0x3f /* transmitter FIFO byte count mask */ ++ ++/* RSR bits */ ++#define IT87_RXBFC 0x3f /* receiver FIFO byte count mask */ ++#define IT87_RXFTO 0x80 /* receiver FIFO time-out */ ++ ++/* IIR bits */ ++#define IT87_IP 0x01 /* interrupt pending */ ++#define IT87_II 0x06 /* interrupt identification mask */ ++#define IT87_II_NOINT 0x00 /* no interrupt */ ++#define IT87_II_TXLDL 0x02 /* transmitter low data level */ ++#define IT87_II_RXDS 0x04 /* receiver data stored */ ++#define IT87_II_RXFO 0x06 /* receiver FIFO overrun */ ++ ++ ++/* ++ * IT8512E/F ++ * ++ * Hardware data obtained from: ++ * ++ * IT8512E/F ++ * Embedded Controller ++ * Preliminary Specification V0.4.1 ++ * ++ * Note that the CIR registers are not directly available to the host, because ++ * they only are accessible to the integrated microcontroller. Thus, in order ++ * use it, some kind of bridging is required. As the bridging may depend on ++ * the controller firmware in use, we are going to use the PNP ID in order to ++ * determine the strategy and ports available. See after these generic ++ * IT8512E/F register definitions for register definitions for those ++ * strategies. ++ */ ++ ++/* register offsets */ ++#define IT85_C0DR 0x00 /* data register */ ++#define IT85_C0MSTCR 0x01 /* master control register */ ++#define IT85_C0IER 0x02 /* interrupt enable register */ ++#define IT85_C0IIR 0x03 /* interrupt identification register */ ++#define IT85_C0CFR 0x04 /* carrier frequency register */ ++#define IT85_C0RCR 0x05 /* receiver control register */ ++#define IT85_C0TCR 0x06 /* transmitter control register */ ++#define IT85_C0SCK 0x07 /* slow clock control register */ ++#define IT85_C0BDLR 0x08 /* baud rate divisor low byte register */ ++#define IT85_C0BDHR 0x09 /* baud rate divisor high byte register */ ++#define IT85_C0TFSR 0x0a /* transmitter FIFO status register */ ++#define IT85_C0RFSR 0x0b /* receiver FIFO status register */ ++#define IT85_C0WCL 0x0d /* wakeup code length register */ ++#define IT85_C0WCR 0x0e /* wakeup code read/write register */ ++#define IT85_C0WPS 0x0f /* wakeup power control/status register */ ++ ++#define IT85_IOREG_LENGTH 0x10 /* length of register file */ ++ ++/* C0MSTCR bits */ ++#define IT85_RESET 0x01 /* reset */ ++#define IT85_FIFOCLR 0x02 /* FIFO clear bit */ ++#define IT85_FIFOTL 0x0c /* FIFO level threshold mask */ ++#define IT85_FIFOTL_DEFAULT 0x08 /* FIFO level threshold default ++ 0x00 -> 1, 0x04 -> 7, 0x08 -> 17, ++ 0x0c -> 25 */ ++#define IT85_ILE 0x10 /* internal loopback enable */ ++#define IT85_ILSEL 0x20 /* internal loopback select */ ++ ++/* C0IER bits */ ++#define IT85_TLDLIE 0x01 /* TX low data level interrupt enable */ ++#define IT85_RDAIE 0x02 /* RX data available interrupt enable */ ++#define IT85_RFOIE 0x04 /* RX FIFO overrun interrupt enable */ ++#define IT85_IEC 0x80 /* interrupt enable function control */ ++ ++/* C0IIR bits */ ++#define IT85_TLDLI 0x01 /* transmitter low data level interrupt */ ++#define IT85_RDAI 0x02 /* receiver data available interrupt */ ++#define IT85_RFOI 0x04 /* receiver FIFO overrun interrupt */ ++#define IT85_NIP 0x80 /* no interrupt pending */ ++ ++/* C0CFR bits */ ++#define IT85_CFQ 0x1f /* carrier frequency mask */ ++#define IT85_HCFS 0x20 /* high speed carrier frequency select */ ++ ++/* C0RCR bits */ ++#define IT85_RXDCR 0x07 /* receiver demodulation carrier range mask */ ++#define IT85_RXACT 0x08 /* receiver active */ ++#define IT85_RXEND 0x10 /* receiver demodulation enable */ ++#define IT85_RDWOS 0x20 /* receiver data without sync */ ++#define IT85_RXEN 0x80 /* receiver enable */ ++ ++/* C0TCR bits */ ++#define IT85_TXMPW 0x07 /* transmitter modulation pulse width mask */ ++#define IT85_TXMPW_DEFAULT 0x04 /* default modulation pulse width */ ++#define IT85_TXMPM 0x18 /* transmitter modulation pulse mode mask */ ++#define IT85_TXMPM_DEFAULT 0x00 /* modulation pulse mode default */ ++#define IT85_TXENDF 0x20 /* transmitter deferral */ ++#define IT85_TXRLE 0x40 /* transmitter run length enable */ ++ ++/* C0SCK bits */ ++#define IT85_SCKS 0x01 /* slow clock select */ ++#define IT85_TXDCKG 0x02 /* TXD clock gating */ ++#define IT85_DLL1P8E 0x04 /* DLL 1.8432M enable */ ++#define IT85_DLLTE 0x08 /* DLL test enable */ ++#define IT85_BRCM 0x70 /* baud rate count mode */ ++#define IT85_DLLOCK 0x80 /* DLL lock */ ++ ++/* C0TFSR bits */ ++#define IT85_TXFBC 0x3f /* transmitter FIFO count mask */ ++ ++/* C0RFSR bits */ ++#define IT85_RXFBC 0x3f /* receiver FIFO count mask */ ++#define IT85_RXFTO 0x80 /* receiver FIFO time-out */ ++ ++/* C0WCL bits */ ++#define IT85_WCL 0x3f /* wakeup code length mask */ ++ ++/* C0WPS bits */ ++#define IT85_CIRPOSIE 0x01 /* power on/off status interrupt enable */ ++#define IT85_CIRPOIS 0x02 /* power on/off interrupt status */ ++#define IT85_CIRPOII 0x04 /* power on/off interrupt identification */ ++#define IT85_RCRST 0x10 /* wakeup code reading counter reset bit */ ++#define IT85_WCRST 0x20 /* wakeup code writing counter reset bit */ ++ ++ ++/* ++ * ITE8708 ++ * ++ * Hardware data obtained from hacked driver for IT8512 in this forum post: ++ * ++ * http://ubuntuforums.org/showthread.php?t=1028640 ++ * ++ * Although there's no official documentation for that driver, analysis would ++ * suggest that it maps the 16 registers of IT8512 onto two 8-register banks, ++ * selectable by a single bank-select bit that's mapped onto both banks. The ++ * IT8512 registers are mapped in a different order, so that the first bank ++ * maps the ones that are used more often, and two registers that share a ++ * reserved high-order bit are placed at the same offset in both banks in ++ * order to reuse the reserved bit as the bank select bit. ++ */ ++ ++/* register offsets */ ++ ++/* mapped onto both banks */ ++#define IT8708_BANKSEL 0x07 /* bank select register */ ++#define IT8708_HRAE 0x80 /* high registers access enable */ ++ ++/* mapped onto the low bank */ ++#define IT8708_C0DR 0x00 /* data register */ ++#define IT8708_C0MSTCR 0x01 /* master control register */ ++#define IT8708_C0IER 0x02 /* interrupt enable register */ ++#define IT8708_C0IIR 0x03 /* interrupt identification register */ ++#define IT8708_C0RFSR 0x04 /* receiver FIFO status register */ ++#define IT8708_C0RCR 0x05 /* receiver control register */ ++#define IT8708_C0TFSR 0x06 /* transmitter FIFO status register */ ++#define IT8708_C0TCR 0x07 /* transmitter control register */ ++ ++/* mapped onto the high bank */ ++#define IT8708_C0BDLR 0x01 /* baud rate divisor low byte register */ ++#define IT8708_C0BDHR 0x02 /* baud rate divisor high byte register */ ++#define IT8708_C0CFR 0x04 /* carrier frequency register */ ++ ++/* registers whose bank mapping we don't know, since they weren't being used ++ * in the hacked driver... most probably they belong to the high bank too, ++ * since they fit in the holes the other registers leave */ ++#define IT8708_C0SCK 0x03 /* slow clock control register */ ++#define IT8708_C0WCL 0x05 /* wakeup code length register */ ++#define IT8708_C0WCR 0x06 /* wakeup code read/write register */ ++#define IT8708_C0WPS 0x07 /* wakeup power control/status register */ ++ ++#define IT8708_IOREG_LENGTH 0x08 /* length of register file */ ++ ++/* two more registers that are defined in the hacked driver, but can't be ++ * found in the data sheets; no idea what they are or how they are accessed, ++ * since the hacked driver doesn't seem to use them */ ++#define IT8708_CSCRR 0x00 ++#define IT8708_CGPINTR 0x01 ++ ++/* CSCRR bits */ ++#define IT8708_CSCRR_SCRB 0x3f ++#define IT8708_CSCRR_PM 0x80 ++ ++/* CGPINTR bits */ ++#define IT8708_CGPINT 0x01 +diff -Naur linux-2.6.38-rc5/drivers/media/rc/Kconfig linux-2.6.38-rc5.patch/drivers/media/rc/Kconfig +--- linux-2.6.38-rc5/drivers/media/rc/Kconfig 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/media/rc/Kconfig 2011-02-22 03:24:59.239412519 +0100 +@@ -148,6 +148,19 @@ + To compile this driver as a module, choose M here: the + module will be called nuvoton-cir. + ++config IR_ITE_CIR ++ tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver" ++ depends on PNP ++ depends on RC_CORE ++ ---help--- ++ Say Y here to enable support for integrated infrared receivers ++ /transceivers made by ITE Tech Inc. These are found in ++ several ASUS devices, like the ASUS Digimatrix or the ASUS ++ EEEBox 1501U. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ite-cir. ++ + config IR_STREAMZAP + tristate "Streamzap PC Remote IR Receiver" + depends on USB_ARCH_HAS_HCD +diff -Naur linux-2.6.38-rc5/drivers/media/rc/Makefile linux-2.6.38-rc5.patch/drivers/media/rc/Makefile +--- linux-2.6.38-rc5/drivers/media/rc/Makefile 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/media/rc/Makefile 2011-02-22 03:24:09.659341983 +0100 +@@ -19,4 +19,5 @@ + obj-$(CONFIG_IR_ENE) += ene_ir.o + obj-$(CONFIG_IR_STREAMZAP) += streamzap.o + obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o ++obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o + obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o +diff -Naur linux-2.6.38-rc5/drivers/staging/lirc/Kconfig linux-2.6.38-rc5.patch/drivers/staging/lirc/Kconfig +--- linux-2.6.38-rc5/drivers/staging/lirc/Kconfig 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/staging/lirc/Kconfig 2011-02-22 03:22:22.436351160 +0100 +@@ -32,12 +32,6 @@ + + Current generation iMON devices use the input layer imon driver. + +-config LIRC_IT87 +- tristate "ITE IT87XX CIR Port Receiver" +- depends on LIRC && PNP +- help +- Driver for the ITE IT87xx IR Receiver +- + config LIRC_ITE8709 + tristate "ITE8709 CIR Port Receiver" + depends on LIRC && PNP +diff -Naur linux-2.6.38-rc5/drivers/staging/lirc/lirc_it87.c linux-2.6.38-rc5.patch/drivers/staging/lirc/lirc_it87.c +--- linux-2.6.38-rc5/drivers/staging/lirc/lirc_it87.c 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/staging/lirc/lirc_it87.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,1027 +0,0 @@ +-/* +- * LIRC driver for ITE IT8712/IT8705 CIR port +- * +- * Copyright (C) 2001 Hans-Gunter Lutke Uphues +- * +- * 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. +- * +- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +- * USA +- * +- * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based +- * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula +- * +- * Attention: Sendmode only tested with debugging logs +- * +- * 2001/02/27 Christoph Bartelmus : +- * reimplemented read function +- * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix, +- * based on work of the following member of the Outertrack Digimatrix +- * Forum: Art103 +- * 2009/12/24 James Edwards implemeted support +- * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the +- * chip identifies as 18. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +- +-#include +-#include +- +-#include "lirc_it87.h" +- +-#ifdef LIRC_IT87_DIGIMATRIX +-static int digimatrix = 1; +-static int it87_freq = 36; /* kHz */ +-static int irq = 9; +-#else +-static int digimatrix; +-static int it87_freq = 38; /* kHz */ +-static int irq = IT87_CIR_DEFAULT_IRQ; +-#endif +- +-static unsigned long it87_bits_in_byte_out; +-static unsigned long it87_send_counter; +-static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN; +- +-#define RBUF_LEN 1024 +- +-#define LIRC_DRIVER_NAME "lirc_it87" +- +-/* timeout for sequences in jiffies (=5/100s) */ +-/* must be longer than TIME_CONST */ +-#define IT87_TIMEOUT (HZ*5/100) +- +-/* module parameters */ +-static int debug; +-#define dprintk(fmt, args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ +- fmt, ## args); \ +- } while (0) +- +-static int io = IT87_CIR_DEFAULT_IOBASE; +-/* receiver demodulator default: off */ +-static int it87_enable_demodulator; +- +-static int timer_enabled; +-static DEFINE_SPINLOCK(timer_lock); +-static struct timer_list timerlist; +-/* time of last signal change detected */ +-static struct timeval last_tv = {0, 0}; +-/* time of last UART data ready interrupt */ +-static struct timeval last_intr_tv = {0, 0}; +-static int last_value; +- +-static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); +- +-static DEFINE_SPINLOCK(hardware_lock); +-static DEFINE_SPINLOCK(dev_lock); +-static bool device_open; +- +-static int rx_buf[RBUF_LEN]; +-unsigned int rx_tail, rx_head; +- +-static struct pnp_driver it87_pnp_driver; +- +-/* SECTION: Prototypes */ +- +-/* Communication with user-space */ +-static int lirc_open(struct inode *inode, struct file *file); +-static int lirc_close(struct inode *inode, struct file *file); +-static unsigned int lirc_poll(struct file *file, poll_table *wait); +-static ssize_t lirc_read(struct file *file, char *buf, +- size_t count, loff_t *ppos); +-static ssize_t lirc_write(struct file *file, const char *buf, +- size_t n, loff_t *pos); +-static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +-static void add_read_queue(int flag, unsigned long val); +-static int init_chrdev(void); +-static void drop_chrdev(void); +-/* Hardware */ +-static irqreturn_t it87_interrupt(int irq, void *dev_id); +-static void send_space(unsigned long len); +-static void send_pulse(unsigned long len); +-static void init_send(void); +-static void terminate_send(unsigned long len); +-static int init_hardware(void); +-static void drop_hardware(void); +-/* Initialisation */ +-static int init_port(void); +-static void drop_port(void); +- +- +-/* SECTION: Communication with user-space */ +- +-static int lirc_open(struct inode *inode, struct file *file) +-{ +- spin_lock(&dev_lock); +- if (device_open) { +- spin_unlock(&dev_lock); +- return -EBUSY; +- } +- device_open = true; +- spin_unlock(&dev_lock); +- return 0; +-} +- +- +-static int lirc_close(struct inode *inode, struct file *file) +-{ +- spin_lock(&dev_lock); +- device_open = false; +- spin_unlock(&dev_lock); +- return 0; +-} +- +- +-static unsigned int lirc_poll(struct file *file, poll_table *wait) +-{ +- poll_wait(file, &lirc_read_queue, wait); +- if (rx_head != rx_tail) +- return POLLIN | POLLRDNORM; +- return 0; +-} +- +- +-static ssize_t lirc_read(struct file *file, char *buf, +- size_t count, loff_t *ppos) +-{ +- int n = 0; +- int retval = 0; +- +- while (n < count) { +- if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) { +- retval = -EAGAIN; +- break; +- } +- retval = wait_event_interruptible(lirc_read_queue, +- rx_head != rx_tail); +- if (retval) +- break; +- +- if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head), +- sizeof(int))) { +- retval = -EFAULT; +- break; +- } +- rx_head = (rx_head + 1) & (RBUF_LEN - 1); +- n += sizeof(int); +- } +- if (n) +- return n; +- return retval; +-} +- +- +-static ssize_t lirc_write(struct file *file, const char *buf, +- size_t n, loff_t *pos) +-{ +- int i = 0; +- int *tx_buf; +- +- if (n % sizeof(int)) +- return -EINVAL; +- tx_buf = memdup_user(buf, n); +- if (IS_ERR(tx_buf)) +- return PTR_ERR(tx_buf); +- n /= sizeof(int); +- init_send(); +- while (1) { +- if (i >= n) +- break; +- if (tx_buf[i]) +- send_pulse(tx_buf[i]); +- i++; +- if (i >= n) +- break; +- if (tx_buf[i]) +- send_space(tx_buf[i]); +- i++; +- } +- terminate_send(tx_buf[i - 1]); +- kfree(tx_buf); +- return n; +-} +- +- +-static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +-{ +- int retval = 0; +- __u32 value = 0; +- unsigned long hw_flags; +- +- if (cmd == LIRC_GET_FEATURES) +- value = LIRC_CAN_SEND_PULSE | +- LIRC_CAN_SET_SEND_CARRIER | +- LIRC_CAN_REC_MODE2; +- else if (cmd == LIRC_GET_SEND_MODE) +- value = LIRC_MODE_PULSE; +- else if (cmd == LIRC_GET_REC_MODE) +- value = LIRC_MODE_MODE2; +- +- switch (cmd) { +- case LIRC_GET_FEATURES: +- case LIRC_GET_SEND_MODE: +- case LIRC_GET_REC_MODE: +- retval = put_user(value, (__u32 *) arg); +- break; +- +- case LIRC_SET_SEND_MODE: +- case LIRC_SET_REC_MODE: +- retval = get_user(value, (__u32 *) arg); +- break; +- +- case LIRC_SET_SEND_CARRIER: +- retval = get_user(value, (__u32 *) arg); +- if (retval) +- return retval; +- value /= 1000; +- if (value > IT87_CIR_FREQ_MAX || +- value < IT87_CIR_FREQ_MIN) +- return -EINVAL; +- +- it87_freq = value; +- +- spin_lock_irqsave(&hardware_lock, hw_flags); +- outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) | +- (it87_freq - IT87_CIR_FREQ_MIN) << 3), +- io + IT87_CIR_TCR2); +- spin_unlock_irqrestore(&hardware_lock, hw_flags); +- dprintk("demodulation frequency: %d kHz\n", it87_freq); +- +- break; +- +- default: +- retval = -EINVAL; +- } +- +- if (retval) +- return retval; +- +- if (cmd == LIRC_SET_REC_MODE) { +- if (value != LIRC_MODE_MODE2) +- retval = -ENOSYS; +- } else if (cmd == LIRC_SET_SEND_MODE) { +- if (value != LIRC_MODE_PULSE) +- retval = -ENOSYS; +- } +- return retval; +-} +- +-static void add_read_queue(int flag, unsigned long val) +-{ +- unsigned int new_rx_tail; +- int newval; +- +- dprintk("add flag %d with val %lu\n", flag, val); +- +- newval = val & PULSE_MASK; +- +- /* +- * statistically, pulses are ~TIME_CONST/2 too long. we could +- * maybe make this more exact, but this is good enough +- */ +- if (flag) { +- /* pulse */ +- if (newval > TIME_CONST / 2) +- newval -= TIME_CONST / 2; +- else /* should not ever happen */ +- newval = 1; +- newval |= PULSE_BIT; +- } else +- newval += TIME_CONST / 2; +- new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); +- if (new_rx_tail == rx_head) { +- dprintk("Buffer overrun.\n"); +- return; +- } +- rx_buf[rx_tail] = newval; +- rx_tail = new_rx_tail; +- wake_up_interruptible(&lirc_read_queue); +-} +- +- +-static const struct file_operations lirc_fops = { +- .owner = THIS_MODULE, +- .read = lirc_read, +- .write = lirc_write, +- .poll = lirc_poll, +- .unlocked_ioctl = lirc_ioctl, +-#ifdef CONFIG_COMPAT +- .compat_ioctl = lirc_ioctl, +-#endif +- .open = lirc_open, +- .release = lirc_close, +- .llseek = noop_llseek, +-}; +- +-static int set_use_inc(void *data) +-{ +- return 0; +-} +- +-static void set_use_dec(void *data) +-{ +-} +- +-static struct lirc_driver driver = { +- .name = LIRC_DRIVER_NAME, +- .minor = -1, +- .code_length = 1, +- .sample_rate = 0, +- .data = NULL, +- .add_to_buf = NULL, +- .set_use_inc = set_use_inc, +- .set_use_dec = set_use_dec, +- .fops = &lirc_fops, +- .dev = NULL, +- .owner = THIS_MODULE, +-}; +- +- +-static int init_chrdev(void) +-{ +- driver.minor = lirc_register_driver(&driver); +- +- if (driver.minor < 0) { +- printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); +- return -EIO; +- } +- return 0; +-} +- +- +-static void drop_chrdev(void) +-{ +- lirc_unregister_driver(driver.minor); +-} +- +- +-/* SECTION: Hardware */ +-static long delta(struct timeval *tv1, struct timeval *tv2) +-{ +- unsigned long deltv; +- +- deltv = tv2->tv_sec - tv1->tv_sec; +- if (deltv > 15) +- deltv = 0xFFFFFF; +- else +- deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec; +- return deltv; +-} +- +-static void it87_timeout(unsigned long data) +-{ +- unsigned long flags; +- +- /* avoid interference with interrupt */ +- spin_lock_irqsave(&timer_lock, flags); +- +- if (digimatrix) { +- /* We have timed out. Disable the RX mechanism. */ +- +- outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) | +- IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR); +- if (it87_RXEN_mask) +- outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, +- io + IT87_CIR_RCR); +- dprintk(" TIMEOUT\n"); +- timer_enabled = 0; +- +- /* fifo clear */ +- outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR, +- io+IT87_CIR_TCR1); +- +- } else { +- /* +- * if last received signal was a pulse, but receiving stopped +- * within the 9 bit frame, we need to finish this pulse and +- * simulate a signal change to from pulse to space. Otherwise +- * upper layers will receive two sequences next time. +- */ +- +- if (last_value) { +- unsigned long pulse_end; +- +- /* determine 'virtual' pulse end: */ +- pulse_end = delta(&last_tv, &last_intr_tv); +- dprintk("timeout add %d for %lu usec\n", +- last_value, pulse_end); +- add_read_queue(last_value, pulse_end); +- last_value = 0; +- last_tv = last_intr_tv; +- } +- } +- spin_unlock_irqrestore(&timer_lock, flags); +-} +- +-static irqreturn_t it87_interrupt(int irq, void *dev_id) +-{ +- unsigned char data; +- struct timeval curr_tv; +- static unsigned long deltv; +- unsigned long deltintrtv; +- unsigned long flags, hw_flags; +- int iir, lsr; +- int fifo = 0; +- static char lastbit; +- char bit; +- +- /* Bit duration in microseconds */ +- const unsigned long bit_duration = 1000000ul / +- (115200 / IT87_CIR_BAUDRATE_DIVISOR); +- +- +- iir = inb(io + IT87_CIR_IIR); +- +- switch (iir & IT87_CIR_IIR_IID) { +- case 0x4: +- case 0x6: +- lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO | +- IT87_CIR_RSR_RXFBC); +- fifo = lsr & IT87_CIR_RSR_RXFBC; +- dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr); +- +- /* avoid interference with timer */ +- spin_lock_irqsave(&timer_lock, flags); +- spin_lock_irqsave(&hardware_lock, hw_flags); +- if (digimatrix) { +- static unsigned long acc_pulse; +- static unsigned long acc_space; +- +- do { +- data = inb(io + IT87_CIR_DR); +- data = ~data; +- fifo--; +- if (data != 0x00) { +- if (timer_enabled) +- del_timer(&timerlist); +- /* +- * start timer for end of +- * sequence detection +- */ +- timerlist.expires = jiffies + +- IT87_TIMEOUT; +- add_timer(&timerlist); +- timer_enabled = 1; +- } +- /* Loop through */ +- for (bit = 0; bit < 8; ++bit) { +- if ((data >> bit) & 1) { +- ++acc_pulse; +- if (lastbit == 0) { +- add_read_queue(0, +- acc_space * +- bit_duration); +- acc_space = 0; +- } +- } else { +- ++acc_space; +- if (lastbit == 1) { +- add_read_queue(1, +- acc_pulse * +- bit_duration); +- acc_pulse = 0; +- } +- } +- lastbit = (data >> bit) & 1; +- } +- +- } while (fifo != 0); +- } else { /* Normal Operation */ +- do { +- del_timer(&timerlist); +- data = inb(io + IT87_CIR_DR); +- +- dprintk("data=%02x\n", data); +- do_gettimeofday(&curr_tv); +- deltv = delta(&last_tv, &curr_tv); +- deltintrtv = delta(&last_intr_tv, &curr_tv); +- +- dprintk("t %lu , d %d\n", +- deltintrtv, (int)data); +- +- /* +- * if nothing came in last 2 cycles, +- * it was gap +- */ +- if (deltintrtv > TIME_CONST * 2) { +- if (last_value) { +- dprintk("GAP\n"); +- +- /* simulate signal change */ +- add_read_queue(last_value, +- deltv - +- deltintrtv); +- last_value = 0; +- last_tv.tv_sec = +- last_intr_tv.tv_sec; +- last_tv.tv_usec = +- last_intr_tv.tv_usec; +- deltv = deltintrtv; +- } +- } +- data = 1; +- if (data ^ last_value) { +- /* +- * deltintrtv > 2*TIME_CONST, +- * remember ? the other case is +- * timeout +- */ +- add_read_queue(last_value, +- deltv-TIME_CONST); +- last_value = data; +- last_tv = curr_tv; +- if (last_tv.tv_usec >= TIME_CONST) +- last_tv.tv_usec -= TIME_CONST; +- else { +- last_tv.tv_sec--; +- last_tv.tv_usec += 1000000 - +- TIME_CONST; +- } +- } +- last_intr_tv = curr_tv; +- if (data) { +- /* +- * start timer for end of +- * sequence detection +- */ +- timerlist.expires = +- jiffies + IT87_TIMEOUT; +- add_timer(&timerlist); +- } +- outb((inb(io + IT87_CIR_RCR) & +- ~IT87_CIR_RCR_RXEN) | +- IT87_CIR_RCR_RXACT, +- io + IT87_CIR_RCR); +- if (it87_RXEN_mask) +- outb(inb(io + IT87_CIR_RCR) | +- IT87_CIR_RCR_RXEN, +- io + IT87_CIR_RCR); +- fifo--; +- } while (fifo != 0); +- } +- spin_unlock_irqrestore(&hardware_lock, hw_flags); +- spin_unlock_irqrestore(&timer_lock, flags); +- +- return IRQ_RETVAL(IRQ_HANDLED); +- +- default: +- /* not our irq */ +- dprintk("unknown IRQ (shouldn't happen) !!\n"); +- return IRQ_RETVAL(IRQ_NONE); +- } +-} +- +- +-static void send_it87(unsigned long len, unsigned long stime, +- unsigned char send_byte, unsigned int count_bits) +-{ +- long count = len / stime; +- long time_left = 0; +- static unsigned char byte_out; +- unsigned long hw_flags; +- +- dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte); +- +- time_left = (long)len - (long)count * (long)stime; +- count += ((2 * time_left) / stime); +- while (count) { +- long i = 0; +- for (i = 0; i < count_bits; i++) { +- byte_out = (byte_out << 1) | (send_byte & 1); +- it87_bits_in_byte_out++; +- } +- if (it87_bits_in_byte_out == 8) { +- dprintk("out=0x%x, tsr_txfbc: 0x%x\n", +- byte_out, +- inb(io + IT87_CIR_TSR) & +- IT87_CIR_TSR_TXFBC); +- +- while ((inb(io + IT87_CIR_TSR) & +- IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE) +- ; +- +- spin_lock_irqsave(&hardware_lock, hw_flags); +- outb(byte_out, io + IT87_CIR_DR); +- spin_unlock_irqrestore(&hardware_lock, hw_flags); +- +- it87_bits_in_byte_out = 0; +- it87_send_counter++; +- byte_out = 0; +- } +- count--; +- } +-} +- +- +-/*TODO: maybe exchange space and pulse because it8705 only modulates 0-bits */ +- +-static void send_space(unsigned long len) +-{ +- send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR); +-} +- +-static void send_pulse(unsigned long len) +-{ +- send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR); +-} +- +- +-static void init_send() +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&hardware_lock, flags); +- /* RXEN=0: receiver disable */ +- it87_RXEN_mask = 0; +- outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN, +- io + IT87_CIR_RCR); +- spin_unlock_irqrestore(&hardware_lock, flags); +- it87_bits_in_byte_out = 0; +- it87_send_counter = 0; +-} +- +- +-static void terminate_send(unsigned long len) +-{ +- unsigned long flags; +- unsigned long last = 0; +- +- last = it87_send_counter; +- /* make sure all necessary data has been sent */ +- while (last == it87_send_counter) +- send_space(len); +- /* wait until all data sent */ +- while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0) +- ; +- /* then re-enable receiver */ +- spin_lock_irqsave(&hardware_lock, flags); +- it87_RXEN_mask = IT87_CIR_RCR_RXEN; +- outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, +- io + IT87_CIR_RCR); +- spin_unlock_irqrestore(&hardware_lock, flags); +-} +- +- +-static int init_hardware(void) +-{ +- unsigned long flags; +- unsigned char it87_rcr = 0; +- +- spin_lock_irqsave(&hardware_lock, flags); +- /* init cir-port */ +- /* enable r/w-access to Baudrate-Register */ +- outb(IT87_CIR_IER_BR, io + IT87_CIR_IER); +- outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR); +- outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR); +- /* Baudrate Register off, define IRQs: Input only */ +- if (digimatrix) { +- outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER); +- /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */ +- } else { +- outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER); +- /* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */ +- } +- it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1; +- if (it87_enable_demodulator) +- it87_rcr |= IT87_CIR_RCR_RXEND; +- outb(it87_rcr, io + IT87_CIR_RCR); +- if (digimatrix) { +- /* Set FIFO depth to 1 byte, and disable TX */ +- outb(inb(io + IT87_CIR_TCR1) | 0x00, +- io + IT87_CIR_TCR1); +- +- /* +- * TX: it87_freq (36kHz), 'reserved' sensitivity +- * setting (0x00) +- */ +- outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00, +- io + IT87_CIR_TCR2); +- } else { +- /* TX: 38kHz, 13,3us (pulse-width) */ +- outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06, +- io + IT87_CIR_TCR2); +- } +- spin_unlock_irqrestore(&hardware_lock, flags); +- return 0; +-} +- +- +-static void drop_hardware(void) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&hardware_lock, flags); +- disable_irq(irq); +- /* receiver disable */ +- it87_RXEN_mask = 0; +- outb(0x1, io + IT87_CIR_RCR); +- /* turn off irqs */ +- outb(0, io + IT87_CIR_IER); +- /* fifo clear */ +- outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1); +- /* reset */ +- outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); +- enable_irq(irq); +- spin_unlock_irqrestore(&hardware_lock, flags); +-} +- +- +-static unsigned char it87_read(unsigned char port) +-{ +- outb(port, IT87_ADRPORT); +- return inb(IT87_DATAPORT); +-} +- +- +-static void it87_write(unsigned char port, unsigned char data) +-{ +- outb(port, IT87_ADRPORT); +- outb(data, IT87_DATAPORT); +-} +- +- +-/* SECTION: Initialisation */ +- +-static int init_port(void) +-{ +- unsigned long hw_flags; +- int retval = 0; +- +- unsigned char init_bytes[4] = IT87_INIT; +- unsigned char it87_chipid = 0; +- unsigned char ldn = 0; +- unsigned int it87_io = 0; +- unsigned int it87_irq = 0; +- +- /* Enter MB PnP Mode */ +- outb(init_bytes[0], IT87_ADRPORT); +- outb(init_bytes[1], IT87_ADRPORT); +- outb(init_bytes[2], IT87_ADRPORT); +- outb(init_bytes[3], IT87_ADRPORT); +- +- /* 8712 or 8705 ? */ +- it87_chipid = it87_read(IT87_CHIP_ID1); +- if (it87_chipid != 0x87) { +- retval = -ENXIO; +- return retval; +- } +- it87_chipid = it87_read(IT87_CHIP_ID2); +- if ((it87_chipid != 0x05) && +- (it87_chipid != 0x12) && +- (it87_chipid != 0x18) && +- (it87_chipid != 0x20)) { +- printk(KERN_INFO LIRC_DRIVER_NAME +- ": no IT8704/05/12/18/20 found (claimed IT87%02x), " +- "exiting..\n", it87_chipid); +- retval = -ENXIO; +- return retval; +- } +- printk(KERN_INFO LIRC_DRIVER_NAME +- ": found IT87%02x.\n", +- it87_chipid); +- +- /* get I/O-Port and IRQ */ +- if (it87_chipid == 0x12 || it87_chipid == 0x18) +- ldn = IT8712_CIR_LDN; +- else +- ldn = IT8705_CIR_LDN; +- it87_write(IT87_LDN, ldn); +- +- it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 + +- it87_read(IT87_CIR_BASE_LSB); +- if (it87_io == 0) { +- if (io == 0) +- io = IT87_CIR_DEFAULT_IOBASE; +- printk(KERN_INFO LIRC_DRIVER_NAME +- ": set default io 0x%x\n", +- io); +- it87_write(IT87_CIR_BASE_MSB, io / 0x100); +- it87_write(IT87_CIR_BASE_LSB, io % 0x100); +- } else +- io = it87_io; +- +- it87_irq = it87_read(IT87_CIR_IRQ); +- if (digimatrix || it87_irq == 0) { +- if (irq == 0) +- irq = IT87_CIR_DEFAULT_IRQ; +- printk(KERN_INFO LIRC_DRIVER_NAME +- ": set default irq 0x%x\n", +- irq); +- it87_write(IT87_CIR_IRQ, irq); +- } else +- irq = it87_irq; +- +- spin_lock_irqsave(&hardware_lock, hw_flags); +- /* reset */ +- outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); +- /* fifo clear */ +- outb(IT87_CIR_TCR1_FIFOCLR | +- /* IT87_CIR_TCR1_ILE | */ +- IT87_CIR_TCR1_TXRLE | +- IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1); +- spin_unlock_irqrestore(&hardware_lock, hw_flags); +- +- /* get I/O port access and IRQ line */ +- if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { +- printk(KERN_ERR LIRC_DRIVER_NAME +- ": i/o port 0x%.4x already in use.\n", io); +- /* Leaving MB PnP Mode */ +- it87_write(IT87_CFGCTRL, 0x2); +- return -EBUSY; +- } +- +- /* activate CIR-Device */ +- it87_write(IT87_CIR_ACT, 0x1); +- +- /* Leaving MB PnP Mode */ +- it87_write(IT87_CFGCTRL, 0x2); +- +- retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/, +- LIRC_DRIVER_NAME, NULL); +- if (retval < 0) { +- printk(KERN_ERR LIRC_DRIVER_NAME +- ": IRQ %d already in use.\n", +- irq); +- release_region(io, 8); +- return retval; +- } +- +- printk(KERN_INFO LIRC_DRIVER_NAME +- ": I/O port 0x%.4x, IRQ %d.\n", io, irq); +- +- init_timer(&timerlist); +- timerlist.function = it87_timeout; +- timerlist.data = 0xabadcafe; +- +- return 0; +-} +- +- +-static void drop_port(void) +-{ +-#if 0 +- unsigned char init_bytes[4] = IT87_INIT; +- +- /* Enter MB PnP Mode */ +- outb(init_bytes[0], IT87_ADRPORT); +- outb(init_bytes[1], IT87_ADRPORT); +- outb(init_bytes[2], IT87_ADRPORT); +- outb(init_bytes[3], IT87_ADRPORT); +- +- /* deactivate CIR-Device */ +- it87_write(IT87_CIR_ACT, 0x0); +- +- /* Leaving MB PnP Mode */ +- it87_write(IT87_CFGCTRL, 0x2); +-#endif +- +- del_timer_sync(&timerlist); +- free_irq(irq, NULL); +- release_region(io, 8); +-} +- +- +-static int init_lirc_it87(void) +-{ +- int retval; +- +- init_waitqueue_head(&lirc_read_queue); +- retval = init_port(); +- if (retval < 0) +- return retval; +- init_hardware(); +- printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n"); +- return 0; +-} +- +-static int it87_probe(struct pnp_dev *pnp_dev, +- const struct pnp_device_id *dev_id) +-{ +- int retval; +- +- driver.dev = &pnp_dev->dev; +- +- retval = init_chrdev(); +- if (retval < 0) +- return retval; +- +- retval = init_lirc_it87(); +- if (retval) +- goto init_lirc_it87_failed; +- +- return 0; +- +-init_lirc_it87_failed: +- drop_chrdev(); +- +- return retval; +-} +- +-static int __init lirc_it87_init(void) +-{ +- return pnp_register_driver(&it87_pnp_driver); +-} +- +- +-static void __exit lirc_it87_exit(void) +-{ +- drop_hardware(); +- drop_chrdev(); +- drop_port(); +- pnp_unregister_driver(&it87_pnp_driver); +- printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); +-} +- +-/* SECTION: PNP for ITE8704/13/18 */ +- +-static const struct pnp_device_id pnp_dev_table[] = { +- {"ITE8704", 0}, +- {"ITE8713", 0}, +- {} +-}; +- +-MODULE_DEVICE_TABLE(pnp, pnp_dev_table); +- +-static struct pnp_driver it87_pnp_driver = { +- .name = LIRC_DRIVER_NAME, +- .id_table = pnp_dev_table, +- .probe = it87_probe, +-}; +- +-module_init(lirc_it87_init); +-module_exit(lirc_it87_exit); +- +-MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port"); +-MODULE_AUTHOR("Hans-Gunter Lutke Uphues"); +-MODULE_LICENSE("GPL"); +- +-module_param(io, int, S_IRUGO); +-MODULE_PARM_DESC(io, "I/O base address (default: 0x310)"); +- +-module_param(irq, int, S_IRUGO); +-#ifdef LIRC_IT87_DIGIMATRIX +-MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)"); +-#else +-MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)"); +-#endif +- +-module_param(it87_enable_demodulator, bool, S_IRUGO); +-MODULE_PARM_DESC(it87_enable_demodulator, +- "Receiver demodulator enable/disable (1/0), default: 0"); +- +-module_param(debug, bool, S_IRUGO | S_IWUSR); +-MODULE_PARM_DESC(debug, "Enable debugging messages"); +- +-module_param(digimatrix, bool, S_IRUGO | S_IWUSR); +-#ifdef LIRC_IT87_DIGIMATRIX +-MODULE_PARM_DESC(digimatrix, +- "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1"); +-#else +-MODULE_PARM_DESC(digimatrix, +- "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0"); +-#endif +- +- +-module_param(it87_freq, int, S_IRUGO); +-#ifdef LIRC_IT87_DIGIMATRIX +-MODULE_PARM_DESC(it87_freq, +- "Carrier demodulator frequency (kHz), (default: 36)"); +-#else +-MODULE_PARM_DESC(it87_freq, +- "Carrier demodulator frequency (kHz), (default: 38)"); +-#endif +diff -Naur linux-2.6.38-rc5/drivers/staging/lirc/lirc_it87.h linux-2.6.38-rc5.patch/drivers/staging/lirc/lirc_it87.h +--- linux-2.6.38-rc5/drivers/staging/lirc/lirc_it87.h 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/staging/lirc/lirc_it87.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,116 +0,0 @@ +-/* lirc_it87.h */ +-/* SECTION: Definitions */ +- +-/********************************* ITE IT87xx ************************/ +- +-/* based on the following documentation from ITE: +- a) IT8712F Preliminary CIR Programming Guide V0.1 +- b) IT8705F Simple LPC I/O Preliminary Specification V0.3 +- c) IT8712F EC-LPC I/O Preliminary Specification V0.5 +-*/ +- +-/* IT8712/05 Ports: */ +-#define IT87_ADRPORT 0x2e +-#define IT87_DATAPORT 0x2f +-#define IT87_INIT {0x87, 0x01, 0x55, 0x55} +- +-/* alternate Ports: */ +-/* +-#define IT87_ADRPORT 0x4e +-#define IT87_DATAPORT 0x4f +-#define IT87_INIT {0x87, 0x01, 0x55, 0xaa} +- */ +- +-/* IT8712/05 Registers */ +-#define IT87_CFGCTRL 0x2 +-#define IT87_LDN 0x7 +-#define IT87_CHIP_ID1 0x20 +-#define IT87_CHIP_ID2 0x21 +-#define IT87_CFG_VERSION 0x22 +-#define IT87_SWSUSPEND 0x23 +- +-#define IT8712_CIR_LDN 0xa +-#define IT8705_CIR_LDN 0x7 +- +-/* CIR Configuration Registers: */ +-#define IT87_CIR_ACT 0x30 +-#define IT87_CIR_BASE_MSB 0x60 +-#define IT87_CIR_BASE_LSB 0x61 +-#define IT87_CIR_IRQ 0x70 +-#define IT87_CIR_CONFIG 0xf0 +- +-/* List of IT87_CIR registers: offset to BaseAddr */ +-#define IT87_CIR_DR 0 +-#define IT87_CIR_IER 1 +-#define IT87_CIR_RCR 2 +-#define IT87_CIR_TCR1 3 +-#define IT87_CIR_TCR2 4 +-#define IT87_CIR_TSR 5 +-#define IT87_CIR_RSR 6 +-#define IT87_CIR_BDLR 5 +-#define IT87_CIR_BDHR 6 +-#define IT87_CIR_IIR 7 +- +-/* Bit Definition */ +-/* IER: */ +-#define IT87_CIR_IER_TM_EN 0x80 +-#define IT87_CIR_IER_RESEVED 0x40 +-#define IT87_CIR_IER_RESET 0x20 +-#define IT87_CIR_IER_BR 0x10 +-#define IT87_CIR_IER_IEC 0x8 +-#define IT87_CIR_IER_RFOIE 0x4 +-#define IT87_CIR_IER_RDAIE 0x2 +-#define IT87_CIR_IER_TLDLIE 0x1 +- +-/* RCR: */ +-#define IT87_CIR_RCR_RDWOS 0x80 +-#define IT87_CIR_RCR_HCFS 0x40 +-#define IT87_CIR_RCR_RXEN 0x20 +-#define IT87_CIR_RCR_RXEND 0x10 +-#define IT87_CIR_RCR_RXACT 0x8 +-#define IT87_CIR_RCR_RXDCR 0x7 +- +-/* TCR1: */ +-#define IT87_CIR_TCR1_FIFOCLR 0x80 +-#define IT87_CIR_TCR1_ILE 0x40 +-#define IT87_CIR_TCR1_FIFOTL 0x30 +-#define IT87_CIR_TCR1_TXRLE 0x8 +-#define IT87_CIR_TCR1_TXENDF 0x4 +-#define IT87_CIR_TCR1_TXMPM 0x3 +- +-/* TCR2: */ +-#define IT87_CIR_TCR2_CFQ 0xf8 +-#define IT87_CIR_TCR2_TXMPW 0x7 +- +-/* TSR: */ +-#define IT87_CIR_TSR_RESERVED 0xc0 +-#define IT87_CIR_TSR_TXFBC 0x3f +- +-/* RSR: */ +-#define IT87_CIR_RSR_RXFTO 0x80 +-#define IT87_CIR_RSR_RESERVED 0x40 +-#define IT87_CIR_RSR_RXFBC 0x3f +- +-/* IIR: */ +-#define IT87_CIR_IIR_RESERVED 0xf8 +-#define IT87_CIR_IIR_IID 0x6 +-#define IT87_CIR_IIR_IIP 0x1 +- +-/* TM: */ +-#define IT87_CIR_TM_IL_SEL 0x80 +-#define IT87_CIR_TM_RESERVED 0x40 +-#define IT87_CIR_TM_TM_REG 0x3f +- +-#define IT87_CIR_FIFO_SIZE 32 +- +-/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */ +-#define IT87_CIR_BAUDRATE_DIVISOR 0x1 +-#define IT87_CIR_DEFAULT_IOBASE 0x310 +-#define IT87_CIR_DEFAULT_IRQ 0x7 +-#define IT87_CIR_SPACE 0x00 +-#define IT87_CIR_PULSE 0xff +-#define IT87_CIR_FREQ_MIN 27 +-#define IT87_CIR_FREQ_MAX 58 +-#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul) +- +-/********************************* ITE IT87xx ************************/ +diff -Naur linux-2.6.38-rc5/drivers/staging/lirc/Makefile linux-2.6.38-rc5.patch/drivers/staging/lirc/Makefile +--- linux-2.6.38-rc5/drivers/staging/lirc/Makefile 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/staging/lirc/Makefile 2011-02-22 03:19:26.756636211 +0100 +@@ -6,7 +6,6 @@ + obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o + obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o + obj-$(CONFIG_LIRC_IMON) += lirc_imon.o +-obj-$(CONFIG_LIRC_IT87) += lirc_it87.o + obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o + obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o + obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o diff --git a/packages/linux/patches/linux-2.6.38-052-aureal_remote_quirk-0.1.patch b/packages/linux/patches/linux-2.6.38-052-aureal_remote_quirk-0.1.patch new file mode 100644 index 0000000000..60cdbf0938 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-052-aureal_remote_quirk-0.1.patch @@ -0,0 +1,113 @@ +diff -Naur linux-2.6.37/drivers/hid/hid-aureal.c linux-2.6.37.patch/drivers/hid/hid-aureal.c +--- linux-2.6.37/drivers/hid/hid-aureal.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.37.patch/drivers/hid/hid-aureal.c 2011-01-07 22:35:31.413389936 +0100 +@@ -0,0 +1,60 @@ ++/* ++ * HID driver for some sunplus "special" devices ++ * ++ * Copyright (c) 1999 Andreas Gal ++ * Copyright (c) 2000-2005 Vojtech Pavlik ++ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc ++ * Copyright (c) 2006-2007 Jiri Kosina ++ * Copyright (c) 2007 Paul Walmsley ++ * Copyright (c) 2008 Jiri Slaby ++ * Copyright (c) 2010 Franco Catrin ++ */ ++ ++/* ++ * 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 ++#include ++#include ++ ++#include "hid-ids.h" ++ ++static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc, ++ unsigned int *rsize) ++{ ++ if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) { ++ dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 " ++ "report descriptor. Keyboard Logical Maximum = 101\n"); ++ rdesc[53] = 0x65; ++ } return rdesc; ++} ++ ++static const struct hid_device_id aureal_devices[] = { ++ { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(hid, aureal_devices); ++ ++static struct hid_driver aureal_driver = { ++ .name = "aureal", ++ .id_table = aureal_devices, ++ .report_fixup = aureal_report_fixup, ++}; ++ ++static int __init aureal_init(void) ++{ ++ return hid_register_driver(&aureal_driver); ++} ++ ++static void __exit aureal_exit(void) ++{ ++ hid_unregister_driver(&aureal_driver); ++} ++ ++module_init(aureal_init); ++module_exit(aureal_exit); ++MODULE_LICENSE("GPL"); +diff -Naur linux-2.6.37/drivers/hid/hid-ids.h linux-2.6.37.patch/drivers/hid/hid-ids.h +--- linux-2.6.37/drivers/hid/hid-ids.h 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37.patch/drivers/hid/hid-ids.h 2011-01-07 22:35:31.414389949 +0100 +@@ -6,6 +6,7 @@ + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley ++ * Copyright (c) 2010 Franco Catrin + */ + + /* +@@ -327,6 +328,9 @@ + #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 + #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 + ++#define USB_VENDOR_ID_AUREAL 0x0755 ++#define USB_DEVICE_ID_AUREAL_W01RN 0x2626 ++ + #define USB_VENDOR_ID_LABTEC 0x1020 + #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 + +diff -Naur linux-2.6.37/drivers/hid/Kconfig linux-2.6.37.patch/drivers/hid/Kconfig +--- linux-2.6.37/drivers/hid/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37.patch/drivers/hid/Kconfig 2011-01-07 22:35:31.467390603 +0100 +@@ -87,6 +87,13 @@ + Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, + MacBooks, MacBook Pros and Apple Aluminum. + ++config HID_AUREAL ++ tristate "Aureal" if EMBEDDED ++ depends on USB_HID ++ default !EMBEDDED ++ ---help--- ++ Support for Aureal Cy se W-01RN Remote Controller ++ + config HID_BELKIN + tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED + depends on USB_HID +diff -Naur linux-2.6.37/drivers/hid/Makefile linux-2.6.37.patch/drivers/hid/Makefile +--- linux-2.6.37/drivers/hid/Makefile 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37.patch/drivers/hid/Makefile 2011-01-07 22:35:31.547391590 +0100 +@@ -29,6 +29,7 @@ + obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o + obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o + obj-$(CONFIG_HID_APPLE) += hid-apple.o ++obj-$(CONFIG_HID_AUREAL) += hid-aureal.o + obj-$(CONFIG_HID_BELKIN) += hid-belkin.o + obj-$(CONFIG_HID_CANDO) += hid-cando.o + obj-$(CONFIG_HID_CHERRY) += hid-cherry.o diff --git a/packages/linux/patches/linux-2.6.38-053_ati-remote_all_keys_and_keychange-0.1.patch b/packages/linux/patches/linux-2.6.38-053_ati-remote_all_keys_and_keychange-0.1.patch new file mode 100644 index 0000000000..9ed2b41967 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-053_ati-remote_all_keys_and_keychange-0.1.patch @@ -0,0 +1,65 @@ +diff -Naur linux-2.6.37-rc6/drivers/input/misc/ati_remote.c linux-2.6.37-rc6.patch/drivers/input/misc/ati_remote.c +--- linux-2.6.37-rc6/drivers/input/misc/ati_remote.c 2010-12-16 02:24:48.000000000 +0100 ++++ linux-2.6.37-rc6.patch/drivers/input/misc/ati_remote.c 2010-12-16 12:35:04.454620549 +0100 +@@ -90,6 +90,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -131,6 +132,10 @@ + module_param(debug, int, 0644); + MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); + ++static int keychange; ++module_param(keychange, int, 0644); ++MODULE_PARM_DESC(keychange, "Enable support for Keychange remotes"); ++ + static int repeat_filter = FILTER_TIME; + module_param(repeat_filter, int, 0644); + MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec"); +@@ -285,6 +290,25 @@ + {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */ + {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */ + ++ /* Coloured keys */ ++ {KIND_FILTERED, 0xf7, 0x32, EV_KEY, KEY_PLAYCD, 1}, /* TXT Rot */ ++ {KIND_FILTERED, 0xf8, 0x33, EV_KEY, KEY_PAUSECD, 1}, /* TXT Gr */ ++ {KIND_FILTERED, 0xf9, 0x34, EV_KEY, KEY_PROG3, 1}, /* TXT Gelb */ ++ {KIND_FILTERED, 0xfa, 0x35, EV_KEY, KEY_PROG4, 1}, /* TXT Blau */ ++ ++ /* More Keys */ ++ {KIND_FILTERED, 0xac, 0x37, EV_KEY, KEY_G, 1}, /* Acquire Image */ ++ {KIND_FILTERED, 0xfe, 0x39, EV_KEY, KEY_H, 1}, /* FULL SCREEN _| */ ++ {KIND_FILTERED, 0xfb, 0x36, EV_KEY, KEY_I, 1}, /* Rename */ ++ {KIND_FILTERED, 0xf1, 0x2c, EV_KEY, KEY_J, 1}, /* TV */ ++ {KIND_FILTERED, 0xf6, 0x31, EV_KEY, KEY_K, 1}, /* Video Desktop */ ++ {KIND_FILTERED, 0xa1, 0x00, EV_KEY, KEY_L, 1}, ++ ++ /* Keys added by torsten at archesoft.de */ ++ {KIND_FILTERED, 0xff, 0x3a, EV_KEY, KEY_M, 1}, /* dvd audio */ ++ {KIND_FILTERED, 0xfc, 0x37, EV_KEY, KEY_N, 1}, /* aquire image */ ++ {KIND_FILTERED, 0xfd, 0x38, EV_KEY, KEY_O, 1}, /* edit image */ ++ + {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0} + }; + +@@ -409,6 +433,16 @@ + { + int i; + ++ /* If keychange is enabled we undo the keychanges of the remote here */ ++ ++ if (keychange){ ++ if (d2 & 0x80) { ++ d1 += 0x80; ++ d2 -= 0x80; ++ } ++ } ++ ++ + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { + /* + * Decide if the table entry matches the remote input. diff --git a/packages/linux/patches/linux-2.6.38-062-Pioneer_DVR-216D_failed_xfermode-0.1.patch b/packages/linux/patches/linux-2.6.38-062-Pioneer_DVR-216D_failed_xfermode-0.1.patch new file mode 100644 index 0000000000..432a72d48d --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-062-Pioneer_DVR-216D_failed_xfermode-0.1.patch @@ -0,0 +1,11 @@ +diff -Naur linux-2.6.38-rc5/drivers/ata/libata-core.c linux-2.6.38-rc5.patch/drivers/ata/libata-core.c +--- linux-2.6.38-rc5/drivers/ata/libata-core.c 2011-02-16 04:23:45.000000000 +0100 ++++ linux-2.6.38-rc5.patch/drivers/ata/libata-core.c 2011-02-22 00:57:25.789636966 +0100 +@@ -4139,6 +4139,7 @@ + */ + { "PIONEER DVD-RW DVRTD08", "1.00", ATA_HORKAGE_NOSETXFER }, + { "PIONEER DVD-RW DVR-212D", "1.28", ATA_HORKAGE_NOSETXFER }, ++ { "PIONEER DVD-RW DVR-216D", "1.07", ATA_HORKAGE_NOSETXFER }, + + /* End Marker */ + { } diff --git a/packages/linux/patches/linux-2.6.38-110-drm_nouveau_upstream-20110312.patch b/packages/linux/patches/linux-2.6.38-110-drm_nouveau_upstream-20110312.patch new file mode 100644 index 0000000000..4abd334599 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-110-drm_nouveau_upstream-20110312.patch @@ -0,0 +1,5016 @@ +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_bios.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_bios.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.c 2011-03-09 18:30:58.000000000 +0100 +@@ -282,7 +282,7 @@ + { + #if 0 + sync(); +- msleep(2); ++ mdelay(2); + #endif + } + +@@ -1904,7 +1904,7 @@ + BIOSLOG(bios, "0x%04X: " + "Condition not met, sleeping for 20ms\n", + offset); +- msleep(20); ++ mdelay(20); + } + } + +@@ -1938,7 +1938,7 @@ + BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X milliseconds\n", + offset, time); + +- msleep(time); ++ mdelay(time); + + return 3; + } +@@ -2962,7 +2962,7 @@ + if (time < 1000) + udelay(time); + else +- msleep((time + 900) / 1000); ++ mdelay((time + 900) / 1000); + + return 3; + } +@@ -3856,7 +3856,7 @@ + + if (script == LVDS_PANEL_OFF) { + /* off-on delay in ms */ +- msleep(ROM16(bios->data[bios->fp.xlated_entry + 7])); ++ mdelay(ROM16(bios->data[bios->fp.xlated_entry + 7])); + } + #ifdef __powerpc__ + /* Powerbook specific quirks */ +@@ -5950,6 +5950,11 @@ + } + } + ++static const u8 hpd_gpio[16] = { ++ 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60, ++}; ++ + static void + parse_dcb_connector_table(struct nvbios *bios) + { +@@ -5986,23 +5991,9 @@ + + cte->type = (cte->entry & 0x000000ff) >> 0; + cte->index2 = (cte->entry & 0x00000f00) >> 8; +- switch (cte->entry & 0x00033000) { +- case 0x00001000: +- cte->gpio_tag = 0x07; +- break; +- case 0x00002000: +- cte->gpio_tag = 0x08; +- break; +- case 0x00010000: +- cte->gpio_tag = 0x51; +- break; +- case 0x00020000: +- cte->gpio_tag = 0x52; +- break; +- default: +- cte->gpio_tag = 0xff; +- break; +- } ++ ++ cte->gpio_tag = ffs((cte->entry & 0x07033000) >> 12); ++ cte->gpio_tag = hpd_gpio[cte->gpio_tag]; + + if (cte->type == 0xff) + continue; +@@ -6702,11 +6693,11 @@ + struct nvbios *bios = &dev_priv->vbios; + struct init_exec iexec = { true, false }; + +- mutex_lock(&bios->lock); ++ spin_lock_bh(&bios->lock); + bios->display.output = dcbent; + parse_init_table(bios, table, &iexec); + bios->display.output = NULL; +- mutex_unlock(&bios->lock); ++ spin_unlock_bh(&bios->lock); + } + + static bool NVInitVBIOS(struct drm_device *dev) +@@ -6715,7 +6706,7 @@ + struct nvbios *bios = &dev_priv->vbios; + + memset(bios, 0, sizeof(struct nvbios)); +- mutex_init(&bios->lock); ++ spin_lock_init(&bios->lock); + bios->dev = dev; + + if (!NVShadowVBIOS(dev, bios->data)) +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_bios.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_bios.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_bios.h 2011-03-09 18:30:58.000000000 +0100 +@@ -251,7 +251,7 @@ + uint8_t digital_min_front_porch; + bool fp_no_ddc; + +- struct mutex lock; ++ spinlock_t lock; + + uint8_t data[NV_PROM_SIZE]; + unsigned int length; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_bo.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_bo.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_bo.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_bo.c 2011-03-09 18:30:58.000000000 +0100 +@@ -57,8 +57,8 @@ + } + + static void +-nouveau_bo_fixup_align(struct nouveau_bo *nvbo, int *align, int *size, +- int *page_shift) ++nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, ++ int *align, int *size, int *page_shift) + { + struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev); + +@@ -83,7 +83,7 @@ + } + } else { + if (likely(dev_priv->chan_vm)) { +- if (*size > 256 * 1024) ++ if (!(flags & TTM_PL_FLAG_TT) && *size > 256 * 1024) + *page_shift = dev_priv->chan_vm->lpg_shift; + else + *page_shift = dev_priv->chan_vm->spg_shift; +@@ -101,8 +101,7 @@ + int + nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, + int size, int align, uint32_t flags, uint32_t tile_mode, +- uint32_t tile_flags, bool no_vm, bool mappable, +- struct nouveau_bo **pnvbo) ++ uint32_t tile_flags, struct nouveau_bo **pnvbo) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_bo *nvbo; +@@ -113,16 +112,14 @@ + return -ENOMEM; + INIT_LIST_HEAD(&nvbo->head); + INIT_LIST_HEAD(&nvbo->entry); +- nvbo->mappable = mappable; +- nvbo->no_vm = no_vm; + nvbo->tile_mode = tile_mode; + nvbo->tile_flags = tile_flags; + nvbo->bo.bdev = &dev_priv->ttm.bdev; + +- nouveau_bo_fixup_align(nvbo, &align, &size, &page_shift); ++ nouveau_bo_fixup_align(nvbo, flags, &align, &size, &page_shift); + align >>= PAGE_SHIFT; + +- if (!nvbo->no_vm && dev_priv->chan_vm) { ++ if (dev_priv->chan_vm) { + ret = nouveau_vm_get(dev_priv->chan_vm, size, page_shift, + NV_MEM_ACCESS_RW, &nvbo->vma); + if (ret) { +@@ -144,11 +141,8 @@ + } + nvbo->channel = NULL; + +- if (nvbo->vma.node) { +- if (nvbo->bo.mem.mem_type == TTM_PL_VRAM) +- nvbo->bo.offset = nvbo->vma.offset; +- } +- ++ if (nvbo->vma.node) ++ nvbo->bo.offset = nvbo->vma.offset; + *pnvbo = nvbo; + return 0; + } +@@ -318,11 +312,8 @@ + if (ret) + return ret; + +- if (nvbo->vma.node) { +- if (nvbo->bo.mem.mem_type == TTM_PL_VRAM) +- nvbo->bo.offset = nvbo->vma.offset; +- } +- ++ if (nvbo->vma.node) ++ nvbo->bo.offset = nvbo->vma.offset; + return 0; + } + +@@ -385,7 +376,8 @@ + case NOUVEAU_GART_AGP: + return ttm_agp_backend_init(bdev, dev->agp->bridge); + #endif +- case NOUVEAU_GART_SGDMA: ++ case NOUVEAU_GART_PDMA: ++ case NOUVEAU_GART_HW: + return nouveau_sgdma_init_ttm(dev); + default: + NV_ERROR(dev, "Unknown GART type %d\n", +@@ -431,7 +423,10 @@ + man->default_caching = TTM_PL_FLAG_WC; + break; + case TTM_PL_TT: +- man->func = &ttm_bo_manager_func; ++ if (dev_priv->card_type >= NV_50) ++ man->func = &nouveau_gart_manager; ++ else ++ man->func = &ttm_bo_manager_func; + switch (dev_priv->gart_info.type) { + case NOUVEAU_GART_AGP: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; +@@ -439,7 +434,8 @@ + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; +- case NOUVEAU_GART_SGDMA: ++ case NOUVEAU_GART_PDMA: ++ case NOUVEAU_GART_HW: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | + TTM_MEMTYPE_FLAG_CMA; + man->available_caching = TTM_PL_MASK_CACHING; +@@ -501,45 +497,22 @@ + return ret; + } + +-static inline uint32_t +-nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo, +- struct nouveau_channel *chan, struct ttm_mem_reg *mem) +-{ +- struct nouveau_bo *nvbo = nouveau_bo(bo); +- +- if (nvbo->no_vm) { +- if (mem->mem_type == TTM_PL_TT) +- return NvDmaGART; +- return NvDmaVRAM; +- } +- +- if (mem->mem_type == TTM_PL_TT) +- return chan->gart_handle; +- return chan->vram_handle; +-} +- + static int + nvc0_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + { +- struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); ++ struct nouveau_mem *old_node = old_mem->mm_node; ++ struct nouveau_mem *new_node = new_mem->mm_node; + struct nouveau_bo *nvbo = nouveau_bo(bo); +- u64 src_offset = old_mem->start << PAGE_SHIFT; +- u64 dst_offset = new_mem->start << PAGE_SHIFT; + u32 page_count = new_mem->num_pages; ++ u64 src_offset, dst_offset; + int ret; + +- if (!nvbo->no_vm) { +- if (old_mem->mem_type == TTM_PL_VRAM) +- src_offset = nvbo->vma.offset; +- else +- src_offset += dev_priv->gart_info.aper_base; +- +- if (new_mem->mem_type == TTM_PL_VRAM) +- dst_offset = nvbo->vma.offset; +- else +- dst_offset += dev_priv->gart_info.aper_base; +- } ++ src_offset = old_node->tmp_vma.offset; ++ if (new_node->tmp_vma.node) ++ dst_offset = new_node->tmp_vma.offset; ++ else ++ dst_offset = nvbo->vma.offset; + + page_count = new_mem->num_pages; + while (page_count) { +@@ -574,33 +547,18 @@ + nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) + { +- struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); ++ struct nouveau_mem *old_node = old_mem->mm_node; ++ struct nouveau_mem *new_node = new_mem->mm_node; + struct nouveau_bo *nvbo = nouveau_bo(bo); + u64 length = (new_mem->num_pages << PAGE_SHIFT); + u64 src_offset, dst_offset; + int ret; + +- src_offset = old_mem->start << PAGE_SHIFT; +- dst_offset = new_mem->start << PAGE_SHIFT; +- if (!nvbo->no_vm) { +- if (old_mem->mem_type == TTM_PL_VRAM) +- src_offset = nvbo->vma.offset; +- else +- src_offset += dev_priv->gart_info.aper_base; +- +- if (new_mem->mem_type == TTM_PL_VRAM) +- dst_offset = nvbo->vma.offset; +- else +- dst_offset += dev_priv->gart_info.aper_base; +- } +- +- ret = RING_SPACE(chan, 3); +- if (ret) +- return ret; +- +- BEGIN_RING(chan, NvSubM2MF, 0x0184, 2); +- OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem)); +- OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem)); ++ src_offset = old_node->tmp_vma.offset; ++ if (new_node->tmp_vma.node) ++ dst_offset = new_node->tmp_vma.offset; ++ else ++ dst_offset = nvbo->vma.offset; + + while (length) { + u32 amount, stride, height; +@@ -681,6 +639,15 @@ + return 0; + } + ++static inline uint32_t ++nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo, ++ struct nouveau_channel *chan, struct ttm_mem_reg *mem) ++{ ++ if (mem->mem_type == TTM_PL_TT) ++ return chan->gart_handle; ++ return chan->vram_handle; ++} ++ + static int + nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, + struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) +@@ -734,15 +701,43 @@ + { + struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); + struct nouveau_bo *nvbo = nouveau_bo(bo); ++ struct ttm_mem_reg *old_mem = &bo->mem; + struct nouveau_channel *chan; + int ret; + + chan = nvbo->channel; +- if (!chan || nvbo->no_vm) { ++ if (!chan) { + chan = dev_priv->channel; + mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX); + } + ++ /* create temporary vma for old memory, this will get cleaned ++ * up after ttm destroys the ttm_mem_reg ++ */ ++ if (dev_priv->card_type >= NV_50) { ++ struct nouveau_mem *node = old_mem->mm_node; ++ if (!node->tmp_vma.node) { ++ u32 page_shift = nvbo->vma.node->type; ++ if (old_mem->mem_type == TTM_PL_TT) ++ page_shift = nvbo->vma.vm->spg_shift; ++ ++ ret = nouveau_vm_get(chan->vm, ++ old_mem->num_pages << PAGE_SHIFT, ++ page_shift, NV_MEM_ACCESS_RO, ++ &node->tmp_vma); ++ if (ret) ++ goto out; ++ } ++ ++ if (old_mem->mem_type == TTM_PL_VRAM) ++ nouveau_vm_map(&node->tmp_vma, node); ++ else { ++ nouveau_vm_map_sg(&node->tmp_vma, 0, ++ old_mem->num_pages << PAGE_SHIFT, ++ node, node->pages); ++ } ++ } ++ + if (dev_priv->card_type < NV_50) + ret = nv04_bo_move_m2mf(chan, bo, &bo->mem, new_mem); + else +@@ -756,6 +751,7 @@ + no_wait_gpu, new_mem); + } + ++out: + if (chan == dev_priv->channel) + mutex_unlock(&chan->mutex); + return ret; +@@ -766,6 +762,7 @@ + bool no_wait_reserve, bool no_wait_gpu, + struct ttm_mem_reg *new_mem) + { ++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); + u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; + struct ttm_placement placement; + struct ttm_mem_reg tmp_mem; +@@ -785,7 +782,23 @@ + if (ret) + goto out; + ++ if (dev_priv->card_type >= NV_50) { ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ struct nouveau_mem *node = tmp_mem.mm_node; ++ struct nouveau_vma *vma = &nvbo->vma; ++ if (vma->node->type != vma->vm->spg_shift) ++ vma = &node->tmp_vma; ++ nouveau_vm_map_sg(vma, 0, tmp_mem.num_pages << PAGE_SHIFT, ++ node, node->pages); ++ } ++ + ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait_reserve, no_wait_gpu, &tmp_mem); ++ ++ if (dev_priv->card_type >= NV_50) { ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ nouveau_vm_unmap(&nvbo->vma); ++ } ++ + if (ret) + goto out; + +@@ -828,6 +841,36 @@ + return ret; + } + ++static void ++nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) ++{ ++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); ++ struct nouveau_mem *node = new_mem->mm_node; ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ struct nouveau_vma *vma = &nvbo->vma; ++ struct nouveau_vm *vm = vma->vm; ++ ++ if (dev_priv->card_type < NV_50) ++ return; ++ ++ switch (new_mem->mem_type) { ++ case TTM_PL_VRAM: ++ nouveau_vm_map(vma, node); ++ break; ++ case TTM_PL_TT: ++ if (vma->node->type != vm->spg_shift) { ++ nouveau_vm_unmap(vma); ++ vma = &node->tmp_vma; ++ } ++ nouveau_vm_map_sg(vma, 0, new_mem->num_pages << PAGE_SHIFT, ++ node, node->pages); ++ break; ++ default: ++ nouveau_vm_unmap(&nvbo->vma); ++ break; ++ } ++} ++ + static int + nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem, + struct nouveau_tile_reg **new_tile) +@@ -835,19 +878,13 @@ + struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); + struct drm_device *dev = dev_priv->dev; + struct nouveau_bo *nvbo = nouveau_bo(bo); +- uint64_t offset; ++ u64 offset = new_mem->start << PAGE_SHIFT; + +- if (nvbo->no_vm || new_mem->mem_type != TTM_PL_VRAM) { +- /* Nothing to do. */ +- *new_tile = NULL; ++ *new_tile = NULL; ++ if (new_mem->mem_type != TTM_PL_VRAM) + return 0; +- } + +- offset = new_mem->start << PAGE_SHIFT; +- +- if (dev_priv->chan_vm) { +- nouveau_vm_map(&nvbo->vma, new_mem->mm_node); +- } else if (dev_priv->card_type >= NV_10) { ++ if (dev_priv->card_type >= NV_10) { + *new_tile = nv10_mem_set_tiling(dev, offset, new_mem->size, + nvbo->tile_mode, + nvbo->tile_flags); +@@ -864,11 +901,8 @@ + struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); + struct drm_device *dev = dev_priv->dev; + +- if (dev_priv->card_type >= NV_10 && +- dev_priv->card_type < NV_50) { +- nv10_mem_put_tile_region(dev, *old_tile, bo->sync_obj); +- *old_tile = new_tile; +- } ++ nv10_mem_put_tile_region(dev, *old_tile, bo->sync_obj); ++ *old_tile = new_tile; + } + + static int +@@ -882,9 +916,11 @@ + struct nouveau_tile_reg *new_tile = NULL; + int ret = 0; + +- ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile); +- if (ret) +- return ret; ++ if (dev_priv->card_type < NV_50) { ++ ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile); ++ if (ret) ++ return ret; ++ } + + /* Fake bo copy. */ + if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) { +@@ -915,10 +951,12 @@ + ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); + + out: +- if (ret) +- nouveau_bo_vm_cleanup(bo, NULL, &new_tile); +- else +- nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile); ++ if (dev_priv->card_type < NV_50) { ++ if (ret) ++ nouveau_bo_vm_cleanup(bo, NULL, &new_tile); ++ else ++ nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile); ++ } + + return ret; + } +@@ -959,7 +997,7 @@ + break; + case TTM_PL_VRAM: + { +- struct nouveau_vram *vram = mem->mm_node; ++ struct nouveau_mem *node = mem->mm_node; + u8 page_shift; + + if (!dev_priv->bar1_vm) { +@@ -970,23 +1008,23 @@ + } + + if (dev_priv->card_type == NV_C0) +- page_shift = vram->page_shift; ++ page_shift = node->page_shift; + else + page_shift = 12; + + ret = nouveau_vm_get(dev_priv->bar1_vm, mem->bus.size, + page_shift, NV_MEM_ACCESS_RW, +- &vram->bar_vma); ++ &node->bar_vma); + if (ret) + return ret; + +- nouveau_vm_map(&vram->bar_vma, vram); ++ nouveau_vm_map(&node->bar_vma, node); + if (ret) { +- nouveau_vm_put(&vram->bar_vma); ++ nouveau_vm_put(&node->bar_vma); + return ret; + } + +- mem->bus.offset = vram->bar_vma.offset; ++ mem->bus.offset = node->bar_vma.offset; + if (dev_priv->card_type == NV_50) /*XXX*/ + mem->bus.offset -= 0x0020000000ULL; + mem->bus.base = pci_resource_start(dev->pdev, 1); +@@ -1003,16 +1041,16 @@ + nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) + { + struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev); +- struct nouveau_vram *vram = mem->mm_node; ++ struct nouveau_mem *node = mem->mm_node; + + if (!dev_priv->bar1_vm || mem->mem_type != TTM_PL_VRAM) + return; + +- if (!vram->bar_vma.node) ++ if (!node->bar_vma.node) + return; + +- nouveau_vm_unmap(&vram->bar_vma); +- nouveau_vm_put(&vram->bar_vma); ++ nouveau_vm_unmap(&node->bar_vma); ++ nouveau_vm_put(&node->bar_vma); + } + + static int +@@ -1062,6 +1100,7 @@ + .invalidate_caches = nouveau_bo_invalidate_caches, + .init_mem_type = nouveau_bo_init_mem_type, + .evict_flags = nouveau_bo_evict_flags, ++ .move_notify = nouveau_bo_move_ntfy, + .move = nouveau_bo_move, + .verify_access = nouveau_bo_verify_access, + .sync_obj_signaled = __nouveau_fence_signalled, +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_channel.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_channel.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_channel.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_channel.c 2011-03-09 18:30:58.000000000 +0100 +@@ -35,7 +35,7 @@ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_bo *pb = chan->pushbuf_bo; + struct nouveau_gpuobj *pushbuf = NULL; +- int ret; ++ int ret = 0; + + if (dev_priv->card_type >= NV_50) { + if (dev_priv->card_type < NV_C0) { +@@ -90,8 +90,7 @@ + else + location = TTM_PL_FLAG_TT; + +- ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false, +- true, &pushbuf); ++ ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, &pushbuf); + if (ret) { + NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret); + return NULL; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_display.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_display.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_display.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_display.c 2011-03-09 18:30:58.000000000 +0100 +@@ -32,6 +32,7 @@ + #include "nouveau_hw.h" + #include "nouveau_crtc.h" + #include "nouveau_dma.h" ++#include "nv50_display.h" + + static void + nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) +@@ -61,18 +62,59 @@ + }; + + int +-nouveau_framebuffer_init(struct drm_device *dev, struct nouveau_framebuffer *nouveau_fb, +- struct drm_mode_fb_cmd *mode_cmd, struct nouveau_bo *nvbo) ++nouveau_framebuffer_init(struct drm_device *dev, ++ struct nouveau_framebuffer *nv_fb, ++ struct drm_mode_fb_cmd *mode_cmd, ++ struct nouveau_bo *nvbo) + { ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct drm_framebuffer *fb = &nv_fb->base; + int ret; + +- ret = drm_framebuffer_init(dev, &nouveau_fb->base, &nouveau_framebuffer_funcs); ++ ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); + if (ret) { + return ret; + } + +- drm_helper_mode_fill_fb_struct(&nouveau_fb->base, mode_cmd); +- nouveau_fb->nvbo = nvbo; ++ drm_helper_mode_fill_fb_struct(fb, mode_cmd); ++ nv_fb->nvbo = nvbo; ++ ++ if (dev_priv->card_type >= NV_50) { ++ u32 tile_flags = nouveau_bo_tile_layout(nvbo); ++ if (tile_flags == 0x7a00 || ++ tile_flags == 0xfe00) ++ nv_fb->r_dma = NvEvoFB32; ++ else ++ if (tile_flags == 0x7000) ++ nv_fb->r_dma = NvEvoFB16; ++ else ++ nv_fb->r_dma = NvEvoVRAM_LP; ++ ++ switch (fb->depth) { ++ case 8: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_8; break; ++ case 15: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_15; break; ++ case 16: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_16; break; ++ case 24: ++ case 32: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_24; break; ++ case 30: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_30; break; ++ default: ++ NV_ERROR(dev, "unknown depth %d\n", fb->depth); ++ return -EINVAL; ++ } ++ ++ if (dev_priv->chipset == 0x50) ++ nv_fb->r_format |= (tile_flags << 8); ++ ++ if (!tile_flags) ++ nv_fb->r_pitch = 0x00100000 | fb->pitch; ++ else { ++ u32 mode = nvbo->tile_mode; ++ if (dev_priv->card_type >= NV_C0) ++ mode >>= 4; ++ nv_fb->r_pitch = ((fb->pitch / 4) << 4) | mode; ++ } ++ } ++ + return 0; + } + +@@ -182,6 +224,7 @@ + struct nouveau_page_flip_state *s, + struct nouveau_fence **pfence) + { ++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private; + struct drm_device *dev = chan->dev; + unsigned long flags; + int ret; +@@ -201,9 +244,12 @@ + if (ret) + goto fail; + +- BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); +- OUT_RING(chan, 0); +- FIRE_RING(chan); ++ if (dev_priv->card_type < NV_C0) ++ BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); ++ else ++ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0500, 1); ++ OUT_RING (chan, 0); ++ FIRE_RING (chan); + + ret = nouveau_fence_new(chan, pfence, true); + if (ret) +@@ -244,7 +290,7 @@ + + /* Initialize a page flip struct */ + *s = (struct nouveau_page_flip_state) +- { { }, s->event, nouveau_crtc(crtc)->index, ++ { { }, event, nouveau_crtc(crtc)->index, + fb->bits_per_pixel, fb->pitch, crtc->x, crtc->y, + new_bo->bo.offset }; + +@@ -255,6 +301,14 @@ + mutex_lock(&chan->mutex); + + /* Emit a page flip */ ++ if (dev_priv->card_type >= NV_50) { ++ ret = nv50_display_flip_next(crtc, fb, chan); ++ if (ret) { ++ nouveau_channel_put(&chan); ++ goto fail_unreserve; ++ } ++ } ++ + ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); + nouveau_channel_put(&chan); + if (ret) +@@ -305,7 +359,8 @@ + } + + list_del(&s->head); +- *ps = *s; ++ if (ps) ++ *ps = *s; + kfree(s); + + spin_unlock_irqrestore(&dev->event_lock, flags); +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_dma.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_dma.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_dma.c 2011-03-12 22:37:55.420744532 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_dma.c 2011-03-09 18:30:58.000000000 +0100 +@@ -97,13 +97,15 @@ + OUT_RING(chan, 0); + + /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */ +- ret = RING_SPACE(chan, 4); ++ ret = RING_SPACE(chan, 6); + if (ret) + return ret; + BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1); +- OUT_RING(chan, NvM2MF); +- BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1); +- OUT_RING(chan, NvNotify0); ++ OUT_RING (chan, NvM2MF); ++ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 3); ++ OUT_RING (chan, NvNotify0); ++ OUT_RING (chan, chan->vram_handle); ++ OUT_RING (chan, chan->gart_handle); + + /* Sit back and pray the channel works.. */ + FIRE_RING(chan); +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_dma.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_dma.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_dma.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_dma.h 2011-03-09 18:30:58.000000000 +0100 +@@ -61,8 +61,6 @@ + NvM2MF = 0x80000001, + NvDmaFB = 0x80000002, + NvDmaTT = 0x80000003, +- NvDmaVRAM = 0x80000004, +- NvDmaGART = 0x80000005, + NvNotify0 = 0x80000006, + Nv2D = 0x80000007, + NvCtxSurf2D = 0x80000008, +@@ -73,12 +71,15 @@ + NvImageBlit = 0x8000000d, + NvSw = 0x8000000e, + NvSema = 0x8000000f, ++ NvEvoSema0 = 0x80000010, ++ NvEvoSema1 = 0x80000011, + + /* G80+ display objects */ + NvEvoVRAM = 0x01000000, + NvEvoFB16 = 0x01000001, + NvEvoFB32 = 0x01000002, +- NvEvoVRAM_LP = 0x01000003 ++ NvEvoVRAM_LP = 0x01000003, ++ NvEvoSync = 0xcafe0000 + }; + + #define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039 +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_dp.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_dp.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_dp.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_dp.c 2011-03-09 18:30:58.000000000 +0100 +@@ -175,7 +175,6 @@ + { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; +- struct bit_displayport_encoder_table_entry *dpse; + struct bit_displayport_encoder_table *dpe; + int ret, i, dpe_headerlen, vs = 0, pre = 0; + uint8_t request[2]; +@@ -183,7 +182,6 @@ + dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); + if (!dpe) + return false; +- dpse = (void *)((char *)dpe + dpe_headerlen); + + ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); + if (ret) +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_drv.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_drv.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_drv.h 2011-03-12 22:37:55.423744472 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_drv.h 2011-03-09 18:30:58.000000000 +0100 +@@ -57,7 +57,7 @@ + #include "nouveau_util.h" + + struct nouveau_grctx; +-struct nouveau_vram; ++struct nouveau_mem; + #include "nouveau_vm.h" + + #define MAX_NUM_DCB_ENTRIES 16 +@@ -65,13 +65,16 @@ + #define NOUVEAU_MAX_CHANNEL_NR 128 + #define NOUVEAU_MAX_TILE_NR 15 + +-struct nouveau_vram { ++struct nouveau_mem { + struct drm_device *dev; + + struct nouveau_vma bar_vma; ++ struct nouveau_vma tmp_vma; + u8 page_shift; + ++ struct drm_mm_node *tag; + struct list_head regions; ++ dma_addr_t *pages; + u32 memtype; + u64 offset; + u64 size; +@@ -90,6 +93,7 @@ + struct nouveau_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; ++ u32 valid_domains; + u32 placements[3]; + u32 busy_placements[3]; + struct ttm_bo_kmap_obj kmap; +@@ -104,8 +108,6 @@ + struct nouveau_channel *channel; + + struct nouveau_vma vma; +- bool mappable; +- bool no_vm; + + uint32_t tile_mode; + uint32_t tile_flags; +@@ -387,6 +389,7 @@ + }; + + struct nouveau_display_engine { ++ void *priv; + int (*early_init)(struct drm_device *); + void (*late_takedown)(struct drm_device *); + int (*create)(struct drm_device *); +@@ -509,8 +512,8 @@ + struct nouveau_vram_engine { + int (*init)(struct drm_device *); + int (*get)(struct drm_device *, u64, u32 align, u32 size_nc, +- u32 type, struct nouveau_vram **); +- void (*put)(struct drm_device *, struct nouveau_vram **); ++ u32 type, struct nouveau_mem **); ++ void (*put)(struct drm_device *, struct nouveau_mem **); + + bool (*flags_valid)(struct drm_device *, u32 tile_flags); + }; +@@ -652,8 +655,6 @@ + /* interrupt handling */ + void (*irq_handler[32])(struct drm_device *); + bool msi_enabled; +- struct workqueue_struct *wq; +- struct work_struct irq_work; + + struct list_head vbl_waiting; + +@@ -691,15 +692,22 @@ + struct { + enum { + NOUVEAU_GART_NONE = 0, +- NOUVEAU_GART_AGP, +- NOUVEAU_GART_SGDMA ++ NOUVEAU_GART_AGP, /* AGP */ ++ NOUVEAU_GART_PDMA, /* paged dma object */ ++ NOUVEAU_GART_HW /* on-chip gart/vm */ + } type; + uint64_t aper_base; + uint64_t aper_size; + uint64_t aper_free; + ++ struct ttm_backend_func *func; ++ ++ struct { ++ struct page *page; ++ dma_addr_t addr; ++ } dummy; ++ + struct nouveau_gpuobj *sg_ctxdma; +- struct nouveau_vma vma; + } gart_info; + + /* nv10-nv40 tiling regions */ +@@ -740,14 +748,6 @@ + + struct backlight_device *backlight; + +- struct nouveau_channel *evo; +- u32 evo_alloc; +- struct { +- struct dcb_entry *dcb; +- u16 script; +- u32 pclk; +- } evo_irq; +- + struct { + struct dentry *channel_root; + } debugfs; +@@ -847,6 +847,7 @@ + struct nouveau_tile_reg *tile, + struct nouveau_fence *fence); + extern const struct ttm_mem_type_manager_func nouveau_vram_manager; ++extern const struct ttm_mem_type_manager_func nouveau_gart_manager; + + /* nouveau_notifier.c */ + extern int nouveau_notifier_init_channel(struct nouveau_channel *); +@@ -1076,7 +1077,7 @@ + /* nv50_fb.c */ + extern int nv50_fb_init(struct drm_device *); + extern void nv50_fb_takedown(struct drm_device *); +-extern void nv50_fb_vm_trap(struct drm_device *, int display, const char *); ++extern void nv50_fb_vm_trap(struct drm_device *, int display); + + /* nvc0_fb.c */ + extern int nvc0_fb_init(struct drm_device *); +@@ -1295,7 +1296,7 @@ + extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *, + int size, int align, uint32_t flags, + uint32_t tile_mode, uint32_t tile_flags, +- bool no_vm, bool mappable, struct nouveau_bo **); ++ struct nouveau_bo **); + extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags); + extern int nouveau_bo_unpin(struct nouveau_bo *); + extern int nouveau_bo_map(struct nouveau_bo *); +@@ -1356,9 +1357,9 @@ + + /* nouveau_gem.c */ + extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *, +- int size, int align, uint32_t flags, ++ int size, int align, uint32_t domain, + uint32_t tile_mode, uint32_t tile_flags, +- bool no_vm, bool mappable, struct nouveau_bo **); ++ struct nouveau_bo **); + extern int nouveau_gem_object_new(struct drm_gem_object *); + extern void nouveau_gem_object_del(struct drm_gem_object *); + extern int nouveau_gem_ioctl_new(struct drm_device *, void *, +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_fbcon.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_fbcon.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_fbcon.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_fbcon.c 2011-03-09 18:30:58.000000000 +0100 +@@ -296,8 +296,8 @@ + size = mode_cmd.pitch * mode_cmd.height; + size = roundup(size, PAGE_SIZE); + +- ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, TTM_PL_FLAG_VRAM, +- 0, 0x0000, false, true, &nvbo); ++ ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, ++ NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, &nvbo); + if (ret) { + NV_ERROR(dev, "failed to allocate framebuffer\n"); + goto out; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_fb.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_fb.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_fb.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_fb.h 2011-03-09 18:30:58.000000000 +0100 +@@ -30,6 +30,9 @@ + struct nouveau_framebuffer { + struct drm_framebuffer base; + struct nouveau_bo *nvbo; ++ u32 r_dma; ++ u32 r_format; ++ u32 r_pitch; + }; + + static inline struct nouveau_framebuffer * +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_fence.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_fence.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_fence.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_fence.c 2011-03-09 18:30:58.000000000 +0100 +@@ -27,13 +27,15 @@ + #include "drmP.h" + #include "drm.h" + ++#include ++#include ++ + #include "nouveau_drv.h" + #include "nouveau_ramht.h" + #include "nouveau_dma.h" + + #define USE_REFCNT(dev) (nouveau_private(dev)->chipset >= 0x10) +-#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17 && \ +- nouveau_private(dev)->card_type < NV_C0) ++#define USE_SEMA(dev) (nouveau_private(dev)->chipset >= 0x17) + + struct nouveau_fence { + struct nouveau_channel *channel; +@@ -230,7 +232,8 @@ + __nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) + { + unsigned long timeout = jiffies + (3 * DRM_HZ); +- unsigned long sleep_time = jiffies + 1; ++ unsigned long sleep_time = NSEC_PER_MSEC / 1000; ++ ktime_t t; + int ret = 0; + + while (1) { +@@ -244,8 +247,13 @@ + + __set_current_state(intr ? TASK_INTERRUPTIBLE + : TASK_UNINTERRUPTIBLE); +- if (lazy && time_after_eq(jiffies, sleep_time)) +- schedule_timeout(1); ++ if (lazy) { ++ t = ktime_set(0, sleep_time); ++ schedule_hrtimeout(&t, HRTIMER_MODE_REL); ++ sleep_time *= 2; ++ if (sleep_time > NSEC_PER_MSEC) ++ sleep_time = NSEC_PER_MSEC; ++ } + + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; +@@ -259,11 +267,12 @@ + } + + static struct nouveau_semaphore * +-alloc_semaphore(struct drm_device *dev) ++semaphore_alloc(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_semaphore *sema; +- int ret; ++ int size = (dev_priv->chipset < 0x84) ? 4 : 16; ++ int ret, i; + + if (!USE_SEMA(dev)) + return NULL; +@@ -277,9 +286,9 @@ + goto fail; + + spin_lock(&dev_priv->fence.lock); +- sema->mem = drm_mm_search_free(&dev_priv->fence.heap, 4, 0, 0); ++ sema->mem = drm_mm_search_free(&dev_priv->fence.heap, size, 0, 0); + if (sema->mem) +- sema->mem = drm_mm_get_block_atomic(sema->mem, 4, 0); ++ sema->mem = drm_mm_get_block_atomic(sema->mem, size, 0); + spin_unlock(&dev_priv->fence.lock); + + if (!sema->mem) +@@ -287,7 +296,8 @@ + + kref_init(&sema->ref); + sema->dev = dev; +- nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 0); ++ for (i = sema->mem->start; i < sema->mem->start + size; i += 4) ++ nouveau_bo_wr32(dev_priv->fence.bo, i / 4, 0); + + return sema; + fail: +@@ -296,7 +306,7 @@ + } + + static void +-free_semaphore(struct kref *ref) ++semaphore_free(struct kref *ref) + { + struct nouveau_semaphore *sema = + container_of(ref, struct nouveau_semaphore, ref); +@@ -318,61 +328,107 @@ + if (unlikely(!signalled)) + nouveau_bo_wr32(dev_priv->fence.bo, sema->mem->start / 4, 1); + +- kref_put(&sema->ref, free_semaphore); ++ kref_put(&sema->ref, semaphore_free); + } + + static int +-emit_semaphore(struct nouveau_channel *chan, int method, +- struct nouveau_semaphore *sema) ++semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema) + { +- struct drm_nouveau_private *dev_priv = sema->dev->dev_private; +- struct nouveau_fence *fence; +- bool smart = (dev_priv->card_type >= NV_50); ++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private; ++ struct nouveau_fence *fence = NULL; + int ret; + +- ret = RING_SPACE(chan, smart ? 8 : 4); ++ if (dev_priv->chipset < 0x84) { ++ ret = RING_SPACE(chan, 3); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_OFFSET, 2); ++ OUT_RING (chan, sema->mem->start); ++ OUT_RING (chan, 1); ++ } else ++ if (dev_priv->chipset < 0xc0) { ++ struct nouveau_vma *vma = &dev_priv->fence.bo->vma; ++ u64 offset = vma->offset + sema->mem->start; ++ ++ ret = RING_SPACE(chan, 5); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubSw, 0x0010, 4); ++ OUT_RING (chan, upper_32_bits(offset)); ++ OUT_RING (chan, lower_32_bits(offset)); ++ OUT_RING (chan, 1); ++ OUT_RING (chan, 1); /* ACQUIRE_EQ */ ++ } else { ++ struct nouveau_vma *vma = &dev_priv->fence.bo->vma; ++ u64 offset = vma->offset + sema->mem->start; ++ ++ ret = RING_SPACE(chan, 5); ++ if (ret) ++ return ret; ++ ++ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); ++ OUT_RING (chan, upper_32_bits(offset)); ++ OUT_RING (chan, lower_32_bits(offset)); ++ OUT_RING (chan, 1); ++ OUT_RING (chan, 0x1001); /* ACQUIRE_EQ */ ++ } ++ ++ /* Delay semaphore destruction until its work is done */ ++ ret = nouveau_fence_new(chan, &fence, true); + if (ret) + return ret; + +- if (smart) { +- BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1); +- OUT_RING(chan, NvSema); +- } +- BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_OFFSET, 1); +- OUT_RING(chan, sema->mem->start); ++ kref_get(&sema->ref); ++ nouveau_fence_work(fence, semaphore_work, sema); ++ nouveau_fence_unref(&fence); ++ return 0; ++} + +- if (smart && method == NV_SW_SEMAPHORE_ACQUIRE) { +- /* +- * NV50 tries to be too smart and context-switch +- * between semaphores instead of doing a "first come, +- * first served" strategy like previous cards +- * do. +- * +- * That's bad because the ACQUIRE latency can get as +- * large as the PFIFO context time slice in the +- * typical DRI2 case where you have several +- * outstanding semaphores at the same moment. +- * +- * If we're going to ACQUIRE, force the card to +- * context switch before, just in case the matching +- * RELEASE is already scheduled to be executed in +- * another channel. +- */ +- BEGIN_RING(chan, NvSubSw, NV_SW_YIELD, 1); +- OUT_RING(chan, 0); +- } +- +- BEGIN_RING(chan, NvSubSw, method, 1); +- OUT_RING(chan, 1); +- +- if (smart && method == NV_SW_SEMAPHORE_RELEASE) { +- /* +- * Force the card to context switch, there may be +- * another channel waiting for the semaphore we just +- * released. +- */ +- BEGIN_RING(chan, NvSubSw, NV_SW_YIELD, 1); +- OUT_RING(chan, 0); ++static int ++semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema) ++{ ++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private; ++ struct nouveau_fence *fence = NULL; ++ int ret; ++ ++ if (dev_priv->chipset < 0x84) { ++ ret = RING_SPACE(chan, 4); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_OFFSET, 1); ++ OUT_RING (chan, sema->mem->start); ++ BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1); ++ OUT_RING (chan, 1); ++ } else ++ if (dev_priv->chipset < 0xc0) { ++ struct nouveau_vma *vma = &dev_priv->fence.bo->vma; ++ u64 offset = vma->offset + sema->mem->start; ++ ++ ret = RING_SPACE(chan, 5); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubSw, 0x0010, 4); ++ OUT_RING (chan, upper_32_bits(offset)); ++ OUT_RING (chan, lower_32_bits(offset)); ++ OUT_RING (chan, 1); ++ OUT_RING (chan, 2); /* RELEASE */ ++ } else { ++ struct nouveau_vma *vma = &dev_priv->fence.bo->vma; ++ u64 offset = vma->offset + sema->mem->start; ++ ++ ret = RING_SPACE(chan, 5); ++ if (ret) ++ return ret; ++ ++ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); ++ OUT_RING (chan, upper_32_bits(offset)); ++ OUT_RING (chan, lower_32_bits(offset)); ++ OUT_RING (chan, 1); ++ OUT_RING (chan, 0x1002); /* RELEASE */ + } + + /* Delay semaphore destruction until its work is done */ +@@ -383,7 +439,6 @@ + kref_get(&sema->ref); + nouveau_fence_work(fence, semaphore_work, sema); + nouveau_fence_unref(&fence); +- + return 0; + } + +@@ -400,7 +455,7 @@ + nouveau_fence_signalled(fence))) + goto out; + +- sema = alloc_semaphore(dev); ++ sema = semaphore_alloc(dev); + if (!sema) { + /* Early card or broken userspace, fall back to + * software sync. */ +@@ -418,17 +473,17 @@ + } + + /* Make wchan wait until it gets signalled */ +- ret = emit_semaphore(wchan, NV_SW_SEMAPHORE_ACQUIRE, sema); ++ ret = semaphore_acquire(wchan, sema); + if (ret) + goto out_unlock; + + /* Signal the semaphore from chan */ +- ret = emit_semaphore(chan, NV_SW_SEMAPHORE_RELEASE, sema); ++ ret = semaphore_release(chan, sema); + + out_unlock: + mutex_unlock(&chan->mutex); + out_unref: +- kref_put(&sema->ref, free_semaphore); ++ kref_put(&sema->ref, semaphore_free); + out: + if (chan) + nouveau_channel_put_unlocked(&chan); +@@ -449,22 +504,23 @@ + struct nouveau_gpuobj *obj = NULL; + int ret; + ++ if (dev_priv->card_type >= NV_C0) ++ goto out_initialised; ++ + /* Create an NV_SW object for various sync purposes */ + ret = nouveau_gpuobj_gr_new(chan, NvSw, NV_SW); + if (ret) + return ret; + + /* we leave subchannel empty for nvc0 */ +- if (dev_priv->card_type < NV_C0) { +- ret = RING_SPACE(chan, 2); +- if (ret) +- return ret; +- BEGIN_RING(chan, NvSubSw, 0, 1); +- OUT_RING(chan, NvSw); +- } ++ ret = RING_SPACE(chan, 2); ++ if (ret) ++ return ret; ++ BEGIN_RING(chan, NvSubSw, 0, 1); ++ OUT_RING(chan, NvSw); + + /* Create a DMA object for the shared cross-channel sync area. */ +- if (USE_SEMA(dev)) { ++ if (USE_SEMA(dev) && dev_priv->chipset < 0x84) { + struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem; + + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, +@@ -484,14 +540,20 @@ + return ret; + BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1); + OUT_RING(chan, NvSema); ++ } else { ++ ret = RING_SPACE(chan, 2); ++ if (ret) ++ return ret; ++ BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1); ++ OUT_RING (chan, chan->vram_handle); /* whole VM */ + } + + FIRE_RING(chan); + ++out_initialised: + INIT_LIST_HEAD(&chan->fence.pending); + spin_lock_init(&chan->fence.lock); + atomic_set(&chan->fence.last_sequence_irq, 0); +- + return 0; + } + +@@ -519,12 +581,13 @@ + nouveau_fence_init(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ int size = (dev_priv->chipset < 0x84) ? 4096 : 16384; + int ret; + + /* Create a shared VRAM heap for cross-channel sync. */ + if (USE_SEMA(dev)) { +- ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, +- 0, 0, false, true, &dev_priv->fence.bo); ++ ret = nouveau_bo_new(dev, NULL, size, 0, TTM_PL_FLAG_VRAM, ++ 0, 0, &dev_priv->fence.bo); + if (ret) + return ret; + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_gem.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_gem.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_gem.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_gem.c 2011-03-09 18:30:58.000000000 +0100 +@@ -61,19 +61,36 @@ + + int + nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan, +- int size, int align, uint32_t flags, uint32_t tile_mode, +- uint32_t tile_flags, bool no_vm, bool mappable, +- struct nouveau_bo **pnvbo) ++ int size, int align, uint32_t domain, uint32_t tile_mode, ++ uint32_t tile_flags, struct nouveau_bo **pnvbo) + { ++ struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_bo *nvbo; ++ u32 flags = 0; + int ret; + ++ if (domain & NOUVEAU_GEM_DOMAIN_VRAM) ++ flags |= TTM_PL_FLAG_VRAM; ++ if (domain & NOUVEAU_GEM_DOMAIN_GART) ++ flags |= TTM_PL_FLAG_TT; ++ if (!flags || domain & NOUVEAU_GEM_DOMAIN_CPU) ++ flags |= TTM_PL_FLAG_SYSTEM; ++ + ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode, +- tile_flags, no_vm, mappable, pnvbo); ++ tile_flags, pnvbo); + if (ret) + return ret; + nvbo = *pnvbo; + ++ /* we restrict allowed domains on nv50+ to only the types ++ * that were requested at creation time. not possibly on ++ * earlier chips without busting the ABI. ++ */ ++ nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_VRAM | ++ NOUVEAU_GEM_DOMAIN_GART; ++ if (dev_priv->card_type >= NV_50) ++ nvbo->valid_domains &= domain; ++ + nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); + if (!nvbo->gem) { + nouveau_bo_ref(NULL, pnvbo); +@@ -97,7 +114,7 @@ + + rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT; + rep->offset = nvbo->bo.offset; +- rep->map_handle = nvbo->mappable ? nvbo->bo.addr_space_offset : 0; ++ rep->map_handle = nvbo->bo.addr_space_offset; + rep->tile_mode = nvbo->tile_mode; + rep->tile_flags = nvbo->tile_flags; + return 0; +@@ -111,19 +128,11 @@ + struct drm_nouveau_gem_new *req = data; + struct nouveau_bo *nvbo = NULL; + struct nouveau_channel *chan = NULL; +- uint32_t flags = 0; + int ret = 0; + + if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL)) + dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping; + +- if (req->info.domain & NOUVEAU_GEM_DOMAIN_VRAM) +- flags |= TTM_PL_FLAG_VRAM; +- if (req->info.domain & NOUVEAU_GEM_DOMAIN_GART) +- flags |= TTM_PL_FLAG_TT; +- if (!flags || req->info.domain & NOUVEAU_GEM_DOMAIN_CPU) +- flags |= TTM_PL_FLAG_SYSTEM; +- + if (!dev_priv->engine.vram.flags_valid(dev, req->info.tile_flags)) { + NV_ERROR(dev, "bad page flags: 0x%08x\n", req->info.tile_flags); + return -EINVAL; +@@ -135,10 +144,9 @@ + return PTR_ERR(chan); + } + +- ret = nouveau_gem_new(dev, chan, req->info.size, req->align, flags, +- req->info.tile_mode, req->info.tile_flags, false, +- (req->info.domain & NOUVEAU_GEM_DOMAIN_MAPPABLE), +- &nvbo); ++ ret = nouveau_gem_new(dev, chan, req->info.size, req->align, ++ req->info.domain, req->info.tile_mode, ++ req->info.tile_flags, &nvbo); + if (chan) + nouveau_channel_put(&chan); + if (ret) +@@ -161,7 +169,7 @@ + { + struct nouveau_bo *nvbo = gem->driver_private; + struct ttm_buffer_object *bo = &nvbo->bo; +- uint32_t domains = valid_domains & ++ uint32_t domains = valid_domains & nvbo->valid_domains & + (write_domains ? write_domains : read_domains); + uint32_t pref_flags = 0, valid_flags = 0; + +@@ -592,7 +600,7 @@ + if (push[i].bo_index >= req->nr_buffers) { + NV_ERROR(dev, "push %d buffer not in list\n", i); + ret = -EINVAL; +- goto out; ++ goto out_prevalid; + } + + bo[push[i].bo_index].read_domains |= (1 << 31); +@@ -604,7 +612,7 @@ + if (ret) { + if (ret != -ERESTARTSYS) + NV_ERROR(dev, "validate: %d\n", ret); +- goto out; ++ goto out_prevalid; + } + + /* Apply any relocations that are required */ +@@ -697,6 +705,8 @@ + out: + validate_fini(&op, fence); + nouveau_fence_unref(&fence); ++ ++out_prevalid: + kfree(bo); + kfree(push); + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_mem.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_mem.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_mem.c 2011-03-12 22:37:55.425744434 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_mem.c 2011-03-09 18:30:58.000000000 +0100 +@@ -152,7 +152,6 @@ + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + +- nouveau_bo_unpin(dev_priv->vga_ram); + nouveau_bo_ref(NULL, &dev_priv->vga_ram); + + ttm_bo_device_release(&dev_priv->ttm.bdev); +@@ -393,11 +392,17 @@ + struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; + int ret, dma_bits; + +- if (dev_priv->card_type >= NV_50 && +- pci_dma_supported(dev->pdev, DMA_BIT_MASK(40))) +- dma_bits = 40; +- else +- dma_bits = 32; ++ dma_bits = 32; ++ if (dev_priv->card_type >= NV_50) { ++ if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(40))) ++ dma_bits = 40; ++ } else ++ if (drm_device_is_pcie(dev) && ++ dev_priv->chipset != 0x40 && ++ dev_priv->chipset != 0x45) { ++ if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(39))) ++ dma_bits = 39; ++ } + + ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits)); + if (ret) +@@ -419,14 +424,32 @@ + } + + /* reserve space at end of VRAM for PRAMIN */ +- if (dev_priv->chipset == 0x40 || dev_priv->chipset == 0x47 || +- dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) +- dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024); +- else +- if (dev_priv->card_type >= NV_40) +- dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024); +- else +- dev_priv->ramin_rsvd_vram = (512 * 1024); ++ if (dev_priv->card_type >= NV_50) { ++ dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024; ++ } else ++ if (dev_priv->card_type >= NV_40) { ++ u32 vs = hweight8((nv_rd32(dev, 0x001540) & 0x0000ff00) >> 8); ++ u32 rsvd; ++ ++ /* estimate grctx size, the magics come from nv40_grctx.c */ ++ if (dev_priv->chipset == 0x40) rsvd = 0x6aa0 * vs; ++ else if (dev_priv->chipset < 0x43) rsvd = 0x4f00 * vs; ++ else if (nv44_graph_class(dev)) rsvd = 0x4980 * vs; ++ else rsvd = 0x4a40 * vs; ++ rsvd += 16 * 1024; ++ rsvd *= dev_priv->engine.fifo.channels; ++ ++ /* pciegart table */ ++ if (drm_device_is_pcie(dev)) ++ rsvd += 512 * 1024; ++ ++ /* object storage */ ++ rsvd += 512 * 1024; ++ ++ dev_priv->ramin_rsvd_vram = round_up(rsvd, 4096); ++ } else { ++ dev_priv->ramin_rsvd_vram = 512 * 1024; ++ } + + ret = dev_priv->engine.vram.init(dev); + if (ret) +@@ -455,13 +478,17 @@ + return ret; + } + +- ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM, +- 0, 0, true, true, &dev_priv->vga_ram); +- if (ret == 0) +- ret = nouveau_bo_pin(dev_priv->vga_ram, TTM_PL_FLAG_VRAM); +- if (ret) { +- NV_WARN(dev, "failed to reserve VGA memory\n"); +- nouveau_bo_ref(NULL, &dev_priv->vga_ram); ++ if (dev_priv->card_type < NV_50) { ++ ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM, ++ 0, 0, &dev_priv->vga_ram); ++ if (ret == 0) ++ ret = nouveau_bo_pin(dev_priv->vga_ram, ++ TTM_PL_FLAG_VRAM); ++ ++ if (ret) { ++ NV_WARN(dev, "failed to reserve VGA memory\n"); ++ nouveau_bo_ref(NULL, &dev_priv->vga_ram); ++ } + } + + dev_priv->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1), +@@ -666,13 +693,14 @@ + { + struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev); + struct nouveau_mm *mm; +- u32 b_size; ++ u64 size, block, rsvd; + int ret; + +- p_size = (p_size << PAGE_SHIFT) >> 12; +- b_size = dev_priv->vram_rblock_size >> 12; ++ rsvd = (256 * 1024); /* vga memory */ ++ size = (p_size << PAGE_SHIFT) - rsvd; ++ block = dev_priv->vram_rblock_size; + +- ret = nouveau_mm_init(&mm, 0, p_size, b_size); ++ ret = nouveau_mm_init(&mm, rsvd >> 12, size >> 12, block >> 12); + if (ret) + return ret; + +@@ -700,9 +728,15 @@ + { + struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev); + struct nouveau_vram_engine *vram = &dev_priv->engine.vram; ++ struct nouveau_mem *node = mem->mm_node; + struct drm_device *dev = dev_priv->dev; + +- vram->put(dev, (struct nouveau_vram **)&mem->mm_node); ++ if (node->tmp_vma.node) { ++ nouveau_vm_unmap(&node->tmp_vma); ++ nouveau_vm_put(&node->tmp_vma); ++ } ++ ++ vram->put(dev, (struct nouveau_mem **)&mem->mm_node); + } + + static int +@@ -715,7 +749,7 @@ + struct nouveau_vram_engine *vram = &dev_priv->engine.vram; + struct drm_device *dev = dev_priv->dev; + struct nouveau_bo *nvbo = nouveau_bo(bo); +- struct nouveau_vram *node; ++ struct nouveau_mem *node; + u32 size_nc = 0; + int ret; + +@@ -724,7 +758,7 @@ + + ret = vram->get(dev, mem->num_pages << PAGE_SHIFT, + mem->page_alignment << PAGE_SHIFT, size_nc, +- (nvbo->tile_flags >> 8) & 0xff, &node); ++ (nvbo->tile_flags >> 8) & 0x3ff, &node); + if (ret) { + mem->mm_node = NULL; + return (ret == -ENOSPC) ? 0 : ret; +@@ -771,3 +805,84 @@ + nouveau_vram_manager_del, + nouveau_vram_manager_debug + }; ++ ++static int ++nouveau_gart_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) ++{ ++ return 0; ++} ++ ++static int ++nouveau_gart_manager_fini(struct ttm_mem_type_manager *man) ++{ ++ return 0; ++} ++ ++static void ++nouveau_gart_manager_del(struct ttm_mem_type_manager *man, ++ struct ttm_mem_reg *mem) ++{ ++ struct nouveau_mem *node = mem->mm_node; ++ ++ if (node->tmp_vma.node) { ++ nouveau_vm_unmap(&node->tmp_vma); ++ nouveau_vm_put(&node->tmp_vma); ++ } ++ mem->mm_node = NULL; ++} ++ ++static int ++nouveau_gart_manager_new(struct ttm_mem_type_manager *man, ++ struct ttm_buffer_object *bo, ++ struct ttm_placement *placement, ++ struct ttm_mem_reg *mem) ++{ ++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ struct nouveau_vma *vma = &nvbo->vma; ++ struct nouveau_vm *vm = vma->vm; ++ struct nouveau_mem *node; ++ int ret; ++ ++ if (unlikely((mem->num_pages << PAGE_SHIFT) >= ++ dev_priv->gart_info.aper_size)) ++ return -ENOMEM; ++ ++ node = kzalloc(sizeof(*node), GFP_KERNEL); ++ if (!node) ++ return -ENOMEM; ++ ++ /* This node must be for evicting large-paged VRAM ++ * to system memory. Due to a nv50 limitation of ++ * not being able to mix large/small pages within ++ * the same PDE, we need to create a temporary ++ * small-paged VMA for the eviction. ++ */ ++ if (vma->node->type != vm->spg_shift) { ++ ret = nouveau_vm_get(vm, (u64)vma->node->length << 12, ++ vm->spg_shift, NV_MEM_ACCESS_RW, ++ &node->tmp_vma); ++ if (ret) { ++ kfree(node); ++ return ret; ++ } ++ } ++ ++ node->page_shift = nvbo->vma.node->type; ++ mem->mm_node = node; ++ mem->start = 0; ++ return 0; ++} ++ ++void ++nouveau_gart_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) ++{ ++} ++ ++const struct ttm_mem_type_manager_func nouveau_gart_manager = { ++ nouveau_gart_manager_init, ++ nouveau_gart_manager_fini, ++ nouveau_gart_manager_new, ++ nouveau_gart_manager_del, ++ nouveau_gart_manager_debug ++}; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_mm.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_mm.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_mm.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_mm.h 2011-03-09 18:30:58.000000000 +0100 +@@ -53,13 +53,13 @@ + + int nv50_vram_init(struct drm_device *); + int nv50_vram_new(struct drm_device *, u64 size, u32 align, u32 size_nc, +- u32 memtype, struct nouveau_vram **); +-void nv50_vram_del(struct drm_device *, struct nouveau_vram **); ++ u32 memtype, struct nouveau_mem **); ++void nv50_vram_del(struct drm_device *, struct nouveau_mem **); + bool nv50_vram_flags_valid(struct drm_device *, u32 tile_flags); + + int nvc0_vram_init(struct drm_device *); + int nvc0_vram_new(struct drm_device *, u64 size, u32 align, u32 ncmin, +- u32 memtype, struct nouveau_vram **); ++ u32 memtype, struct nouveau_mem **); + bool nvc0_vram_flags_valid(struct drm_device *, u32 tile_flags); + + #endif +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_notifier.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_notifier.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_notifier.c 2011-03-12 22:37:55.427744395 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_notifier.c 2011-03-09 18:30:58.000000000 +0100 +@@ -39,12 +39,11 @@ + int ret; + + if (nouveau_vram_notify) +- flags = TTM_PL_FLAG_VRAM; ++ flags = NOUVEAU_GEM_DOMAIN_VRAM; + else +- flags = TTM_PL_FLAG_TT; ++ flags = NOUVEAU_GEM_DOMAIN_GART; + +- ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, +- 0, 0x0000, false, true, &ntfy); ++ ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, 0, 0, &ntfy); + if (ret) + return ret; + +@@ -100,6 +99,7 @@ + uint32_t *b_offset) + { + struct drm_device *dev = chan->dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *nobj = NULL; + struct drm_mm_node *mem; + uint32_t offset; +@@ -114,11 +114,16 @@ + return -ENOMEM; + } + +- if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) +- target = NV_MEM_TARGET_VRAM; +- else +- target = NV_MEM_TARGET_GART; +- offset = chan->notifier_bo->bo.mem.start << PAGE_SHIFT; ++ if (dev_priv->card_type < NV_50) { ++ if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) ++ target = NV_MEM_TARGET_VRAM; ++ else ++ target = NV_MEM_TARGET_GART; ++ offset = chan->notifier_bo->bo.mem.start << PAGE_SHIFT; ++ } else { ++ target = NV_MEM_TARGET_VM; ++ offset = chan->notifier_bo->vma.offset; ++ } + offset += mem->start; + + ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset, +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_object.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_object.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_object.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_object.c 2011-03-09 18:30:58.000000000 +0100 +@@ -36,6 +36,7 @@ + #include "nouveau_drm.h" + #include "nouveau_ramht.h" + #include "nouveau_vm.h" ++#include "nv50_display.h" + + struct nouveau_gpuobj_method { + struct list_head head; +@@ -490,16 +491,22 @@ + } + + if (target == NV_MEM_TARGET_GART) { +- if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { +- target = NV_MEM_TARGET_PCI_NOSNOOP; +- base += dev_priv->gart_info.aper_base; +- } else +- if (base != 0) { +- base = nouveau_sgdma_get_physical(dev, base); ++ struct nouveau_gpuobj *gart = dev_priv->gart_info.sg_ctxdma; ++ ++ if (dev_priv->gart_info.type == NOUVEAU_GART_PDMA) { ++ if (base == 0) { ++ nouveau_gpuobj_ref(gart, pobj); ++ return 0; ++ } ++ ++ base = nouveau_sgdma_get_physical(dev, base); + target = NV_MEM_TARGET_PCI; + } else { +- nouveau_gpuobj_ref(dev_priv->gart_info.sg_ctxdma, pobj); +- return 0; ++ base += dev_priv->gart_info.aper_base; ++ if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) ++ target = NV_MEM_TARGET_PCI_NOSNOOP; ++ else ++ target = NV_MEM_TARGET_PCI; + } + } + +@@ -776,7 +783,7 @@ + struct drm_device *dev = chan->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *vram = NULL, *tt = NULL; +- int ret; ++ int ret, i; + + NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); + +@@ -841,6 +848,25 @@ + nouveau_gpuobj_ref(NULL, &ramht); + if (ret) + return ret; ++ ++ /* dma objects for display sync channel semaphore blocks */ ++ for (i = 0; i < 2; i++) { ++ struct nouveau_gpuobj *sem = NULL; ++ struct nv50_display_crtc *dispc = ++ &nv50_display(dev)->crtc[i]; ++ u64 offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT; ++ ++ ret = nouveau_gpuobj_dma_new(chan, 0x3d, offset, 0xfff, ++ NV_MEM_ACCESS_RW, ++ NV_MEM_TARGET_VRAM, &sem); ++ if (ret) ++ return ret; ++ ++ ret = nouveau_ramht_insert(chan, NvEvoSema0 + i, sem); ++ nouveau_gpuobj_ref(NULL, &sem); ++ if (ret) ++ return ret; ++ } + } + + /* VRAM ctxdma */ +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_ramht.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_ramht.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_ramht.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_ramht.c 2011-03-09 18:30:58.000000000 +0100 +@@ -114,7 +114,9 @@ + (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT); + } else { + if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) { +- ctx = (gpuobj->cinst << 10) | chan->id; ++ ctx = (gpuobj->cinst << 10) | ++ (chan->id << 28) | ++ chan->id; /* HASH_TAG */ + } else { + ctx = (gpuobj->cinst >> 4) | + ((gpuobj->engine << +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_sgdma.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_sgdma.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_sgdma.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_sgdma.c 2011-03-09 18:30:58.000000000 +0100 +@@ -74,8 +74,24 @@ + } + } + ++static void ++nouveau_sgdma_destroy(struct ttm_backend *be) ++{ ++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; ++ ++ if (be) { ++ NV_DEBUG(nvbe->dev, "\n"); ++ ++ if (nvbe) { ++ if (nvbe->pages) ++ be->func->clear(be); ++ kfree(nvbe); ++ } ++ } ++} ++ + static int +-nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) ++nv04_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) + { + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; + struct drm_device *dev = nvbe->dev; +@@ -102,7 +118,7 @@ + } + + static int +-nouveau_sgdma_unbind(struct ttm_backend *be) ++nv04_sgdma_unbind(struct ttm_backend *be) + { + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; + struct drm_device *dev = nvbe->dev; +@@ -125,59 +141,245 @@ + return 0; + } + ++static struct ttm_backend_func nv04_sgdma_backend = { ++ .populate = nouveau_sgdma_populate, ++ .clear = nouveau_sgdma_clear, ++ .bind = nv04_sgdma_bind, ++ .unbind = nv04_sgdma_unbind, ++ .destroy = nouveau_sgdma_destroy ++}; ++ + static void +-nouveau_sgdma_destroy(struct ttm_backend *be) ++nv41_sgdma_flush(struct nouveau_sgdma_be *nvbe) ++{ ++ struct drm_device *dev = nvbe->dev; ++ ++ nv_wr32(dev, 0x100810, 0x00000022); ++ if (!nv_wait(dev, 0x100810, 0x00000100, 0x00000100)) ++ NV_ERROR(dev, "vm flush timeout: 0x%08x\n", ++ nv_rd32(dev, 0x100810)); ++ nv_wr32(dev, 0x100810, 0x00000000); ++} ++ ++static int ++nv41_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) + { + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; ++ struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private; ++ struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma; ++ dma_addr_t *list = nvbe->pages; ++ u32 pte = mem->start << 2; ++ u32 cnt = nvbe->nr_pages; + +- if (be) { +- NV_DEBUG(nvbe->dev, "\n"); ++ nvbe->offset = mem->start << PAGE_SHIFT; + +- if (nvbe) { +- if (nvbe->pages) +- be->func->clear(be); +- kfree(nvbe); ++ while (cnt--) { ++ nv_wo32(pgt, pte, (*list++ >> 7) | 1); ++ pte += 4; ++ } ++ ++ nv41_sgdma_flush(nvbe); ++ nvbe->bound = true; ++ return 0; ++} ++ ++static int ++nv41_sgdma_unbind(struct ttm_backend *be) ++{ ++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; ++ struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private; ++ struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma; ++ u32 pte = (nvbe->offset >> 12) << 2; ++ u32 cnt = nvbe->nr_pages; ++ ++ while (cnt--) { ++ nv_wo32(pgt, pte, 0x00000000); ++ pte += 4; ++ } ++ ++ nv41_sgdma_flush(nvbe); ++ nvbe->bound = false; ++ return 0; ++} ++ ++static struct ttm_backend_func nv41_sgdma_backend = { ++ .populate = nouveau_sgdma_populate, ++ .clear = nouveau_sgdma_clear, ++ .bind = nv41_sgdma_bind, ++ .unbind = nv41_sgdma_unbind, ++ .destroy = nouveau_sgdma_destroy ++}; ++ ++static void ++nv44_sgdma_flush(struct nouveau_sgdma_be *nvbe) ++{ ++ struct drm_device *dev = nvbe->dev; ++ ++ nv_wr32(dev, 0x100814, (nvbe->nr_pages - 1) << 12); ++ nv_wr32(dev, 0x100808, nvbe->offset | 0x20); ++ if (!nv_wait(dev, 0x100808, 0x00000001, 0x00000001)) ++ NV_ERROR(dev, "gart flush timeout: 0x%08x\n", ++ nv_rd32(dev, 0x100808)); ++ nv_wr32(dev, 0x100808, 0x00000000); ++} ++ ++static void ++nv44_sgdma_fill(struct nouveau_gpuobj *pgt, dma_addr_t *list, u32 base, u32 cnt) ++{ ++ struct drm_nouveau_private *dev_priv = pgt->dev->dev_private; ++ dma_addr_t dummy = dev_priv->gart_info.dummy.addr; ++ u32 pte, tmp[4]; ++ ++ pte = base >> 2; ++ base &= ~0x0000000f; ++ ++ tmp[0] = nv_ro32(pgt, base + 0x0); ++ tmp[1] = nv_ro32(pgt, base + 0x4); ++ tmp[2] = nv_ro32(pgt, base + 0x8); ++ tmp[3] = nv_ro32(pgt, base + 0xc); ++ while (cnt--) { ++ u32 addr = list ? (*list++ >> 12) : (dummy >> 12); ++ switch (pte++ & 0x3) { ++ case 0: ++ tmp[0] &= ~0x07ffffff; ++ tmp[0] |= addr; ++ break; ++ case 1: ++ tmp[0] &= ~0xf8000000; ++ tmp[0] |= addr << 27; ++ tmp[1] &= ~0x003fffff; ++ tmp[1] |= addr >> 5; ++ break; ++ case 2: ++ tmp[1] &= ~0xffc00000; ++ tmp[1] |= addr << 22; ++ tmp[2] &= ~0x0001ffff; ++ tmp[2] |= addr >> 10; ++ break; ++ case 3: ++ tmp[2] &= ~0xfffe0000; ++ tmp[2] |= addr << 17; ++ tmp[3] &= ~0x00000fff; ++ tmp[3] |= addr >> 15; ++ break; + } + } ++ ++ tmp[3] |= 0x40000000; ++ ++ nv_wo32(pgt, base + 0x0, tmp[0]); ++ nv_wo32(pgt, base + 0x4, tmp[1]); ++ nv_wo32(pgt, base + 0x8, tmp[2]); ++ nv_wo32(pgt, base + 0xc, tmp[3]); + } + + static int +-nv50_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) ++nv44_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) + { + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; + struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private; ++ struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma; ++ dma_addr_t *list = nvbe->pages; ++ u32 pte = mem->start << 2, tmp[4]; ++ u32 cnt = nvbe->nr_pages; ++ int i; + + nvbe->offset = mem->start << PAGE_SHIFT; + +- nouveau_vm_map_sg(&dev_priv->gart_info.vma, nvbe->offset, +- nvbe->nr_pages << PAGE_SHIFT, nvbe->pages); ++ if (pte & 0x0000000c) { ++ u32 max = 4 - ((pte >> 2) & 0x3); ++ u32 part = (cnt > max) ? max : cnt; ++ nv44_sgdma_fill(pgt, list, pte, part); ++ pte += (part << 2); ++ list += part; ++ cnt -= part; ++ } ++ ++ while (cnt >= 4) { ++ for (i = 0; i < 4; i++) ++ tmp[i] = *list++ >> 12; ++ nv_wo32(pgt, pte + 0x0, tmp[0] >> 0 | tmp[1] << 27); ++ nv_wo32(pgt, pte + 0x4, tmp[1] >> 5 | tmp[2] << 22); ++ nv_wo32(pgt, pte + 0x8, tmp[2] >> 10 | tmp[3] << 17); ++ nv_wo32(pgt, pte + 0xc, tmp[3] >> 15 | 0x40000000); ++ pte += 0x10; ++ cnt -= 4; ++ } ++ ++ if (cnt) ++ nv44_sgdma_fill(pgt, list, pte, cnt); ++ ++ nv44_sgdma_flush(nvbe); + nvbe->bound = true; + return 0; + } + + static int +-nv50_sgdma_unbind(struct ttm_backend *be) ++nv44_sgdma_unbind(struct ttm_backend *be) + { + struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; + struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private; ++ struct nouveau_gpuobj *pgt = dev_priv->gart_info.sg_ctxdma; ++ u32 pte = (nvbe->offset >> 12) << 2; ++ u32 cnt = nvbe->nr_pages; ++ ++ if (pte & 0x0000000c) { ++ u32 max = 4 - ((pte >> 2) & 0x3); ++ u32 part = (cnt > max) ? max : cnt; ++ nv44_sgdma_fill(pgt, NULL, pte, part); ++ pte += (part << 2); ++ cnt -= part; ++ } + +- if (!nvbe->bound) +- return 0; ++ while (cnt >= 4) { ++ nv_wo32(pgt, pte + 0x0, 0x00000000); ++ nv_wo32(pgt, pte + 0x4, 0x00000000); ++ nv_wo32(pgt, pte + 0x8, 0x00000000); ++ nv_wo32(pgt, pte + 0xc, 0x00000000); ++ pte += 0x10; ++ cnt -= 4; ++ } + +- nouveau_vm_unmap_at(&dev_priv->gart_info.vma, nvbe->offset, +- nvbe->nr_pages << PAGE_SHIFT); ++ if (cnt) ++ nv44_sgdma_fill(pgt, NULL, pte, cnt); ++ ++ nv44_sgdma_flush(nvbe); + nvbe->bound = false; + return 0; + } + +-static struct ttm_backend_func nouveau_sgdma_backend = { ++static struct ttm_backend_func nv44_sgdma_backend = { + .populate = nouveau_sgdma_populate, + .clear = nouveau_sgdma_clear, +- .bind = nouveau_sgdma_bind, +- .unbind = nouveau_sgdma_unbind, ++ .bind = nv44_sgdma_bind, ++ .unbind = nv44_sgdma_unbind, + .destroy = nouveau_sgdma_destroy + }; + ++static int ++nv50_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) ++{ ++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; ++ struct nouveau_mem *node = mem->mm_node; ++ /* noop: bound in move_notify() */ ++ node->pages = nvbe->pages; ++ nvbe->pages = (dma_addr_t *)node; ++ nvbe->bound = true; ++ return 0; ++} ++ ++static int ++nv50_sgdma_unbind(struct ttm_backend *be) ++{ ++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; ++ struct nouveau_mem *node = (struct nouveau_mem *)nvbe->pages; ++ /* noop: unbound in move_notify() */ ++ nvbe->pages = node->pages; ++ node->pages = NULL; ++ nvbe->bound = false; ++ return 0; ++} ++ + static struct ttm_backend_func nv50_sgdma_backend = { + .populate = nouveau_sgdma_populate, + .clear = nouveau_sgdma_clear, +@@ -198,10 +400,7 @@ + + nvbe->dev = dev; + +- if (dev_priv->card_type < NV_50) +- nvbe->backend.func = &nouveau_sgdma_backend; +- else +- nvbe->backend.func = &nv50_sgdma_backend; ++ nvbe->backend.func = dev_priv->gart_info.func; + return &nvbe->backend; + } + +@@ -210,21 +409,64 @@ + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpuobj *gpuobj = NULL; +- uint32_t aper_size, obj_size; +- int i, ret; ++ u32 aper_size, align; ++ int ret; + +- if (dev_priv->card_type < NV_50) { +- if(dev_priv->ramin_rsvd_vram < 2 * 1024 * 1024) +- aper_size = 64 * 1024 * 1024; +- else +- aper_size = 512 * 1024 * 1024; +- +- obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4; +- obj_size += 8; /* ctxdma header */ +- +- ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16, +- NVOBJ_FLAG_ZERO_ALLOC | +- NVOBJ_FLAG_ZERO_FREE, &gpuobj); ++ if (dev_priv->card_type >= NV_50 || drm_device_is_pcie(dev)) ++ aper_size = 512 * 1024 * 1024; ++ else ++ aper_size = 64 * 1024 * 1024; ++ ++ /* Dear NVIDIA, NV44+ would like proper present bits in PTEs for ++ * christmas. The cards before it have them, the cards after ++ * it have them, why is NV44 so unloved? ++ */ ++ dev_priv->gart_info.dummy.page = alloc_page(GFP_DMA32 | GFP_KERNEL); ++ if (!dev_priv->gart_info.dummy.page) ++ return -ENOMEM; ++ ++ dev_priv->gart_info.dummy.addr = ++ pci_map_page(dev->pdev, dev_priv->gart_info.dummy.page, ++ 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); ++ if (pci_dma_mapping_error(dev->pdev, dev_priv->gart_info.dummy.addr)) { ++ NV_ERROR(dev, "error mapping dummy page\n"); ++ __free_page(dev_priv->gart_info.dummy.page); ++ dev_priv->gart_info.dummy.page = NULL; ++ return -ENOMEM; ++ } ++ ++ if (dev_priv->card_type >= NV_50) { ++ dev_priv->gart_info.aper_base = 0; ++ dev_priv->gart_info.aper_size = aper_size; ++ dev_priv->gart_info.type = NOUVEAU_GART_HW; ++ dev_priv->gart_info.func = &nv50_sgdma_backend; ++ } else ++ if (drm_device_is_pcie(dev) && ++ dev_priv->chipset != 0x40 && dev_priv->chipset != 0x45) { ++ if (nv44_graph_class(dev)) { ++ dev_priv->gart_info.func = &nv44_sgdma_backend; ++ align = 512 * 1024; ++ } else { ++ dev_priv->gart_info.func = &nv41_sgdma_backend; ++ align = 16; ++ } ++ ++ ret = nouveau_gpuobj_new(dev, NULL, aper_size / 1024, align, ++ NVOBJ_FLAG_ZERO_ALLOC | ++ NVOBJ_FLAG_ZERO_FREE, &gpuobj); ++ if (ret) { ++ NV_ERROR(dev, "Error creating sgdma object: %d\n", ret); ++ return ret; ++ } ++ ++ dev_priv->gart_info.sg_ctxdma = gpuobj; ++ dev_priv->gart_info.aper_base = 0; ++ dev_priv->gart_info.aper_size = aper_size; ++ dev_priv->gart_info.type = NOUVEAU_GART_HW; ++ } else { ++ ret = nouveau_gpuobj_new(dev, NULL, (aper_size / 1024) + 8, 16, ++ NVOBJ_FLAG_ZERO_ALLOC | ++ NVOBJ_FLAG_ZERO_FREE, &gpuobj); + if (ret) { + NV_ERROR(dev, "Error creating sgdma object: %d\n", ret); + return ret; +@@ -236,25 +478,14 @@ + (0 << 14) /* RW */ | + (2 << 16) /* PCI */); + nv_wo32(gpuobj, 4, aper_size - 1); +- for (i = 2; i < 2 + (aper_size >> 12); i++) +- nv_wo32(gpuobj, i * 4, 0x00000000); + + dev_priv->gart_info.sg_ctxdma = gpuobj; + dev_priv->gart_info.aper_base = 0; + dev_priv->gart_info.aper_size = aper_size; +- } else +- if (dev_priv->chan_vm) { +- ret = nouveau_vm_get(dev_priv->chan_vm, 512 * 1024 * 1024, +- 12, NV_MEM_ACCESS_RW, +- &dev_priv->gart_info.vma); +- if (ret) +- return ret; +- +- dev_priv->gart_info.aper_base = dev_priv->gart_info.vma.offset; +- dev_priv->gart_info.aper_size = 512 * 1024 * 1024; ++ dev_priv->gart_info.type = NOUVEAU_GART_PDMA; ++ dev_priv->gart_info.func = &nv04_sgdma_backend; + } + +- dev_priv->gart_info.type = NOUVEAU_GART_SGDMA; + return 0; + } + +@@ -264,7 +495,13 @@ + struct drm_nouveau_private *dev_priv = dev->dev_private; + + nouveau_gpuobj_ref(NULL, &dev_priv->gart_info.sg_ctxdma); +- nouveau_vm_put(&dev_priv->gart_info.vma); ++ ++ if (dev_priv->gart_info.dummy.page) { ++ pci_unmap_page(dev->pdev, dev_priv->gart_info.dummy.addr, ++ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); ++ __free_page(dev_priv->gart_info.dummy.page); ++ dev_priv->gart_info.dummy.page = NULL; ++ } + } + + uint32_t +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_state.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_state.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_state.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_state.c 2011-03-09 18:30:58.000000000 +0100 +@@ -544,7 +544,6 @@ + nouveau_card_init_channel(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_gpuobj *gpuobj = NULL; + int ret; + + ret = nouveau_channel_alloc(dev, &dev_priv->channel, +@@ -552,41 +551,8 @@ + if (ret) + return ret; + +- /* no dma objects on fermi... */ +- if (dev_priv->card_type >= NV_C0) +- goto out_done; +- +- ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY, +- 0, dev_priv->vram_size, +- NV_MEM_ACCESS_RW, NV_MEM_TARGET_VRAM, +- &gpuobj); +- if (ret) +- goto out_err; +- +- ret = nouveau_ramht_insert(dev_priv->channel, NvDmaVRAM, gpuobj); +- nouveau_gpuobj_ref(NULL, &gpuobj); +- if (ret) +- goto out_err; +- +- ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY, +- 0, dev_priv->gart_info.aper_size, +- NV_MEM_ACCESS_RW, NV_MEM_TARGET_GART, +- &gpuobj); +- if (ret) +- goto out_err; +- +- ret = nouveau_ramht_insert(dev_priv->channel, NvDmaGART, gpuobj); +- nouveau_gpuobj_ref(NULL, &gpuobj); +- if (ret) +- goto out_err; +- +-out_done: + mutex_unlock(&dev_priv->channel->mutex); + return 0; +- +-out_err: +- nouveau_channel_put(&dev_priv->channel); +- return ret; + } + + static void nouveau_switcheroo_set_state(struct pci_dev *pdev, +@@ -929,12 +895,6 @@ + NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", + dev->pci_vendor, dev->pci_device, dev->pdev->class); + +- dev_priv->wq = create_workqueue("nouveau"); +- if (!dev_priv->wq) { +- ret = -EINVAL; +- goto err_priv; +- } +- + /* resource 0 is mmio regs */ + /* resource 1 is linear FB */ + /* resource 2 is RAMIN (mmio regs + 0x1000000) */ +@@ -947,7 +907,7 @@ + NV_ERROR(dev, "Unable to initialize the mmio mapping. " + "Please report your setup to " DRIVER_EMAIL "\n"); + ret = -EINVAL; +- goto err_wq; ++ goto err_priv; + } + NV_DEBUG(dev, "regs mapped ok at 0x%llx\n", + (unsigned long long)mmio_start_offs); +@@ -1054,8 +1014,6 @@ + iounmap(dev_priv->ramin); + err_mmio: + iounmap(dev_priv->mmio); +-err_wq: +- destroy_workqueue(dev_priv->wq); + err_priv: + kfree(dev_priv); + dev->dev_private = NULL; +@@ -1126,7 +1084,7 @@ + getparam->value = 1; + break; + case NOUVEAU_GETPARAM_HAS_PAGEFLIP: +- getparam->value = (dev_priv->card_type < NV_50); ++ getparam->value = 1; + break; + case NOUVEAU_GETPARAM_GRAPH_UNITS: + /* NV40 and NV50 versions are quite different, but register +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_temp.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_temp.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_temp.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_temp.c 2011-03-09 18:30:58.000000000 +0100 +@@ -239,11 +239,9 @@ + probe_monitoring_device(struct nouveau_i2c_chan *i2c, + struct i2c_board_info *info) + { +- char modalias[16] = "i2c:"; + struct i2c_client *client; + +- strlcat(modalias, info->type, sizeof(modalias)); +- request_module(modalias); ++ request_module("%s%s", I2C_MODULE_PREFIX, info->type); + + client = i2c_new_device(&i2c->adapter, info); + if (!client) +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_util.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_util.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_util.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_util.c 2011-03-09 18:30:58.000000000 +0100 +@@ -47,18 +47,27 @@ + printk(" (unknown bits 0x%08x)", value); + } + +-void +-nouveau_enum_print(const struct nouveau_enum *en, u32 value) ++const struct nouveau_enum * ++nouveau_enum_find(const struct nouveau_enum *en, u32 value) + { + while (en->name) { +- if (value == en->value) { +- printk("%s", en->name); +- return; +- } +- ++ if (en->value == value) ++ return en; + en++; + } + ++ return NULL; ++} ++ ++void ++nouveau_enum_print(const struct nouveau_enum *en, u32 value) ++{ ++ en = nouveau_enum_find(en, value); ++ if (en) { ++ printk("%s", en->name); ++ return; ++ } ++ + printk("(unknown enum 0x%08x)", value); + } + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_util.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_util.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_util.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_util.h 2011-03-09 18:30:58.000000000 +0100 +@@ -36,10 +36,14 @@ + struct nouveau_enum { + u32 value; + const char *name; ++ void *data; + }; + + void nouveau_bitfield_print(const struct nouveau_bitfield *, u32 value); + void nouveau_enum_print(const struct nouveau_enum *, u32 value); ++const struct nouveau_enum * ++nouveau_enum_find(const struct nouveau_enum *, u32 value); ++ + int nouveau_ratelimit(void); + + #endif +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_vm.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_vm.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_vm.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_vm.c 2011-03-09 18:30:58.000000000 +0100 +@@ -28,7 +28,7 @@ + #include "nouveau_vm.h" + + void +-nouveau_vm_map_at(struct nouveau_vma *vma, u64 delta, struct nouveau_vram *vram) ++nouveau_vm_map_at(struct nouveau_vma *vma, u64 delta, struct nouveau_mem *node) + { + struct nouveau_vm *vm = vma->vm; + struct nouveau_mm_node *r; +@@ -40,7 +40,8 @@ + u32 max = 1 << (vm->pgt_bits - bits); + u32 end, len; + +- list_for_each_entry(r, &vram->regions, rl_entry) { ++ delta = 0; ++ list_for_each_entry(r, &node->regions, rl_entry) { + u64 phys = (u64)r->offset << 12; + u32 num = r->length >> bits; + +@@ -52,7 +53,7 @@ + end = max; + len = end - pte; + +- vm->map(vma, pgt, vram, pte, len, phys); ++ vm->map(vma, pgt, node, pte, len, phys, delta); + + num -= len; + pte += len; +@@ -60,6 +61,8 @@ + pde++; + pte = 0; + } ++ ++ delta += (u64)len << vma->node->type; + } + } + +@@ -67,14 +70,14 @@ + } + + void +-nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_vram *vram) ++nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_mem *node) + { +- nouveau_vm_map_at(vma, 0, vram); ++ nouveau_vm_map_at(vma, 0, node); + } + + void + nouveau_vm_map_sg(struct nouveau_vma *vma, u64 delta, u64 length, +- dma_addr_t *list) ++ struct nouveau_mem *mem, dma_addr_t *list) + { + struct nouveau_vm *vm = vma->vm; + int big = vma->node->type != vm->spg_shift; +@@ -94,7 +97,7 @@ + end = max; + len = end - pte; + +- vm->map_sg(vma, pgt, pte, list, len); ++ vm->map_sg(vma, pgt, mem, pte, len, list); + + num -= len; + pte += len; +@@ -311,18 +314,7 @@ + vm->spg_shift = 12; + vm->lpg_shift = 17; + pgt_bits = 27; +- +- /* Should be 4096 everywhere, this is a hack that's +- * currently necessary to avoid an elusive bug that +- * causes corruption when mixing small/large pages +- */ +- if (length < (1ULL << 40)) +- block = 4096; +- else { +- block = (1 << pgt_bits); +- if (length < block) +- block = length; +- } ++ block = 4096; + } else { + kfree(vm); + return -ENOSYS; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_vm.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_vm.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nouveau_vm.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nouveau_vm.h 2011-03-09 18:30:58.000000000 +0100 +@@ -67,9 +67,10 @@ + void (*map_pgt)(struct nouveau_gpuobj *pgd, u32 pde, + struct nouveau_gpuobj *pgt[2]); + void (*map)(struct nouveau_vma *, struct nouveau_gpuobj *, +- struct nouveau_vram *, u32 pte, u32 cnt, u64 phys); ++ struct nouveau_mem *, u32 pte, u32 cnt, ++ u64 phys, u64 delta); + void (*map_sg)(struct nouveau_vma *, struct nouveau_gpuobj *, +- u32 pte, dma_addr_t *, u32 cnt); ++ struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); + void (*unmap)(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt); + void (*flush)(struct nouveau_vm *); + }; +@@ -82,20 +83,20 @@ + int nouveau_vm_get(struct nouveau_vm *, u64 size, u32 page_shift, + u32 access, struct nouveau_vma *); + void nouveau_vm_put(struct nouveau_vma *); +-void nouveau_vm_map(struct nouveau_vma *, struct nouveau_vram *); +-void nouveau_vm_map_at(struct nouveau_vma *, u64 offset, struct nouveau_vram *); ++void nouveau_vm_map(struct nouveau_vma *, struct nouveau_mem *); ++void nouveau_vm_map_at(struct nouveau_vma *, u64 offset, struct nouveau_mem *); + void nouveau_vm_unmap(struct nouveau_vma *); + void nouveau_vm_unmap_at(struct nouveau_vma *, u64 offset, u64 length); + void nouveau_vm_map_sg(struct nouveau_vma *, u64 offset, u64 length, +- dma_addr_t *); ++ struct nouveau_mem *, dma_addr_t *); + + /* nv50_vm.c */ + void nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, + struct nouveau_gpuobj *pgt[2]); + void nv50_vm_map(struct nouveau_vma *, struct nouveau_gpuobj *, +- struct nouveau_vram *, u32 pte, u32 cnt, u64 phys); ++ struct nouveau_mem *, u32 pte, u32 cnt, u64 phys, u64 delta); + void nv50_vm_map_sg(struct nouveau_vma *, struct nouveau_gpuobj *, +- u32 pte, dma_addr_t *, u32 cnt); ++ struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); + void nv50_vm_unmap(struct nouveau_gpuobj *, u32 pte, u32 cnt); + void nv50_vm_flush(struct nouveau_vm *); + void nv50_vm_flush_engine(struct drm_device *, int engine); +@@ -104,9 +105,9 @@ + void nvc0_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, + struct nouveau_gpuobj *pgt[2]); + void nvc0_vm_map(struct nouveau_vma *, struct nouveau_gpuobj *, +- struct nouveau_vram *, u32 pte, u32 cnt, u64 phys); ++ struct nouveau_mem *, u32 pte, u32 cnt, u64 phys, u64 delta); + void nvc0_vm_map_sg(struct nouveau_vma *, struct nouveau_gpuobj *, +- u32 pte, dma_addr_t *, u32 cnt); ++ struct nouveau_mem *, u32 pte, u32 cnt, dma_addr_t *); + void nvc0_vm_unmap(struct nouveau_gpuobj *, u32 pte, u32 cnt); + void nvc0_vm_flush(struct nouveau_vm *); + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv04_crtc.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv04_crtc.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv04_crtc.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv04_crtc.c 2011-03-09 18:30:58.000000000 +0100 +@@ -1031,7 +1031,7 @@ + drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); + + ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, +- 0, 0x0000, false, true, &nv_crtc->cursor.nvbo); ++ 0, 0x0000, &nv_crtc->cursor.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv04_fifo.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv04_fifo.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv04_fifo.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv04_fifo.c 2011-03-09 18:30:58.000000000 +0100 +@@ -379,6 +379,15 @@ + return handled; + } + ++static const char *nv_dma_state_err(u32 state) ++{ ++ static const char * const desc[] = { ++ "NONE", "CALL_SUBR_ACTIVE", "INVALID_MTHD", "RET_SUBR_INACTIVE", ++ "INVALID_CMD", "IB_EMPTY"/* NV50+ */, "MEM_FAULT", "UNK" ++ }; ++ return desc[(state >> 29) & 0x7]; ++} ++ + void + nv04_fifo_isr(struct drm_device *dev) + { +@@ -460,9 +469,10 @@ + if (nouveau_ratelimit()) + NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%02x%08x " + "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x " +- "State 0x%08x Push 0x%08x\n", ++ "State 0x%08x (err: %s) Push 0x%08x\n", + chid, ho_get, dma_get, ho_put, + dma_put, ib_get, ib_put, state, ++ nv_dma_state_err(state), + push); + + /* METHOD_COUNT, in DMA_STATE on earlier chipsets */ +@@ -476,8 +486,9 @@ + } + } else { + NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%08x " +- "Put 0x%08x State 0x%08x Push 0x%08x\n", +- chid, dma_get, dma_put, state, push); ++ "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n", ++ chid, dma_get, dma_put, state, ++ nv_dma_state_err(state), push); + + if (dma_get != dma_put) + nv_wr32(dev, 0x003244, dma_put); +@@ -505,7 +516,7 @@ + + if (dev_priv->card_type == NV_50) { + if (status & 0x00000010) { +- nv50_fb_vm_trap(dev, 1, "PFIFO_BAR_FAULT"); ++ nv50_fb_vm_trap(dev, nouveau_ratelimit()); + status &= ~0x00000010; + nv_wr32(dev, 0x002100, 0x00000010); + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv40_fb.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv40_fb.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv40_fb.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv40_fb.c 2011-03-09 18:30:58.000000000 +0100 +@@ -24,6 +24,53 @@ + } + } + ++static void ++nv40_fb_init_gart(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_gpuobj *gart = dev_priv->gart_info.sg_ctxdma; ++ ++ if (dev_priv->gart_info.type != NOUVEAU_GART_HW) { ++ nv_wr32(dev, 0x100800, 0x00000001); ++ return; ++ } ++ ++ nv_wr32(dev, 0x100800, gart->pinst | 0x00000002); ++ nv_mask(dev, 0x10008c, 0x00000100, 0x00000100); ++ nv_wr32(dev, 0x100820, 0x00000000); ++} ++ ++static void ++nv44_fb_init_gart(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_gpuobj *gart = dev_priv->gart_info.sg_ctxdma; ++ u32 vinst; ++ ++ if (dev_priv->gart_info.type != NOUVEAU_GART_HW) { ++ nv_wr32(dev, 0x100850, 0x80000000); ++ nv_wr32(dev, 0x100800, 0x00000001); ++ return; ++ } ++ ++ /* calculate vram address of this PRAMIN block, object ++ * must be allocated on 512KiB alignment, and not exceed ++ * a total size of 512KiB for this to work correctly ++ */ ++ vinst = nv_rd32(dev, 0x10020c); ++ vinst -= ((gart->pinst >> 19) + 1) << 19; ++ ++ nv_wr32(dev, 0x100850, 0x80000000); ++ nv_wr32(dev, 0x100818, dev_priv->gart_info.dummy.addr); ++ ++ nv_wr32(dev, 0x100804, dev_priv->gart_info.aper_size); ++ nv_wr32(dev, 0x100850, 0x00008000); ++ nv_mask(dev, 0x10008c, 0x00000200, 0x00000200); ++ nv_wr32(dev, 0x100820, 0x00000000); ++ nv_wr32(dev, 0x10082c, 0x00000001); ++ nv_wr32(dev, 0x100800, vinst | 0x00000010); ++} ++ + int + nv40_fb_init(struct drm_device *dev) + { +@@ -32,12 +79,12 @@ + uint32_t tmp; + int i; + +- /* This is strictly a NV4x register (don't know about NV5x). */ +- /* The blob sets these to all kinds of values, and they mess up our setup. */ +- /* I got value 0x52802 instead. For some cards the blob even sets it back to 0x1. */ +- /* Note: the blob doesn't read this value, so i'm pretty sure this is safe for all cards. */ +- /* Any idea what this is? */ +- nv_wr32(dev, NV40_PFB_UNK_800, 0x1); ++ if (dev_priv->chipset != 0x40 && dev_priv->chipset != 0x45) { ++ if (nv44_graph_class(dev)) ++ nv44_fb_init_gart(dev); ++ else ++ nv40_fb_init_gart(dev); ++ } + + switch (dev_priv->chipset) { + case 0x40: +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_crtc.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_crtc.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_crtc.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_crtc.c 2011-03-09 18:30:58.000000000 +0100 +@@ -65,7 +65,7 @@ + { + struct drm_device *dev = nv_crtc->base.dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + int index = nv_crtc->index, ret; + + NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); +@@ -135,8 +135,7 @@ + nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) + { + struct drm_device *dev = nv_crtc->base.dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + int ret; + + NV_DEBUG_KMS(dev, "\n"); +@@ -186,8 +185,7 @@ + struct nouveau_connector *nv_connector = + nouveau_crtc_connector_get(nv_crtc); + struct drm_device *dev = nv_crtc->base.dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + struct drm_display_mode *native_mode = NULL; + struct drm_display_mode *mode = &nv_crtc->base.mode; + uint32_t outX, outY, horiz, vert; +@@ -445,6 +443,42 @@ + { + } + ++static int ++nv50_crtc_wait_complete(struct drm_crtc *crtc) ++{ ++ struct drm_device *dev = crtc->dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; ++ struct nv50_display *disp = nv50_display(dev); ++ struct nouveau_channel *evo = disp->master; ++ u64 start; ++ int ret; ++ ++ ret = RING_SPACE(evo, 6); ++ if (ret) ++ return ret; ++ BEGIN_RING(evo, 0, 0x0084, 1); ++ OUT_RING (evo, 0x80000000); ++ BEGIN_RING(evo, 0, 0x0080, 1); ++ OUT_RING (evo, 0); ++ BEGIN_RING(evo, 0, 0x0084, 1); ++ OUT_RING (evo, 0x00000000); ++ ++ nv_wo32(disp->ntfy, 0x000, 0x00000000); ++ FIRE_RING (evo); ++ ++ start = ptimer->read(dev); ++ do { ++ nv_wr32(dev, 0x61002c, 0x370); ++ nv_wr32(dev, 0x000140, 1); ++ ++ if (nv_ro32(disp->ntfy, 0x000)) ++ return 0; ++ } while (ptimer->read(dev) - start < 2000000000ULL); ++ ++ return -EBUSY; ++} ++ + static void + nv50_crtc_prepare(struct drm_crtc *crtc) + { +@@ -453,6 +487,7 @@ + + NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + ++ nv50_display_flip_stop(crtc); + drm_vblank_pre_modeset(dev, nv_crtc->index); + nv50_crtc_blank(nv_crtc, true); + } +@@ -461,24 +496,14 @@ + nv50_crtc_commit(struct drm_crtc *crtc) + { + struct drm_device *dev = crtc->dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); +- int ret; + + NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + + nv50_crtc_blank(nv_crtc, false); + drm_vblank_post_modeset(dev, nv_crtc->index); +- +- ret = RING_SPACE(evo, 2); +- if (ret) { +- NV_ERROR(dev, "no space while committing crtc\n"); +- return; +- } +- BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); +- OUT_RING (evo, 0); +- FIRE_RING (evo); ++ nv50_crtc_wait_complete(crtc); ++ nv50_display_flip_next(crtc, crtc->fb, NULL); + } + + static bool +@@ -491,15 +516,15 @@ + static int + nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *passed_fb, +- int x, int y, bool update, bool atomic) ++ int x, int y, bool atomic) + { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = nv_crtc->base.dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + struct drm_framebuffer *drm_fb = nv_crtc->base.fb; + struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); +- int ret, format; ++ int ret; + + NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); + +@@ -525,28 +550,6 @@ + } + } + +- switch (drm_fb->depth) { +- case 8: +- format = NV50_EVO_CRTC_FB_DEPTH_8; +- break; +- case 15: +- format = NV50_EVO_CRTC_FB_DEPTH_15; +- break; +- case 16: +- format = NV50_EVO_CRTC_FB_DEPTH_16; +- break; +- case 24: +- case 32: +- format = NV50_EVO_CRTC_FB_DEPTH_24; +- break; +- case 30: +- format = NV50_EVO_CRTC_FB_DEPTH_30; +- break; +- default: +- NV_ERROR(dev, "unknown depth %d\n", drm_fb->depth); +- return -EINVAL; +- } +- + nv_crtc->fb.offset = fb->nvbo->bo.mem.start << PAGE_SHIFT; + nv_crtc->fb.tile_flags = nouveau_bo_tile_layout(fb->nvbo); + nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8; +@@ -556,14 +559,7 @@ + return ret; + + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1); +- if (nv_crtc->fb.tile_flags == 0x7a00 || +- nv_crtc->fb.tile_flags == 0xfe00) +- OUT_RING(evo, NvEvoFB32); +- else +- if (nv_crtc->fb.tile_flags == 0x7000) +- OUT_RING(evo, NvEvoFB16); +- else +- OUT_RING(evo, NvEvoVRAM_LP); ++ OUT_RING (evo, fb->r_dma); + } + + ret = RING_SPACE(evo, 12); +@@ -571,45 +567,26 @@ + return ret; + + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5); +- OUT_RING(evo, nv_crtc->fb.offset >> 8); +- OUT_RING(evo, 0); +- OUT_RING(evo, (drm_fb->height << 16) | drm_fb->width); +- if (!nv_crtc->fb.tile_flags) { +- OUT_RING(evo, drm_fb->pitch | (1 << 20)); +- } else { +- u32 tile_mode = fb->nvbo->tile_mode; +- if (dev_priv->card_type >= NV_C0) +- tile_mode >>= 4; +- OUT_RING(evo, ((drm_fb->pitch / 4) << 4) | tile_mode); +- } +- if (dev_priv->chipset == 0x50) +- OUT_RING(evo, (nv_crtc->fb.tile_flags << 8) | format); +- else +- OUT_RING(evo, format); ++ OUT_RING (evo, nv_crtc->fb.offset >> 8); ++ OUT_RING (evo, 0); ++ OUT_RING (evo, (drm_fb->height << 16) | drm_fb->width); ++ OUT_RING (evo, fb->r_pitch); ++ OUT_RING (evo, fb->r_format); + + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1); +- OUT_RING(evo, fb->base.depth == 8 ? +- NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON); ++ OUT_RING (evo, fb->base.depth == 8 ? ++ NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON); + + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); +- OUT_RING(evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR); ++ OUT_RING (evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR); + BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1); +- OUT_RING(evo, (y << 16) | x); ++ OUT_RING (evo, (y << 16) | x); + + if (nv_crtc->lut.depth != fb->base.depth) { + nv_crtc->lut.depth = fb->base.depth; + nv50_crtc_lut_load(crtc); + } + +- if (update) { +- ret = RING_SPACE(evo, 2); +- if (ret) +- return ret; +- BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); +- OUT_RING(evo, 0); +- FIRE_RING(evo); +- } +- + return 0; + } + +@@ -619,8 +596,7 @@ + struct drm_framebuffer *old_fb) + { + struct drm_device *dev = crtc->dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_connector *nv_connector = NULL; + uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; +@@ -700,14 +676,25 @@ + nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false); + nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false); + +- return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false, false); ++ return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); + } + + static int + nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) + { +- return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, true, false); ++ int ret; ++ ++ nv50_display_flip_stop(crtc); ++ ret = nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); ++ if (ret) ++ return ret; ++ ++ ret = nv50_crtc_wait_complete(crtc); ++ if (ret) ++ return ret; ++ ++ return nv50_display_flip_next(crtc, crtc->fb, NULL); + } + + static int +@@ -715,7 +702,14 @@ + struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) + { +- return nv50_crtc_do_mode_set_base(crtc, fb, x, y, true, true); ++ int ret; ++ ++ nv50_display_flip_stop(crtc); ++ ret = nv50_crtc_do_mode_set_base(crtc, fb, x, y, true); ++ if (ret) ++ return ret; ++ ++ return nv50_crtc_wait_complete(crtc); + } + + static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = { +@@ -758,7 +752,7 @@ + nv_crtc->lut.depth = 0; + + ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM, +- 0, 0x0000, false, true, &nv_crtc->lut.nvbo); ++ 0, 0x0000, &nv_crtc->lut.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) +@@ -784,7 +778,7 @@ + drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); + + ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, +- 0, 0x0000, false, true, &nv_crtc->cursor.nvbo); ++ 0, 0x0000, &nv_crtc->cursor.nvbo); + if (!ret) { + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_cursor.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_cursor.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_cursor.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_cursor.c 2011-03-09 18:30:58.000000000 +0100 +@@ -36,9 +36,9 @@ + static void + nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update) + { +- struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; + struct drm_device *dev = nv_crtc->base.dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + int ret; + + NV_DEBUG_KMS(dev, "\n"); +@@ -71,9 +71,9 @@ + static void + nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) + { +- struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; + struct drm_device *dev = nv_crtc->base.dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + int ret; + + NV_DEBUG_KMS(dev, "\n"); +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_dac.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_dac.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_dac.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_dac.c 2011-03-09 18:30:58.000000000 +0100 +@@ -41,8 +41,7 @@ + { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + int ret; + + if (!nv_encoder->crtc) +@@ -216,8 +215,7 @@ + { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); + uint32_t mode_ctl = 0, mode_ctl2 = 0; + int ret; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_display.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_display.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_display.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_display.c 2011-03-09 18:30:58.000000000 +0100 +@@ -24,6 +24,7 @@ + * + */ + ++#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) + #include "nv50_display.h" + #include "nouveau_crtc.h" + #include "nouveau_encoder.h" +@@ -34,6 +35,7 @@ + #include "drm_crtc_helper.h" + + static void nv50_display_isr(struct drm_device *); ++static void nv50_display_bh(unsigned long); + + static inline int + nv50_sor_nr(struct drm_device *dev) +@@ -172,16 +174,16 @@ + ret = nv50_evo_init(dev); + if (ret) + return ret; +- evo = dev_priv->evo; ++ evo = nv50_display(dev)->master; + + nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9); + +- ret = RING_SPACE(evo, 11); ++ ret = RING_SPACE(evo, 15); + if (ret) + return ret; + BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); + OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED); +- OUT_RING(evo, NV50_EVO_DMA_NOTIFY_HANDLE_NONE); ++ OUT_RING(evo, NvEvoSync); + BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1); + OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); + BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1); +@@ -190,6 +192,11 @@ + OUT_RING(evo, 0); + BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1); + OUT_RING(evo, 0); ++ /* required to make display sync channels not hate life */ ++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK900), 1); ++ OUT_RING (evo, 0x00000311); ++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(1, UNK900), 1); ++ OUT_RING (evo, 0x00000311); + FIRE_RING(evo); + if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2)) + NV_ERROR(dev, "evo pushbuf stalled\n"); +@@ -201,6 +208,8 @@ + static int nv50_display_disable(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nv50_display *disp = nv50_display(dev); ++ struct nouveau_channel *evo = disp->master; + struct drm_crtc *drm_crtc; + int ret, i; + +@@ -212,12 +221,12 @@ + nv50_crtc_blank(crtc, true); + } + +- ret = RING_SPACE(dev_priv->evo, 2); ++ ret = RING_SPACE(evo, 2); + if (ret == 0) { +- BEGIN_RING(dev_priv->evo, 0, NV50_EVO_UPDATE, 1); +- OUT_RING(dev_priv->evo, 0); ++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); ++ OUT_RING(evo, 0); + } +- FIRE_RING(dev_priv->evo); ++ FIRE_RING(evo); + + /* Almost like ack'ing a vblank interrupt, maybe in the spirit of + * cleaning up? +@@ -267,10 +276,16 @@ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_table *dcb = &dev_priv->vbios.dcb; + struct drm_connector *connector, *ct; ++ struct nv50_display *priv; + int ret, i; + + NV_DEBUG_KMS(dev, "\n"); + ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ dev_priv->engine.display.priv = priv; ++ + /* init basic kernel modesetting */ + drm_mode_config_init(dev); + +@@ -330,7 +345,7 @@ + } + } + +- INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); ++ tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); + nouveau_irq_register(dev, 26, nv50_display_isr); + + ret = nv50_display_init(dev); +@@ -345,12 +360,131 @@ + void + nv50_display_destroy(struct drm_device *dev) + { ++ struct nv50_display *disp = nv50_display(dev); ++ + NV_DEBUG_KMS(dev, "\n"); + + drm_mode_config_cleanup(dev); + + nv50_display_disable(dev); + nouveau_irq_unregister(dev, 26); ++ kfree(disp); ++} ++ ++void ++nv50_display_flip_stop(struct drm_crtc *crtc) ++{ ++ struct nv50_display *disp = nv50_display(crtc->dev); ++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); ++ struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; ++ struct nouveau_channel *evo = dispc->sync; ++ int ret; ++ ++ ret = RING_SPACE(evo, 8); ++ if (ret) { ++ WARN_ON(1); ++ return; ++ } ++ ++ BEGIN_RING(evo, 0, 0x0084, 1); ++ OUT_RING (evo, 0x00000000); ++ BEGIN_RING(evo, 0, 0x0094, 1); ++ OUT_RING (evo, 0x00000000); ++ BEGIN_RING(evo, 0, 0x00c0, 1); ++ OUT_RING (evo, 0x00000000); ++ BEGIN_RING(evo, 0, 0x0080, 1); ++ OUT_RING (evo, 0x00000000); ++ FIRE_RING (evo); ++} ++ ++int ++nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, ++ struct nouveau_channel *chan) ++{ ++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; ++ struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); ++ struct nv50_display *disp = nv50_display(crtc->dev); ++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); ++ struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; ++ struct nouveau_channel *evo = dispc->sync; ++ int ret; ++ ++ ret = RING_SPACE(evo, 24); ++ if (unlikely(ret)) ++ return ret; ++ ++ /* synchronise with the rendering channel, if necessary */ ++ if (likely(chan)) { ++ u64 offset = dispc->sem.bo->vma.offset + dispc->sem.offset; ++ ++ ret = RING_SPACE(chan, 10); ++ if (ret) { ++ WIND_RING(evo); ++ return ret; ++ } ++ ++ if (dev_priv->chipset < 0xc0) { ++ BEGIN_RING(chan, NvSubSw, 0x0060, 2); ++ OUT_RING (chan, NvEvoSema0 + nv_crtc->index); ++ OUT_RING (chan, dispc->sem.offset); ++ BEGIN_RING(chan, NvSubSw, 0x006c, 1); ++ OUT_RING (chan, 0xf00d0000 | dispc->sem.value); ++ BEGIN_RING(chan, NvSubSw, 0x0064, 2); ++ OUT_RING (chan, dispc->sem.offset ^ 0x10); ++ OUT_RING (chan, 0x74b1e000); ++ BEGIN_RING(chan, NvSubSw, 0x0060, 1); ++ if (dev_priv->chipset < 0x84) ++ OUT_RING (chan, NvSema); ++ else ++ OUT_RING (chan, chan->vram_handle); ++ } else { ++ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); ++ OUT_RING (chan, upper_32_bits(offset)); ++ OUT_RING (chan, lower_32_bits(offset)); ++ OUT_RING (chan, 0xf00d0000 | dispc->sem.value); ++ OUT_RING (chan, 0x1002); ++ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4); ++ OUT_RING (chan, upper_32_bits(offset)); ++ OUT_RING (chan, lower_32_bits(offset ^ 0x10)); ++ OUT_RING (chan, 0x74b1e000); ++ OUT_RING (chan, 0x1001); ++ } ++ FIRE_RING (chan); ++ } else { ++ nouveau_bo_wr32(dispc->sem.bo, dispc->sem.offset / 4, ++ 0xf00d0000 | dispc->sem.value); ++ } ++ ++ /* queue the flip on the crtc's "display sync" channel */ ++ BEGIN_RING(evo, 0, 0x0100, 1); ++ OUT_RING (evo, 0xfffe0000); ++ BEGIN_RING(evo, 0, 0x0084, 5); ++ OUT_RING (evo, chan ? 0x00000100 : 0x00000010); ++ OUT_RING (evo, dispc->sem.offset); ++ OUT_RING (evo, 0xf00d0000 | dispc->sem.value); ++ OUT_RING (evo, 0x74b1e000); ++ OUT_RING (evo, NvEvoSync); ++ BEGIN_RING(evo, 0, 0x00a0, 2); ++ OUT_RING (evo, 0x00000000); ++ OUT_RING (evo, 0x00000000); ++ BEGIN_RING(evo, 0, 0x00c0, 1); ++ OUT_RING (evo, nv_fb->r_dma); ++ BEGIN_RING(evo, 0, 0x0110, 2); ++ OUT_RING (evo, 0x00000000); ++ OUT_RING (evo, 0x00000000); ++ BEGIN_RING(evo, 0, 0x0800, 5); ++ OUT_RING (evo, (nv_fb->nvbo->bo.mem.start << PAGE_SHIFT) >> 8); ++ OUT_RING (evo, 0); ++ OUT_RING (evo, (fb->height << 16) | fb->width); ++ OUT_RING (evo, nv_fb->r_pitch); ++ OUT_RING (evo, nv_fb->r_format); ++ BEGIN_RING(evo, 0, 0x0080, 1); ++ OUT_RING (evo, 0x00000000); ++ FIRE_RING (evo); ++ ++ dispc->sem.offset ^= 0x10; ++ dispc->sem.value++; ++ return 0; + } + + static u16 +@@ -466,11 +600,12 @@ + nv50_display_unk10_handler(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nv50_display *disp = nv50_display(dev); + u32 unk30 = nv_rd32(dev, 0x610030), mc; + int i, crtc, or, type = OUTPUT_ANY; + + NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); +- dev_priv->evo_irq.dcb = NULL; ++ disp->irq.dcb = NULL; + + nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8); + +@@ -541,7 +676,7 @@ + + if (dcb->type == type && (dcb->or & (1 << or))) { + nouveau_bios_run_display_table(dev, dcb, 0, -1); +- dev_priv->evo_irq.dcb = dcb; ++ disp->irq.dcb = dcb; + goto ack; + } + } +@@ -587,15 +722,16 @@ + nv50_display_unk20_handler(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; +- u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc; ++ struct nv50_display *disp = nv50_display(dev); ++ u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc = 0; + struct dcb_entry *dcb; + int i, crtc, or, type = OUTPUT_ANY; + + NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); +- dcb = dev_priv->evo_irq.dcb; ++ dcb = disp->irq.dcb; + if (dcb) { + nouveau_bios_run_display_table(dev, dcb, 0, -2); +- dev_priv->evo_irq.dcb = NULL; ++ disp->irq.dcb = NULL; + } + + /* CRTC clock change requested? */ +@@ -692,9 +828,9 @@ + nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0); + } + +- dev_priv->evo_irq.dcb = dcb; +- dev_priv->evo_irq.pclk = pclk; +- dev_priv->evo_irq.script = script; ++ disp->irq.dcb = dcb; ++ disp->irq.pclk = pclk; ++ disp->irq.script = script; + + ack: + nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20); +@@ -735,13 +871,13 @@ + static void + nv50_display_unk40_handler(struct drm_device *dev) + { +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct dcb_entry *dcb = dev_priv->evo_irq.dcb; +- u16 script = dev_priv->evo_irq.script; +- u32 unk30 = nv_rd32(dev, 0x610030), pclk = dev_priv->evo_irq.pclk; ++ struct nv50_display *disp = nv50_display(dev); ++ struct dcb_entry *dcb = disp->irq.dcb; ++ u16 script = disp->irq.script; ++ u32 unk30 = nv_rd32(dev, 0x610030), pclk = disp->irq.pclk; + + NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30); +- dev_priv->evo_irq.dcb = NULL; ++ disp->irq.dcb = NULL; + if (!dcb) + goto ack; + +@@ -754,12 +890,10 @@ + nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8); + } + +-void +-nv50_display_irq_handler_bh(struct work_struct *work) ++static void ++nv50_display_bh(unsigned long data) + { +- struct drm_nouveau_private *dev_priv = +- container_of(work, struct drm_nouveau_private, irq_work); +- struct drm_device *dev = dev_priv->dev; ++ struct drm_device *dev = (struct drm_device *)data; + + for (;;) { + uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); +@@ -807,7 +941,7 @@ + static void + nv50_display_isr(struct drm_device *dev) + { +- struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nv50_display *disp = nv50_display(dev); + uint32_t delayed = 0; + + while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { +@@ -835,8 +969,7 @@ + NV50_PDISPLAY_INTR_1_CLK_UNK40)); + if (clock) { + nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); +- if (!work_pending(&dev_priv->irq_work)) +- queue_work(dev_priv->wq, &dev_priv->irq_work); ++ tasklet_schedule(&disp->tasklet); + delayed |= clock; + intr1 &= ~clock; + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_display.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_display.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_display.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_display.h 2011-03-09 18:30:58.000000000 +0100 +@@ -35,7 +35,36 @@ + #include "nouveau_crtc.h" + #include "nv50_evo.h" + +-void nv50_display_irq_handler_bh(struct work_struct *work); ++struct nv50_display_crtc { ++ struct nouveau_channel *sync; ++ struct { ++ struct nouveau_bo *bo; ++ u32 offset; ++ u16 value; ++ } sem; ++}; ++ ++struct nv50_display { ++ struct nouveau_channel *master; ++ struct nouveau_gpuobj *ntfy; ++ ++ struct nv50_display_crtc crtc[2]; ++ ++ struct tasklet_struct tasklet; ++ struct { ++ struct dcb_entry *dcb; ++ u16 script; ++ u32 pclk; ++ } irq; ++}; ++ ++static inline struct nv50_display * ++nv50_display(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ return dev_priv->engine.display.priv; ++} ++ + int nv50_display_early_init(struct drm_device *dev); + void nv50_display_late_takedown(struct drm_device *dev); + int nv50_display_create(struct drm_device *dev); +@@ -44,4 +73,15 @@ + int nv50_crtc_blank(struct nouveau_crtc *, bool blank); + int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); + ++int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, ++ struct nouveau_channel *chan); ++void nv50_display_flip_stop(struct drm_crtc *); ++ ++int nv50_evo_init(struct drm_device *dev); ++void nv50_evo_fini(struct drm_device *dev); ++void nv50_evo_dmaobj_init(struct nouveau_gpuobj *, u32 memtype, u64 base, ++ u64 size); ++int nv50_evo_dmaobj_new(struct nouveau_channel *, u32 handle, u32 memtype, ++ u64 base, u64 size, struct nouveau_gpuobj **); ++ + #endif /* __NV50_DISPLAY_H__ */ +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_evo.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_evo.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_evo.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_evo.c 2011-03-09 18:30:58.000000000 +0100 +@@ -27,20 +27,17 @@ + #include "nouveau_drv.h" + #include "nouveau_dma.h" + #include "nouveau_ramht.h" ++#include "nv50_display.h" + + static void + nv50_evo_channel_del(struct nouveau_channel **pevo) + { +- struct drm_nouveau_private *dev_priv; + struct nouveau_channel *evo = *pevo; + + if (!evo) + return; + *pevo = NULL; + +- dev_priv = evo->dev->dev_private; +- dev_priv->evo_alloc &= ~(1 << evo->id); +- + nouveau_gpuobj_channel_takedown(evo); + nouveau_bo_unmap(evo->pushbuf_bo); + nouveau_bo_ref(NULL, &evo->pushbuf_bo); +@@ -51,42 +48,61 @@ + kfree(evo); + } + ++void ++nv50_evo_dmaobj_init(struct nouveau_gpuobj *obj, u32 memtype, u64 base, u64 size) ++{ ++ struct drm_nouveau_private *dev_priv = obj->dev->dev_private; ++ u32 flags5; ++ ++ if (dev_priv->chipset < 0xc0) { ++ /* not supported on 0x50, specified in format mthd */ ++ if (dev_priv->chipset == 0x50) ++ memtype = 0; ++ flags5 = 0x00010000; ++ } else { ++ if (memtype & 0x80000000) ++ flags5 = 0x00000000; /* large pages */ ++ else ++ flags5 = 0x00020000; ++ } ++ ++ nv50_gpuobj_dma_init(obj, 0, 0x3d, base, size, NV_MEM_TARGET_VRAM, ++ NV_MEM_ACCESS_RW, (memtype >> 8) & 0xff, 0); ++ nv_wo32(obj, 0x14, flags5); ++ dev_priv->engine.instmem.flush(obj->dev); ++} ++ + int +-nv50_evo_dmaobj_new(struct nouveau_channel *evo, u32 class, u32 name, +- u32 tile_flags, u32 magic_flags, u32 offset, u32 limit, +- u32 flags5) ++nv50_evo_dmaobj_new(struct nouveau_channel *evo, u32 handle, u32 memtype, ++ u64 base, u64 size, struct nouveau_gpuobj **pobj) + { +- struct drm_nouveau_private *dev_priv = evo->dev->dev_private; +- struct drm_device *dev = evo->dev; ++ struct nv50_display *disp = nv50_display(evo->dev); + struct nouveau_gpuobj *obj = NULL; + int ret; + +- ret = nouveau_gpuobj_new(dev, dev_priv->evo, 6*4, 32, 0, &obj); ++ ret = nouveau_gpuobj_new(evo->dev, disp->master, 6*4, 32, 0, &obj); + if (ret) + return ret; + obj->engine = NVOBJ_ENGINE_DISPLAY; + +- nv_wo32(obj, 0, (tile_flags << 22) | (magic_flags << 16) | class); +- nv_wo32(obj, 4, limit); +- nv_wo32(obj, 8, offset); +- nv_wo32(obj, 12, 0x00000000); +- nv_wo32(obj, 16, 0x00000000); +- nv_wo32(obj, 20, flags5); +- dev_priv->engine.instmem.flush(dev); ++ nv50_evo_dmaobj_init(obj, memtype, base, size); + +- ret = nouveau_ramht_insert(evo, name, obj); +- nouveau_gpuobj_ref(NULL, &obj); +- if (ret) { +- return ret; +- } ++ ret = nouveau_ramht_insert(evo, handle, obj); ++ if (ret) ++ goto out; + +- return 0; ++ if (pobj) ++ nouveau_gpuobj_ref(obj, pobj); ++out: ++ nouveau_gpuobj_ref(NULL, &obj); ++ return ret; + } + + static int +-nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pevo) ++nv50_evo_channel_new(struct drm_device *dev, int chid, ++ struct nouveau_channel **pevo) + { +- struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nv50_display *disp = nv50_display(dev); + struct nouveau_channel *evo; + int ret; + +@@ -95,25 +111,13 @@ + return -ENOMEM; + *pevo = evo; + +- for (evo->id = 0; evo->id < 5; evo->id++) { +- if (dev_priv->evo_alloc & (1 << evo->id)) +- continue; +- +- dev_priv->evo_alloc |= (1 << evo->id); +- break; +- } +- +- if (evo->id == 5) { +- kfree(evo); +- return -ENODEV; +- } +- ++ evo->id = chid; + evo->dev = dev; + evo->user_get = 4; + evo->user_put = 0; + + ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, +- false, true, &evo->pushbuf_bo); ++ &evo->pushbuf_bo); + if (ret == 0) + ret = nouveau_bo_pin(evo->pushbuf_bo, TTM_PL_FLAG_VRAM); + if (ret) { +@@ -138,8 +142,8 @@ + } + + /* bind primary evo channel's ramht to the channel */ +- if (dev_priv->evo && evo != dev_priv->evo) +- nouveau_ramht_ref(dev_priv->evo->ramht, &evo->ramht, NULL); ++ if (disp->master && evo != disp->master) ++ nouveau_ramht_ref(disp->master->ramht, &evo->ramht, NULL); + + return 0; + } +@@ -212,21 +216,39 @@ + } + } + ++static void ++nv50_evo_destroy(struct drm_device *dev) ++{ ++ struct nv50_display *disp = nv50_display(dev); ++ int i; ++ ++ for (i = 0; i < 2; i++) { ++ if (disp->crtc[i].sem.bo) { ++ nouveau_bo_unmap(disp->crtc[i].sem.bo); ++ nouveau_bo_ref(NULL, &disp->crtc[i].sem.bo); ++ } ++ nv50_evo_channel_del(&disp->crtc[i].sync); ++ } ++ nouveau_gpuobj_ref(NULL, &disp->ntfy); ++ nv50_evo_channel_del(&disp->master); ++} ++ + static int + nv50_evo_create(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nv50_display *disp = nv50_display(dev); + struct nouveau_gpuobj *ramht = NULL; + struct nouveau_channel *evo; +- int ret; ++ int ret, i, j; + + /* create primary evo channel, the one we use for modesetting + * purporses + */ +- ret = nv50_evo_channel_new(dev, &dev_priv->evo); ++ ret = nv50_evo_channel_new(dev, 0, &disp->master); + if (ret) + return ret; +- evo = dev_priv->evo; ++ evo = disp->master; + + /* setup object management on it, any other evo channel will + * use this also as there's no per-channel support on the +@@ -236,109 +258,167 @@ + NVOBJ_FLAG_ZERO_ALLOC, &evo->ramin); + if (ret) { + NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret); +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; ++ goto err; + } + + ret = drm_mm_init(&evo->ramin_heap, 0, 32768); + if (ret) { + NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret); +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; ++ goto err; + } + + ret = nouveau_gpuobj_new(dev, evo, 4096, 16, 0, &ramht); + if (ret) { + NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret); +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; ++ goto err; + } + + ret = nouveau_ramht_new(dev, ramht, &evo->ramht); + nouveau_gpuobj_ref(NULL, &ramht); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } ++ if (ret) ++ goto err; ++ ++ /* not sure exactly what this is.. ++ * ++ * the first dword of the structure is used by nvidia to wait on ++ * full completion of an EVO "update" command. ++ * ++ * method 0x8c on the master evo channel will fill a lot more of ++ * this structure with some undefined info ++ */ ++ ret = nouveau_gpuobj_new(dev, disp->master, 0x1000, 0, ++ NVOBJ_FLAG_ZERO_ALLOC, &disp->ntfy); ++ if (ret) ++ goto err; ++ ++ ret = nv50_evo_dmaobj_new(disp->master, NvEvoSync, 0x0000, ++ disp->ntfy->vinst, disp->ntfy->size, NULL); ++ if (ret) ++ goto err; + + /* create some default objects for the scanout memtypes we support */ +- if (dev_priv->card_type >= NV_C0) { +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0xfe, 0x19, +- 0, 0xffffffff, 0x00000000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } ++ ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM, 0x0000, ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; + +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM, 0, 0x19, +- 0, dev_priv->vram_size, 0x00020000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } ++ ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM_LP, 0x80000000, ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; + +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM_LP, 0, 0x19, +- 0, dev_priv->vram_size, 0x00000000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } +- } else { +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB16, 0x70, 0x19, +- 0, 0xffffffff, 0x00010000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } ++ ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB32, 0x80000000 | ++ (dev_priv->chipset < 0xc0 ? 0x7a00 : 0xfe00), ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; + ++ ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB16, 0x80000000 | ++ (dev_priv->chipset < 0xc0 ? 0x7000 : 0xfe00), ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; + +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0x7a, 0x19, +- 0, 0xffffffff, 0x00010000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } ++ /* create "display sync" channels and other structures we need ++ * to implement page flipping ++ */ ++ for (i = 0; i < 2; i++) { ++ struct nv50_display_crtc *dispc = &disp->crtc[i]; ++ u64 offset; + +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM, 0, 0x19, +- 0, dev_priv->vram_size, 0x00010000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; +- } ++ ret = nv50_evo_channel_new(dev, 1 + i, &dispc->sync); ++ if (ret) ++ goto err; + +- ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM_LP, 0, 0x19, +- 0, dev_priv->vram_size, 0x00010000); +- if (ret) { +- nv50_evo_channel_del(&dev_priv->evo); +- return ret; ++ ret = nouveau_bo_new(dev, NULL, 4096, 0x1000, TTM_PL_FLAG_VRAM, ++ 0, 0x0000, &dispc->sem.bo); ++ if (!ret) { ++ offset = dispc->sem.bo->bo.mem.start << PAGE_SHIFT; ++ ++ ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM); ++ if (!ret) ++ ret = nouveau_bo_map(dispc->sem.bo); ++ if (ret) ++ nouveau_bo_ref(NULL, &dispc->sem.bo); + } ++ ++ if (ret) ++ goto err; ++ ++ ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoSync, 0x0000, ++ offset, 4096, NULL); ++ if (ret) ++ goto err; ++ ++ ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoVRAM_LP, 0x80000000, ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; ++ ++ ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB32, 0x80000000 | ++ (dev_priv->chipset < 0xc0 ? ++ 0x7a00 : 0xfe00), ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; ++ ++ ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB16, 0x80000000 | ++ (dev_priv->chipset < 0xc0 ? ++ 0x7000 : 0xfe00), ++ 0, dev_priv->vram_size, NULL); ++ if (ret) ++ goto err; ++ ++ for (j = 0; j < 4096; j += 4) ++ nouveau_bo_wr32(dispc->sem.bo, j / 4, 0x74b1e000); ++ dispc->sem.offset = 0; + } + + return 0; ++ ++err: ++ nv50_evo_destroy(dev); ++ return ret; + } + + int + nv50_evo_init(struct drm_device *dev) + { +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- int ret; ++ struct nv50_display *disp = nv50_display(dev); ++ int ret, i; + +- if (!dev_priv->evo) { ++ if (!disp->master) { + ret = nv50_evo_create(dev); + if (ret) + return ret; + } + +- return nv50_evo_channel_init(dev_priv->evo); ++ ret = nv50_evo_channel_init(disp->master); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < 2; i++) { ++ ret = nv50_evo_channel_init(disp->crtc[i].sync); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; + } + + void + nv50_evo_fini(struct drm_device *dev) + { +- struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nv50_display *disp = nv50_display(dev); ++ int i; + +- if (dev_priv->evo) { +- nv50_evo_channel_fini(dev_priv->evo); +- nv50_evo_channel_del(&dev_priv->evo); ++ for (i = 0; i < 2; i++) { ++ if (disp->crtc[i].sync) ++ nv50_evo_channel_fini(disp->crtc[i].sync); + } ++ ++ if (disp->master) ++ nv50_evo_channel_fini(disp->master); ++ ++ nv50_evo_destroy(dev); + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_evo.h linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_evo.h +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_evo.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_evo.h 2011-03-09 18:30:58.000000000 +0100 +@@ -27,12 +27,6 @@ + #ifndef __NV50_EVO_H__ + #define __NV50_EVO_H__ + +-int nv50_evo_init(struct drm_device *dev); +-void nv50_evo_fini(struct drm_device *dev); +-int nv50_evo_dmaobj_new(struct nouveau_channel *, u32 class, u32 name, +- u32 tile_flags, u32 magic_flags, +- u32 offset, u32 limit); +- + #define NV50_EVO_UPDATE 0x00000080 + #define NV50_EVO_UNK84 0x00000084 + #define NV50_EVO_UNK84_NOTIFY 0x40000000 +@@ -119,5 +113,7 @@ + /* Both of these are needed, otherwise nothing happens. */ + #define NV50_EVO_CRTC_SCALE_RES1 0x000008d8 + #define NV50_EVO_CRTC_SCALE_RES2 0x000008dc ++#define NV50_EVO_CRTC_UNK900 0x00000900 ++#define NV50_EVO_CRTC_UNK904 0x00000904 + + #endif +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_fb.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_fb.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_fb.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_fb.c 2011-03-09 18:30:58.000000000 +0100 +@@ -8,31 +8,61 @@ + dma_addr_t r100c08; + }; + ++static void ++nv50_fb_destroy(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; ++ struct nv50_fb_priv *priv = pfb->priv; ++ ++ if (pfb->tag_heap.free_stack.next) ++ drm_mm_takedown(&pfb->tag_heap); ++ ++ if (priv->r100c08_page) { ++ pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE, ++ PCI_DMA_BIDIRECTIONAL); ++ __free_page(priv->r100c08_page); ++ } ++ ++ kfree(priv); ++ pfb->priv = NULL; ++} ++ + static int + nv50_fb_create(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + struct nv50_fb_priv *priv; ++ u32 tagmem; ++ int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; ++ pfb->priv = priv; + + priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!priv->r100c08_page) { +- kfree(priv); ++ nv50_fb_destroy(dev); + return -ENOMEM; + } + + priv->r100c08 = pci_map_page(dev->pdev, priv->r100c08_page, 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(dev->pdev, priv->r100c08)) { +- __free_page(priv->r100c08_page); +- kfree(priv); ++ nv50_fb_destroy(dev); + return -EFAULT; + } + +- dev_priv->engine.fb.priv = priv; ++ tagmem = nv_rd32(dev, 0x100320); ++ NV_DEBUG(dev, "%d tags available\n", tagmem); ++ ret = drm_mm_init(&pfb->tag_heap, 0, tagmem); ++ if (ret) { ++ nv50_fb_destroy(dev); ++ return ret; ++ } ++ + return 0; + } + +@@ -81,26 +111,112 @@ + void + nv50_fb_takedown(struct drm_device *dev) + { +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nv50_fb_priv *priv; ++ nv50_fb_destroy(dev); ++} + +- priv = dev_priv->engine.fb.priv; +- if (!priv) +- return; +- dev_priv->engine.fb.priv = NULL; ++static struct nouveau_enum vm_dispatch_subclients[] = { ++ { 0x00000000, "GRCTX", NULL }, ++ { 0x00000001, "NOTIFY", NULL }, ++ { 0x00000002, "QUERY", NULL }, ++ { 0x00000003, "COND", NULL }, ++ { 0x00000004, "M2M_IN", NULL }, ++ { 0x00000005, "M2M_OUT", NULL }, ++ { 0x00000006, "M2M_NOTIFY", NULL }, ++ {} ++}; + +- pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE, +- PCI_DMA_BIDIRECTIONAL); +- __free_page(priv->r100c08_page); +- kfree(priv); +-} ++static struct nouveau_enum vm_ccache_subclients[] = { ++ { 0x00000000, "CB", NULL }, ++ { 0x00000001, "TIC", NULL }, ++ { 0x00000002, "TSC", NULL }, ++ {} ++}; ++ ++static struct nouveau_enum vm_prop_subclients[] = { ++ { 0x00000000, "RT0", NULL }, ++ { 0x00000001, "RT1", NULL }, ++ { 0x00000002, "RT2", NULL }, ++ { 0x00000003, "RT3", NULL }, ++ { 0x00000004, "RT4", NULL }, ++ { 0x00000005, "RT5", NULL }, ++ { 0x00000006, "RT6", NULL }, ++ { 0x00000007, "RT7", NULL }, ++ { 0x00000008, "ZETA", NULL }, ++ { 0x00000009, "LOCAL", NULL }, ++ { 0x0000000a, "GLOBAL", NULL }, ++ { 0x0000000b, "STACK", NULL }, ++ { 0x0000000c, "DST2D", NULL }, ++ {} ++}; ++ ++static struct nouveau_enum vm_pfifo_subclients[] = { ++ { 0x00000000, "PUSHBUF", NULL }, ++ { 0x00000001, "SEMAPHORE", NULL }, ++ {} ++}; ++ ++static struct nouveau_enum vm_bar_subclients[] = { ++ { 0x00000000, "FB", NULL }, ++ { 0x00000001, "IN", NULL }, ++ {} ++}; ++ ++static struct nouveau_enum vm_client[] = { ++ { 0x00000000, "STRMOUT", NULL }, ++ { 0x00000003, "DISPATCH", vm_dispatch_subclients }, ++ { 0x00000004, "PFIFO_WRITE", NULL }, ++ { 0x00000005, "CCACHE", vm_ccache_subclients }, ++ { 0x00000006, "PPPP", NULL }, ++ { 0x00000007, "CLIPID", NULL }, ++ { 0x00000008, "PFIFO_READ", NULL }, ++ { 0x00000009, "VFETCH", NULL }, ++ { 0x0000000a, "TEXTURE", NULL }, ++ { 0x0000000b, "PROP", vm_prop_subclients }, ++ { 0x0000000c, "PVP", NULL }, ++ { 0x0000000d, "PBSP", NULL }, ++ { 0x0000000e, "PCRYPT", NULL }, ++ { 0x0000000f, "PCOUNTER", NULL }, ++ { 0x00000011, "PDAEMON", NULL }, ++ {} ++}; ++ ++static struct nouveau_enum vm_engine[] = { ++ { 0x00000000, "PGRAPH", NULL }, ++ { 0x00000001, "PVP", NULL }, ++ { 0x00000004, "PEEPHOLE", NULL }, ++ { 0x00000005, "PFIFO", vm_pfifo_subclients }, ++ { 0x00000006, "BAR", vm_bar_subclients }, ++ { 0x00000008, "PPPP", NULL }, ++ { 0x00000009, "PBSP", NULL }, ++ { 0x0000000a, "PCRYPT", NULL }, ++ { 0x0000000b, "PCOUNTER", NULL }, ++ { 0x0000000c, "SEMAPHORE_BG", NULL }, ++ { 0x0000000d, "PCOPY", NULL }, ++ { 0x0000000e, "PDAEMON", NULL }, ++ {} ++}; ++ ++static struct nouveau_enum vm_fault[] = { ++ { 0x00000000, "PT_NOT_PRESENT", NULL }, ++ { 0x00000001, "PT_TOO_SHORT", NULL }, ++ { 0x00000002, "PAGE_NOT_PRESENT", NULL }, ++ { 0x00000003, "PAGE_SYSTEM_ONLY", NULL }, ++ { 0x00000004, "PAGE_READ_ONLY", NULL }, ++ { 0x00000006, "NULL_DMAOBJ", NULL }, ++ { 0x00000007, "WRONG_MEMTYPE", NULL }, ++ { 0x0000000b, "VRAM_LIMIT", NULL }, ++ { 0x0000000f, "DMAOBJ_LIMIT", NULL }, ++ {} ++}; + + void +-nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name) ++nv50_fb_vm_trap(struct drm_device *dev, int display) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ const struct nouveau_enum *en, *cl; + unsigned long flags; + u32 trap[6], idx, chinst; ++ u8 st0, st1, st2, st3; + int i, ch; + + idx = nv_rd32(dev, 0x100c90); +@@ -117,8 +233,8 @@ + if (!display) + return; + ++ /* lookup channel id */ + chinst = (trap[2] << 16) | trap[1]; +- + spin_lock_irqsave(&dev_priv->channels.lock, flags); + for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { + struct nouveau_channel *chan = dev_priv->channels.ptr[ch]; +@@ -131,9 +247,48 @@ + } + spin_unlock_irqrestore(&dev_priv->channels.lock, flags); + +- NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x " +- "channel %d (0x%08x)\n", +- name, (trap[5] & 0x100 ? "read" : "write"), +- trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, +- trap[0], ch, chinst); ++ /* decode status bits into something more useful */ ++ if (dev_priv->chipset < 0xa3 || ++ dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) { ++ st0 = (trap[0] & 0x0000000f) >> 0; ++ st1 = (trap[0] & 0x000000f0) >> 4; ++ st2 = (trap[0] & 0x00000f00) >> 8; ++ st3 = (trap[0] & 0x0000f000) >> 12; ++ } else { ++ st0 = (trap[0] & 0x000000ff) >> 0; ++ st1 = (trap[0] & 0x0000ff00) >> 8; ++ st2 = (trap[0] & 0x00ff0000) >> 16; ++ st3 = (trap[0] & 0xff000000) >> 24; ++ } ++ ++ NV_INFO(dev, "VM: trapped %s at 0x%02x%04x%04x on ch %d [0x%08x] ", ++ (trap[5] & 0x00000100) ? "read" : "write", ++ trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, ch, chinst); ++ ++ en = nouveau_enum_find(vm_engine, st0); ++ if (en) ++ printk("%s/", en->name); ++ else ++ printk("%02x/", st0); ++ ++ cl = nouveau_enum_find(vm_client, st2); ++ if (cl) ++ printk("%s/", cl->name); ++ else ++ printk("%02x/", st2); ++ ++ if (cl && cl->data) cl = nouveau_enum_find(cl->data, st3); ++ else if (en && en->data) cl = nouveau_enum_find(en->data, st3); ++ else cl = NULL; ++ if (cl) ++ printk("%s", cl->name); ++ else ++ printk("%02x", st3); ++ ++ printk(" reason: "); ++ en = nouveau_enum_find(vm_fault, st1); ++ if (en) ++ printk("%s\n", en->name); ++ else ++ printk("0x%08x\n", st1); + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_fifo.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_fifo.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_fifo.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_fifo.c 2011-03-09 18:30:58.000000000 +0100 +@@ -149,6 +149,7 @@ + nv_wr32(dev, 0x3204, 0); + nv_wr32(dev, 0x3210, 0); + nv_wr32(dev, 0x3270, 0); ++ nv_wr32(dev, 0x2044, 0x01003fff); + + /* Enable dummy channels setup by nv50_instmem.c */ + nv50_fifo_channel_enable(dev, 0); +@@ -273,7 +274,7 @@ + nv_wo32(ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | + (4 << 24) /* SEARCH_FULL */ | + (chan->ramht->gpuobj->cinst >> 4)); +- nv_wo32(ramfc, 0x44, 0x2101ffff); ++ nv_wo32(ramfc, 0x44, 0x01003fff); + nv_wo32(ramfc, 0x60, 0x7fffffff); + nv_wo32(ramfc, 0x40, 0x00000000); + nv_wo32(ramfc, 0x7c, 0x30000001); +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_gpio.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_gpio.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_gpio.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_gpio.c 2011-03-09 18:30:58.000000000 +0100 +@@ -137,6 +137,7 @@ + struct nv50_gpio_priv *priv = pgpio->priv; + struct nv50_gpio_handler *gpioh, *tmp; + struct dcb_gpio_entry *gpio; ++ LIST_HEAD(tofree); + unsigned long flags; + + gpio = nouveau_bios_gpio_entry(dev, tag); +@@ -149,10 +150,14 @@ + gpioh->handler != handler || + gpioh->data != data) + continue; +- list_del(&gpioh->head); +- kfree(gpioh); ++ list_move(&gpioh->head, &tofree); + } + spin_unlock_irqrestore(&priv->lock, flags); ++ ++ list_for_each_entry_safe(gpioh, tmp, &tofree, head) { ++ flush_work_sync(&gpioh->work); ++ kfree(gpioh); ++ } + } + + bool +@@ -205,7 +210,6 @@ + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; +- struct nv50_gpio_priv *priv; + int ret; + + if (!pgpio->priv) { +@@ -213,7 +217,6 @@ + if (ret) + return ret; + } +- priv = pgpio->priv; + + /* disable, and ack any pending gpio interrupts */ + nv_wr32(dev, 0xe050, 0x00000000); +@@ -293,7 +296,7 @@ + continue; + gpioh->inhibit = true; + +- queue_work(dev_priv->wq, &gpioh->work); ++ schedule_work(&gpioh->work); + } + spin_unlock(&priv->lock); + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_graph.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_graph.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_graph.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_graph.c 2011-03-09 18:30:58.000000000 +0100 +@@ -95,13 +95,41 @@ + } + + static void +-nv50_graph_init_regs(struct drm_device *dev) ++nv50_graph_init_zcull(struct drm_device *dev) + { ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ int i; ++ + NV_DEBUG(dev, "\n"); + +- nv_wr32(dev, NV04_PGRAPH_DEBUG_3, +- (1 << 2) /* HW_CONTEXT_SWITCH_ENABLED */); +- nv_wr32(dev, 0x402ca8, 0x800); ++ switch (dev_priv->chipset & 0xf0) { ++ case 0x50: ++ case 0x80: ++ case 0x90: ++ nv_wr32(dev, 0x402ca8, 0x00000800); ++ break; ++ case 0xa0: ++ default: ++ nv_wr32(dev, 0x402cc0, 0x00000000); ++ if (dev_priv->chipset == 0xa0 || ++ dev_priv->chipset == 0xaa || ++ dev_priv->chipset == 0xac) { ++ nv_wr32(dev, 0x402ca8, 0x00000802); ++ } else { ++ nv_wr32(dev, 0x402cc0, 0x00000000); ++ nv_wr32(dev, 0x402ca8, 0x00000002); ++ } ++ ++ break; ++ } ++ ++ /* zero out zcull regions */ ++ for (i = 0; i < 8; i++) { ++ nv_wr32(dev, 0x402c20 + (i * 8), 0x00000000); ++ nv_wr32(dev, 0x402c24 + (i * 8), 0x00000000); ++ nv_wr32(dev, 0x402c28 + (i * 8), 0x00000000); ++ nv_wr32(dev, 0x402c2c + (i * 8), 0x00000000); ++ } + } + + static int +@@ -136,6 +164,7 @@ + } + kfree(cp); + ++ nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */ + nv_wr32(dev, 0x400320, 4); + nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0); + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0); +@@ -151,7 +180,7 @@ + + nv50_graph_init_reset(dev); + nv50_graph_init_regs__nv(dev); +- nv50_graph_init_regs(dev); ++ nv50_graph_init_zcull(dev); + + ret = nv50_graph_init_ctxctl(dev); + if (ret) +@@ -409,12 +438,7 @@ + nv50_graph_nvsw_mthd_page_flip(struct nouveau_channel *chan, + u32 class, u32 mthd, u32 data) + { +- struct nouveau_page_flip_state s; +- +- if (!nouveau_finish_page_flip(chan, &s)) { +- /* XXX - Do something here */ +- } +- ++ nouveau_finish_page_flip(chan, NULL); + return 0; + } + +@@ -526,11 +550,11 @@ + + static struct nouveau_enum nv50_mp_exec_error_names[] = + { +- { 3, "STACK_UNDERFLOW" }, +- { 4, "QUADON_ACTIVE" }, +- { 8, "TIMEOUT" }, +- { 0x10, "INVALID_OPCODE" }, +- { 0x40, "BREAKPOINT" }, ++ { 3, "STACK_UNDERFLOW", NULL }, ++ { 4, "QUADON_ACTIVE", NULL }, ++ { 8, "TIMEOUT", NULL }, ++ { 0x10, "INVALID_OPCODE", NULL }, ++ { 0x40, "BREAKPOINT", NULL }, + {} + }; + +@@ -558,47 +582,47 @@ + + /* There must be a *lot* of these. Will take some time to gather them up. */ + struct nouveau_enum nv50_data_error_names[] = { +- { 0x00000003, "INVALID_QUERY_OR_TEXTURE" }, +- { 0x00000004, "INVALID_VALUE" }, +- { 0x00000005, "INVALID_ENUM" }, +- { 0x00000008, "INVALID_OBJECT" }, +- { 0x00000009, "READ_ONLY_OBJECT" }, +- { 0x0000000a, "SUPERVISOR_OBJECT" }, +- { 0x0000000b, "INVALID_ADDRESS_ALIGNMENT" }, +- { 0x0000000c, "INVALID_BITFIELD" }, +- { 0x0000000d, "BEGIN_END_ACTIVE" }, +- { 0x0000000e, "SEMANTIC_COLOR_BACK_OVER_LIMIT" }, +- { 0x0000000f, "VIEWPORT_ID_NEEDS_GP" }, +- { 0x00000010, "RT_DOUBLE_BIND" }, +- { 0x00000011, "RT_TYPES_MISMATCH" }, +- { 0x00000012, "RT_LINEAR_WITH_ZETA" }, +- { 0x00000015, "FP_TOO_FEW_REGS" }, +- { 0x00000016, "ZETA_FORMAT_CSAA_MISMATCH" }, +- { 0x00000017, "RT_LINEAR_WITH_MSAA" }, +- { 0x00000018, "FP_INTERPOLANT_START_OVER_LIMIT" }, +- { 0x00000019, "SEMANTIC_LAYER_OVER_LIMIT" }, +- { 0x0000001a, "RT_INVALID_ALIGNMENT" }, +- { 0x0000001b, "SAMPLER_OVER_LIMIT" }, +- { 0x0000001c, "TEXTURE_OVER_LIMIT" }, +- { 0x0000001e, "GP_TOO_MANY_OUTPUTS" }, +- { 0x0000001f, "RT_BPP128_WITH_MS8" }, +- { 0x00000021, "Z_OUT_OF_BOUNDS" }, +- { 0x00000023, "XY_OUT_OF_BOUNDS" }, +- { 0x00000027, "CP_MORE_PARAMS_THAN_SHARED" }, +- { 0x00000028, "CP_NO_REG_SPACE_STRIPED" }, +- { 0x00000029, "CP_NO_REG_SPACE_PACKED" }, +- { 0x0000002a, "CP_NOT_ENOUGH_WARPS" }, +- { 0x0000002b, "CP_BLOCK_SIZE_MISMATCH" }, +- { 0x0000002c, "CP_NOT_ENOUGH_LOCAL_WARPS" }, +- { 0x0000002d, "CP_NOT_ENOUGH_STACK_WARPS" }, +- { 0x0000002e, "CP_NO_BLOCKDIM_LATCH" }, +- { 0x00000031, "ENG2D_FORMAT_MISMATCH" }, +- { 0x0000003f, "PRIMITIVE_ID_NEEDS_GP" }, +- { 0x00000044, "SEMANTIC_VIEWPORT_OVER_LIMIT" }, +- { 0x00000045, "SEMANTIC_COLOR_FRONT_OVER_LIMIT" }, +- { 0x00000046, "LAYER_ID_NEEDS_GP" }, +- { 0x00000047, "SEMANTIC_CLIP_OVER_LIMIT" }, +- { 0x00000048, "SEMANTIC_PTSZ_OVER_LIMIT" }, ++ { 0x00000003, "INVALID_QUERY_OR_TEXTURE", NULL }, ++ { 0x00000004, "INVALID_VALUE", NULL }, ++ { 0x00000005, "INVALID_ENUM", NULL }, ++ { 0x00000008, "INVALID_OBJECT", NULL }, ++ { 0x00000009, "READ_ONLY_OBJECT", NULL }, ++ { 0x0000000a, "SUPERVISOR_OBJECT", NULL }, ++ { 0x0000000b, "INVALID_ADDRESS_ALIGNMENT", NULL }, ++ { 0x0000000c, "INVALID_BITFIELD", NULL }, ++ { 0x0000000d, "BEGIN_END_ACTIVE", NULL }, ++ { 0x0000000e, "SEMANTIC_COLOR_BACK_OVER_LIMIT", NULL }, ++ { 0x0000000f, "VIEWPORT_ID_NEEDS_GP", NULL }, ++ { 0x00000010, "RT_DOUBLE_BIND", NULL }, ++ { 0x00000011, "RT_TYPES_MISMATCH", NULL }, ++ { 0x00000012, "RT_LINEAR_WITH_ZETA", NULL }, ++ { 0x00000015, "FP_TOO_FEW_REGS", NULL }, ++ { 0x00000016, "ZETA_FORMAT_CSAA_MISMATCH", NULL }, ++ { 0x00000017, "RT_LINEAR_WITH_MSAA", NULL }, ++ { 0x00000018, "FP_INTERPOLANT_START_OVER_LIMIT", NULL }, ++ { 0x00000019, "SEMANTIC_LAYER_OVER_LIMIT", NULL }, ++ { 0x0000001a, "RT_INVALID_ALIGNMENT", NULL }, ++ { 0x0000001b, "SAMPLER_OVER_LIMIT", NULL }, ++ { 0x0000001c, "TEXTURE_OVER_LIMIT", NULL }, ++ { 0x0000001e, "GP_TOO_MANY_OUTPUTS", NULL }, ++ { 0x0000001f, "RT_BPP128_WITH_MS8", NULL }, ++ { 0x00000021, "Z_OUT_OF_BOUNDS", NULL }, ++ { 0x00000023, "XY_OUT_OF_BOUNDS", NULL }, ++ { 0x00000027, "CP_MORE_PARAMS_THAN_SHARED", NULL }, ++ { 0x00000028, "CP_NO_REG_SPACE_STRIPED", NULL }, ++ { 0x00000029, "CP_NO_REG_SPACE_PACKED", NULL }, ++ { 0x0000002a, "CP_NOT_ENOUGH_WARPS", NULL }, ++ { 0x0000002b, "CP_BLOCK_SIZE_MISMATCH", NULL }, ++ { 0x0000002c, "CP_NOT_ENOUGH_LOCAL_WARPS", NULL }, ++ { 0x0000002d, "CP_NOT_ENOUGH_STACK_WARPS", NULL }, ++ { 0x0000002e, "CP_NO_BLOCKDIM_LATCH", NULL }, ++ { 0x00000031, "ENG2D_FORMAT_MISMATCH", NULL }, ++ { 0x0000003f, "PRIMITIVE_ID_NEEDS_GP", NULL }, ++ { 0x00000044, "SEMANTIC_VIEWPORT_OVER_LIMIT", NULL }, ++ { 0x00000045, "SEMANTIC_COLOR_FRONT_OVER_LIMIT", NULL }, ++ { 0x00000046, "LAYER_ID_NEEDS_GP", NULL }, ++ { 0x00000047, "SEMANTIC_CLIP_OVER_LIMIT", NULL }, ++ { 0x00000048, "SEMANTIC_PTSZ_OVER_LIMIT", NULL }, + {} + }; + +@@ -678,7 +702,6 @@ + tps++; + switch (type) { + case 6: /* texture error... unknown for now */ +- nv50_fb_vm_trap(dev, display, name); + if (display) { + NV_ERROR(dev, "magic set %d:\n", i); + for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4) +@@ -701,7 +724,6 @@ + uint32_t e1c = nv_rd32(dev, ustatus_addr + 0x14); + uint32_t e20 = nv_rd32(dev, ustatus_addr + 0x18); + uint32_t e24 = nv_rd32(dev, ustatus_addr + 0x1c); +- nv50_fb_vm_trap(dev, display, name); + /* 2d engine destination */ + if (ustatus & 0x00000010) { + if (display) { +@@ -912,10 +934,10 @@ + printk("\n"); + NV_INFO(dev, "PGRAPH - TRAP_CCACHE %08x %08x %08x %08x" + " %08x %08x %08x\n", +- nv_rd32(dev, 0x405800), nv_rd32(dev, 0x405804), +- nv_rd32(dev, 0x405808), nv_rd32(dev, 0x40580c), +- nv_rd32(dev, 0x405810), nv_rd32(dev, 0x405814), +- nv_rd32(dev, 0x40581c)); ++ nv_rd32(dev, 0x405000), nv_rd32(dev, 0x405004), ++ nv_rd32(dev, 0x405008), nv_rd32(dev, 0x40500c), ++ nv_rd32(dev, 0x405010), nv_rd32(dev, 0x405014), ++ nv_rd32(dev, 0x40501c)); + + } + +@@ -1044,6 +1066,7 @@ + NV_INFO(dev, "PGRAPH - ch %d (0x%010llx) subc %d " + "class 0x%04x mthd 0x%04x data 0x%08x\n", + chid, inst, subc, class, mthd, data); ++ nv50_fb_vm_trap(dev, 1); + } + } + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_instmem.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_instmem.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_instmem.c 2011-03-12 22:37:55.428744375 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_instmem.c 2011-03-09 18:30:58.000000000 +0100 +@@ -300,7 +300,7 @@ + } + + struct nv50_gpuobj_node { +- struct nouveau_vram *vram; ++ struct nouveau_mem *vram; + struct nouveau_vma chan_vma; + u32 align; + }; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_sor.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_sor.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_sor.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_sor.c 2011-03-09 18:30:58.000000000 +0100 +@@ -41,8 +41,7 @@ + { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(dev)->master; + int ret; + + if (!nv_encoder->crtc) +@@ -184,8 +183,7 @@ + nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) + { +- struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; +- struct nouveau_channel *evo = dev_priv->evo; ++ struct nouveau_channel *evo = nv50_display(encoder->dev)->master; + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_vm.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_vm.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_vm.c 2011-03-12 22:37:55.430744335 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_vm.c 2011-03-09 18:30:58.000000000 +0100 +@@ -31,7 +31,6 @@ + nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde, + struct nouveau_gpuobj *pgt[2]) + { +- struct drm_nouveau_private *dev_priv = pgd->dev->dev_private; + u64 phys = 0xdeadcafe00000000ULL; + u32 coverage = 0; + +@@ -58,10 +57,9 @@ + } + + static inline u64 +-nv50_vm_addr(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, +- u64 phys, u32 memtype, u32 target) ++nv50_vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target) + { +- struct drm_nouveau_private *dev_priv = pgt->dev->dev_private; ++ struct drm_nouveau_private *dev_priv = vma->vm->dev->dev_private; + + phys |= 1; /* present */ + phys |= (u64)memtype << 40; +@@ -85,12 +83,13 @@ + + void + nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, +- struct nouveau_vram *mem, u32 pte, u32 cnt, u64 phys) ++ struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) + { ++ u32 comp = (mem->memtype & 0x180) >> 7; + u32 block; + int i; + +- phys = nv50_vm_addr(vma, pgt, phys, mem->memtype, 0); ++ phys = nv50_vm_addr(vma, phys, mem->memtype, 0); + pte <<= 3; + cnt <<= 3; + +@@ -107,6 +106,11 @@ + + phys += block << (vma->node->type - 3); + cnt -= block; ++ if (comp) { ++ u32 tag = mem->tag->start + ((delta >> 16) * comp); ++ offset_h |= (tag << 17); ++ delta += block << (vma->node->type - 3); ++ } + + while (block) { + nv_wo32(pgt, pte + 0, offset_l); +@@ -119,11 +123,11 @@ + + void + nv50_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, +- u32 pte, dma_addr_t *list, u32 cnt) ++ struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) + { + pte <<= 3; + while (cnt--) { +- u64 phys = nv50_vm_addr(vma, pgt, (u64)*list++, 0, 2); ++ u64 phys = nv50_vm_addr(vma, (u64)*list++, mem->memtype, 2); + nv_wo32(pgt, pte + 0, lower_32_bits(phys)); + nv_wo32(pgt, pte + 4, upper_32_bits(phys)); + pte += 8; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_vram.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_vram.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv50_vram.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv50_vram.c 2011-03-09 18:30:58.000000000 +0100 +@@ -48,42 +48,49 @@ + } + + void +-nv50_vram_del(struct drm_device *dev, struct nouveau_vram **pvram) ++nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; + struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM]; + struct nouveau_mm *mm = man->priv; + struct nouveau_mm_node *this; +- struct nouveau_vram *vram; ++ struct nouveau_mem *mem; + +- vram = *pvram; +- *pvram = NULL; +- if (unlikely(vram == NULL)) ++ mem = *pmem; ++ *pmem = NULL; ++ if (unlikely(mem == NULL)) + return; + + mutex_lock(&mm->mutex); +- while (!list_empty(&vram->regions)) { +- this = list_first_entry(&vram->regions, struct nouveau_mm_node, rl_entry); ++ while (!list_empty(&mem->regions)) { ++ this = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); + + list_del(&this->rl_entry); + nouveau_mm_put(mm, this); + } ++ ++ if (mem->tag) { ++ drm_mm_put_block(mem->tag); ++ mem->tag = NULL; ++ } + mutex_unlock(&mm->mutex); + +- kfree(vram); ++ kfree(mem); + } + + int + nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc, +- u32 type, struct nouveau_vram **pvram) ++ u32 memtype, struct nouveau_mem **pmem) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; + struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM]; + struct nouveau_mm *mm = man->priv; + struct nouveau_mm_node *r; +- struct nouveau_vram *vram; ++ struct nouveau_mem *mem; ++ int comp = (memtype & 0x300) >> 8; ++ int type = (memtype & 0x07f); + int ret; + + if (!types[type]) +@@ -92,32 +99,46 @@ + align >>= 12; + size_nc >>= 12; + +- vram = kzalloc(sizeof(*vram), GFP_KERNEL); +- if (!vram) ++ mem = kzalloc(sizeof(*mem), GFP_KERNEL); ++ if (!mem) + return -ENOMEM; + +- INIT_LIST_HEAD(&vram->regions); +- vram->dev = dev_priv->dev; +- vram->memtype = type; +- vram->size = size; +- + mutex_lock(&mm->mutex); ++ if (comp) { ++ if (align == 16) { ++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; ++ int n = (size >> 4) * comp; ++ ++ mem->tag = drm_mm_search_free(&pfb->tag_heap, n, 0, 0); ++ if (mem->tag) ++ mem->tag = drm_mm_get_block(mem->tag, n, 0); ++ } ++ ++ if (unlikely(!mem->tag)) ++ comp = 0; ++ } ++ ++ INIT_LIST_HEAD(&mem->regions); ++ mem->dev = dev_priv->dev; ++ mem->memtype = (comp << 7) | type; ++ mem->size = size; ++ + do { + ret = nouveau_mm_get(mm, types[type], size, size_nc, align, &r); + if (ret) { + mutex_unlock(&mm->mutex); +- nv50_vram_del(dev, &vram); ++ nv50_vram_del(dev, &mem); + return ret; + } + +- list_add_tail(&r->rl_entry, &vram->regions); ++ list_add_tail(&r->rl_entry, &mem->regions); + size -= r->length; + } while (size); + mutex_unlock(&mm->mutex); + +- r = list_first_entry(&vram->regions, struct nouveau_mm_node, rl_entry); +- vram->offset = (u64)r->offset << 12; +- *pvram = vram; ++ r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); ++ mem->offset = (u64)r->offset << 12; ++ *pmem = mem; + return 0; + } + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv84_crypt.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv84_crypt.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nv84_crypt.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nv84_crypt.c 2011-03-09 18:30:58.000000000 +0100 +@@ -136,5 +136,5 @@ + nv_wr32(dev, 0x102130, stat); + nv_wr32(dev, 0x10200c, 0x10); + +- nv50_fb_vm_trap(dev, show, "PCRYPT"); ++ nv50_fb_vm_trap(dev, show); + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_fifo.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_fifo.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_fifo.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_fifo.c 2011-03-09 18:30:58.000000000 +0100 +@@ -116,7 +116,7 @@ + + /* allocate vram for control regs, map into polling area */ + ret = nouveau_bo_new(dev, NULL, 0x1000, 0, TTM_PL_FLAG_VRAM, +- 0, 0, true, true, &fifoch->user); ++ 0, 0, &fifoch->user); + if (ret) + goto error; + +@@ -418,6 +418,12 @@ + { + u32 stat = nv_rd32(dev, 0x002100); + ++ if (stat & 0x00000100) { ++ NV_INFO(dev, "PFIFO: unknown status 0x00000100\n"); ++ nv_wr32(dev, 0x002100, 0x00000100); ++ stat &= ~0x00000100; ++ } ++ + if (stat & 0x10000000) { + u32 units = nv_rd32(dev, 0x00259c); + u32 u = units; +@@ -446,10 +452,15 @@ + stat &= ~0x20000000; + } + ++ if (stat & 0x40000000) { ++ NV_INFO(dev, "PFIFO: unknown status 0x40000000\n"); ++ nv_mask(dev, 0x002a00, 0x00000000, 0x00000000); ++ stat &= ~0x40000000; ++ } ++ + if (stat) { + NV_INFO(dev, "PFIFO: unhandled status 0x%08x\n", stat); + nv_wr32(dev, 0x002100, stat); ++ nv_wr32(dev, 0x002140, 0); + } +- +- nv_wr32(dev, 0x2140, 0); + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_graph.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_graph.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_graph.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_graph.c 2011-03-09 18:30:58.000000000 +0100 +@@ -299,6 +299,14 @@ + } + + static int ++nvc0_graph_mthd_page_flip(struct nouveau_channel *chan, ++ u32 class, u32 mthd, u32 data) ++{ ++ nouveau_finish_page_flip(chan, NULL); ++ return 0; ++} ++ ++static int + nvc0_graph_create(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; +@@ -395,6 +403,7 @@ + nouveau_irq_register(dev, 25, nvc0_runk140_isr); + NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */ + NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */ ++ NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip); + NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */ + NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */ + return 0; +@@ -640,7 +649,6 @@ + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; +- struct nvc0_graph_priv *priv; + int ret; + + dev_priv->engine.graph.accel_blocked = true; +@@ -665,7 +673,6 @@ + if (ret) + return ret; + } +- priv = pgraph->priv; + + nvc0_graph_init_obj418880(dev); + nvc0_graph_init_regs(dev); +@@ -730,9 +737,12 @@ + u32 class = nv_rd32(dev, 0x404200 + (subc * 4)); + + if (stat & 0x00000010) { +- NV_INFO(dev, "PGRAPH: ILLEGAL_MTHD ch %d [0x%010llx] subc %d " +- "class 0x%04x mthd 0x%04x data 0x%08x\n", +- chid, inst, subc, class, mthd, data); ++ if (nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data)) { ++ NV_INFO(dev, "PGRAPH: ILLEGAL_MTHD ch %d [0x%010llx] " ++ "subc %d class 0x%04x mthd 0x%04x " ++ "data 0x%08x\n", ++ chid, inst, subc, class, mthd, data); ++ } + nv_wr32(dev, 0x400100, 0x00000010); + stat &= ~0x00000010; + } +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_vm.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_vm.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_vm.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_vm.c 2011-03-09 18:30:58.000000000 +0100 +@@ -59,7 +59,7 @@ + + void + nvc0_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, +- struct nouveau_vram *mem, u32 pte, u32 cnt, u64 phys) ++ struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta) + { + u32 next = 1 << (vma->node->type - 8); + +@@ -75,11 +75,11 @@ + + void + nvc0_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, +- u32 pte, dma_addr_t *list, u32 cnt) ++ struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) + { + pte <<= 3; + while (cnt--) { +- u64 phys = nvc0_vm_addr(vma, *list++, 0, 5); ++ u64 phys = nvc0_vm_addr(vma, *list++, mem->memtype, 5); + nv_wo32(pgt, pte + 0, lower_32_bits(phys)); + nv_wo32(pgt, pte + 4, upper_32_bits(phys)); + pte += 8; +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_vram.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_vram.c +--- linux-2.6.38-rc7/drivers/gpu/drm/nouveau/nvc0_vram.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/nouveau/nvc0_vram.c 2011-03-09 18:30:58.000000000 +0100 +@@ -26,64 +26,78 @@ + #include "nouveau_drv.h" + #include "nouveau_mm.h" + ++/* 0 = unsupported ++ * 1 = non-compressed ++ * 3 = compressed ++ */ ++static const u8 types[256] = { ++ 1, 1, 3, 3, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, ++ 0, 1, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, ++ 3, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 1, 1, 1, 1, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, ++ 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, ++ 3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 3, ++ 3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0, ++ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0 ++}; ++ + bool + nvc0_vram_flags_valid(struct drm_device *dev, u32 tile_flags) + { +- switch (tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK) { +- case 0x0000: +- case 0xfe00: +- case 0xdb00: +- case 0x1100: +- return true; +- default: +- break; +- } +- +- return false; ++ u8 memtype = (tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK) >> 8; ++ return likely((types[memtype] == 1)); + } + + int + nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin, +- u32 type, struct nouveau_vram **pvram) ++ u32 type, struct nouveau_mem **pmem) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; + struct ttm_mem_type_manager *man = &bdev->man[TTM_PL_VRAM]; + struct nouveau_mm *mm = man->priv; + struct nouveau_mm_node *r; +- struct nouveau_vram *vram; ++ struct nouveau_mem *mem; + int ret; + + size >>= 12; + align >>= 12; + ncmin >>= 12; + +- vram = kzalloc(sizeof(*vram), GFP_KERNEL); +- if (!vram) ++ mem = kzalloc(sizeof(*mem), GFP_KERNEL); ++ if (!mem) + return -ENOMEM; + +- INIT_LIST_HEAD(&vram->regions); +- vram->dev = dev_priv->dev; +- vram->memtype = type; +- vram->size = size; ++ INIT_LIST_HEAD(&mem->regions); ++ mem->dev = dev_priv->dev; ++ mem->memtype = (type & 0xff); ++ mem->size = size; + + mutex_lock(&mm->mutex); + do { + ret = nouveau_mm_get(mm, 1, size, ncmin, align, &r); + if (ret) { + mutex_unlock(&mm->mutex); +- nv50_vram_del(dev, &vram); ++ nv50_vram_del(dev, &mem); + return ret; + } + +- list_add_tail(&r->rl_entry, &vram->regions); ++ list_add_tail(&r->rl_entry, &mem->regions); + size -= r->length; + } while (size); + mutex_unlock(&mm->mutex); + +- r = list_first_entry(&vram->regions, struct nouveau_mm_node, rl_entry); +- vram->offset = (u64)r->offset << 12; +- *pvram = vram; ++ r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); ++ mem->offset = (u64)r->offset << 12; ++ *pmem = mem; + return 0; + } + +diff -Naur linux-2.6.38-rc7/drivers/gpu/drm/ttm/ttm_bo.c linux-2.6.38-rc7.nouveau/drivers/gpu/drm/ttm/ttm_bo.c +--- linux-2.6.38-rc7/drivers/gpu/drm/ttm/ttm_bo.c 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/drivers/gpu/drm/ttm/ttm_bo.c 2011-03-09 18:30:58.000000000 +0100 +@@ -406,11 +406,12 @@ + } + + if (bo->mem.mem_type == TTM_PL_SYSTEM) { ++ if (bdev->driver->move_notify) ++ bdev->driver->move_notify(bo, mem); + bo->mem = *mem; + mem->mm_node = NULL; + goto moved; + } +- + } + + if (bdev->driver->move_notify) +diff -Naur linux-2.6.38-rc7/include/drm/nouveau_drm.h linux-2.6.38-rc7.nouveau/include/drm/nouveau_drm.h +--- linux-2.6.38-rc7/include/drm/nouveau_drm.h 2011-03-01 22:55:12.000000000 +0100 ++++ linux-2.6.38-rc7.nouveau/include/drm/nouveau_drm.h 2011-03-09 18:30:58.000000000 +0100 +@@ -94,6 +94,7 @@ + #define NOUVEAU_GEM_DOMAIN_GART (1 << 2) + #define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3) + ++#define NOUVEAU_GEM_TILE_COMP 0x00030000 /* nv50-only */ + #define NOUVEAU_GEM_TILE_LAYOUT_MASK 0x0000ff00 + #define NOUVEAU_GEM_TILE_16BPP 0x00000001 + #define NOUVEAU_GEM_TILE_32BPP 0x00000002 diff --git a/packages/linux/patches/linux-2.6.38-716_mm-zero_swappiness.patch b/packages/linux/patches/linux-2.6.38-716_mm-zero_swappiness.patch new file mode 100644 index 0000000000..fad5b48936 --- /dev/null +++ b/packages/linux/patches/linux-2.6.38-716_mm-zero_swappiness.patch @@ -0,0 +1,17 @@ +--- + mm/vmscan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: linux-2.6.37-ck2/mm/vmscan.c +=================================================================== +--- linux-2.6.37-ck2.orig/mm/vmscan.c 2011-01-06 14:04:10.000000000 +1100 ++++ linux-2.6.37-ck2/mm/vmscan.c 2011-02-14 10:11:00.536252000 +1100 +@@ -133,7 +133,7 @@ + /* + * From 0 .. 100. Higher means more swappy. + */ +-int vm_swappiness = 60; ++int vm_swappiness; + long vm_total_pages; /* The total number of pages which the VM controls */ + + static LIST_HEAD(shrinker_list);