From: <mar...@us...> - 2006-10-14 07:15:54
|
Revision: 625 http://svn.sourceforge.net/hackndev/?rev=625&view=rev Author: marex_z71 Date: 2006-10-14 00:15:41 -0700 (Sat, 14 Oct 2006) Log Message: ----------- New PalmTC keyboard driver by Holger Bocklet Modified Paths: -------------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c Added Paths: ----------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c Removed Paths: ------------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_buttons.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_fbkeyb.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_tsc2101.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/tsc2101.h Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig 2006-10-14 07:15:41 UTC (rev 625) @@ -6,23 +6,10 @@ Palm Tungsten C. Currently there is only basic support for this PDA. -config PALMTC_BUTTONS - tristate "Palm Tungsten C buttons driver" +config PALMTC_KEYBOARD + tristate "Palm Tungsten C keyboard driver" depends on MACH_OMAP_PALMTC help This driver translates button presses on a Palm - Tungsten C to Linux input subsystem events, still non - functional. + Tungsten C to Linux input subsystem events -config PALMTC_TSC2101 - tristate "Support for TSC2101 touchscreen and audio" - depends on MACH_OMAP_PALMTC - help - Adds support for TSC2101 touchscreen and audio controller. This - code is hearly the same as soc/tsc2101 so is obsolete. - -config PALMTC_FBKEYBOARD - bool "On-screen keyboard (BROKEN)" - depends on PALMTC_TSC2101 - help - On-screen framebuffer keyboard Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile 2006-10-14 07:15:41 UTC (rev 625) @@ -3,5 +3,4 @@ # obj-$(CONFIG_MACH_OMAP_PALMTC) += palmtc.o -obj-$(CONFIG_PALMTC_BUTTONS) += palmtc_buttons.o -obj-$(CONFIG_PALMTC_TSC2101) += palmtc_tsc2101.o +obj-$(CONFIG_PALMTC_KEYBOARD) += palmtc_keyboard.o Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c 2006-10-14 07:15:41 UTC (rev 625) @@ -7,6 +7,7 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <asm/arch/audio.h> #include <asm/mach/arch.h> #include <asm/mach-types.h> #include <asm/arch/hardware.h> @@ -14,6 +15,7 @@ #include <asm/domain.h> #include <linux/device.h> +#include <linux/platform_device.h> #include <linux/fb.h> #include <asm/arch/pxa-dmabounce.h> @@ -54,9 +56,29 @@ .pxafb_backlight_power = palm_backlight_power, }; -static void __init palm_init(void) + + +static struct platform_device palmtc_audio_device = { + .name = "pxa2xx-ac97", + .id = -1, +}; + +static struct platform_device palmtc_keyboard_device = { + .name = "palmtc-kbd", + .id = -1, +}; + + +static struct platform_device *devices[] __initdata = { + &palmtc_audio_device, + &palmtc_keyboard_device, +}; + +static void __init palmtc_init(void) { set_pxa_fb_info(&palmtclcd); + GCR &= ~GCR_PRIRDY_IEN; + platform_add_devices (devices, ARRAY_SIZE (devices)); } MACHINE_START(OMAP_PALMTC, "Palm Tungsten C") @@ -67,6 +89,6 @@ .map_io = pxa_map_io, .init_irq = pxa_init_irq, .timer = &pxa_timer, - .init_machine = palm_init + .init_machine = palmtc_init MACHINE_END Deleted: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_buttons.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_buttons.c 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_buttons.c 2006-10-14 07:15:41 UTC (rev 625) @@ -1,113 +0,0 @@ -/* - * Palm TC hardware buttons - * - * Author: Chetan Kumar S<shi...@gm...> - * Based on Palm T3 hardware buttons - * - */ -#include <linux/module.h> -#include <linux/config.h> - -#include <linux/input.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <asm/irq.h> -#include <asm/mach/arch.h> -#include <asm/mach/map.h> -#include <asm/mach-types.h> -#include <asm/hardware.h> -#include <asm/arch/pxa-regs.h> -#include <asm/arch/irqs.h> -#include <linux/sched.h> -#include <linux/workqueue.h> - - - -#define GET_GPIO(gpio) (GPLR(gpio) & GPIO_bit(gpio)) - - -#define MY_WORK_QUEUE_NAME "WQsched.c" - -#define CURRGPIO 18 - -static struct workqueue_struct *my_workqueue; - -static void handle_gpio(void* irq) -{ - int gpn = (int)irq; - printk(KERN_ERR "palmtc: GPIO[%d] = %d\n", gpn, GET_GPIO(gpn)); -} - -irqreturn_t gpio_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - static int initialised = 0; - static struct work_struct task; - -printk(KERN_INFO "gpio_irq.\n"); - -//Not very optimized, need to check this sometime -Chetan - if (initialised == 0) { - INIT_WORK(&task, handle_gpio, dev_id); - initialised = 1; - } else { - PREPARE_WORK(&task, handle_gpio, dev_id); - } - - queue_work(my_workqueue, &task); - - return IRQ_HANDLED; -} - - -#define REG_GPIO(x) \ - ret = request_irq (IRQ_GPIO(x), gpio_irq, SA_SAMPLE_RANDOM, "Palmtc Buttons", (void*)x); \ - set_irq_type (IRQ_GPIO(x), IRQT_BOTHEDGE); \ - if(ret!=0) { \ - printk(KERN_INFO "Request GPIO: %d failed\n", x); \ - return 1; \ - } else { \ - printk(KERN_INFO "Registered GPIO %d\n", x); \ - } - -#define UNREG_GPIO(x) \ - free_irq(IRQ_GPIO(x),(void*)x); - -static int __init palmtc_btn_init(void) -{ - int ret; - printk(KERN_INFO "palmtc: init\n"); - printk(KERN_INFO "GPDR Dump:\n0: %X\n1: %X\n2: %X\n", GPDR0, GPDR1, GPDR2); - - - my_workqueue = create_workqueue(MY_WORK_QUEUE_NAME); - - REG_GPIO(0); - REG_GPIO(9); - REG_GPIO(10); - REG_GPIO(11); - - printk(KERN_INFO "palmtc: registered for irqs %d, %d, %d, %d\n", IRQ_GPIO(0), IRQ_GPIO(9), IRQ_GPIO(10), IRQ_GPIO(11)); - - -printk("Keypad register set done.........\n"); - - return 0; -} - -static void __exit palmtc_btn_cleanup(void) -{ - printk(KERN_INFO "tc buttons: unloading...\n"); - printk(KERN_INFO "Releasing irq\n"); - UNREG_GPIO(0); - UNREG_GPIO(9); - UNREG_GPIO(10); - UNREG_GPIO(11); - -} - -module_init(palmtc_btn_init); -module_exit(palmtc_btn_cleanup); - -MODULE_AUTHOR ("Chetan Kumar S <shi...@gm...>"); -MODULE_DESCRIPTION ("Button support for Palm Tungsten C"); -MODULE_LICENSE("GPL"); Deleted: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_fbkeyb.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_fbkeyb.c 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_fbkeyb.c 2006-10-14 07:15:41 UTC (rev 625) @@ -1,72 +0,0 @@ -/* - * On-screen keyboard in kernel space - * - * Author: Vladimir "Farcaller" Pouzanov <far...@gm...> - * - */ -#include <linux/module.h> -#include <linux/config.h> - -#include <linux/input.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <asm/irq.h> -#include <asm/mach/arch.h> -#include <asm/mach/map.h> -#include <asm/mach-types.h> -#include <asm/hardware.h> -#include <asm/arch/pxa-regs.h> -#include <asm/arch/irqs.h> -#include <linux/sched.h> -#include <linux/workqueue.h> - -#include <linux/fb.h> -#include <linux/vt_kern.h> - -static int resized; - -void drawkeyb(void) -{ - /* - struct fb_info *p; - struct vc_data *vc; - struct fb_fillrect rect; - int i; - unsigned long fg; - - if(resized<2) { - resized++; - vc = vc_cons[0].d; - vc_resize(vc, 40, 41); - } - p = registered_fb[0]; - - rect.dx = 0; - rect.width = 320; - rect.height = 2; - rect.color = 0x00FF00FF; - rect.rop = ROP_COPY; - for(i = 150; i < 320; i+=10) { - rect.dy = i; - cfb_fillrect(p, &rect); - } - if (p->fix.visual == FB_VISUAL_TRUECOLOR || p->fix.visual == FB_VISUAL_DIRECTCOLOR ) { - fg = ((u32 *) (p->pseudo_palette))[rect.color]; - printk(KERN_INFO "Direct col %lu\n", fg); - } else { - fg = rect.color; - printk(KERN_INFO "Indir col %lu\n", fg); - } - */ -} - -static int __init palmt3_fbk_init(void) -{ - printk(KERN_INFO "fbkeyb: init\n"); - resized = 0; - - return 0; -} - -module_init(palmt3_fbk_init); - Added: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c (rev 0) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c 2006-10-14 07:15:41 UTC (rev 625) @@ -0,0 +1,402 @@ +/* + * Palm TC hardware buttons + * + * Author: Holger Bocklet, Bit...@gm..., + * based on research by Chetan Kumar S<shi...@gm...> + * with bits and pieces from + * htcuniversal_kbd.c, (Milan Votava) + * palmt3_buttons.c, (Vladimir "Farcaller" Pouzanov <far...@gm...>, Martin Kupec) + * palmte2_keyboard, (Carlos Eduardo Medaglia Dyonisio <ca...@ne...>) + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/delay.h> + +#include <asm/mach/arch.h> +#include <asm/mach-types.h> +#include <asm/hardware.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/irqs.h> + +#define USE_RELEASE_TIMER +#define USE_DOUBLECLICK + +#define PALMTC_KEYBOARD_DEBUG + +#ifdef PALMTC_KEYBOARD_DEBUG +#define DBG(x...) \ + printk(KERN_INFO "Palmtc keyboard: " x) +#else +#define DBG(x...) do { } while (0) +#endif + +#define TRUE 1 +#define FALSE 0 +#define PERMANENT 2 + +#define MAX_ROW 12 +#define MAX_COL 4 + +#define SHIFT_BIT 0x80 +#define PRESSED_BIT 1 + +#define PALMTC_BLUEKEY -17 + +#define RELEASE_CHECK_TIME_MS 50 +#define DOUBLECLICK_TIME_MS 250 + +#define GET_GPIO(gpio) (GPLR(gpio) & GPIO_bit(gpio)) + +#define GET_KEY_BIT(vkey, bit) ((vkey >> (bit) ) & 0x01) +#define SET_KEY_BIT(vkey, bit) (vkey |= (1 << (bit)) ) +#define CLEAR_KEY_BIT(vkey, bit) (vkey &= ~(1 << (bit)) ) + +#ifdef USE_RELEASE_TIMER +static void release_timer_went_off (unsigned long); +static struct timer_list key_release_timer; +#endif + +/********************************************************** + * Key-matrix map german + * GPIO 0 9 10 11 + * 18 CALEND CONTACT MAIL WEB + * 19 x DOWN OPT OK + * 20 POWER + * 21 TAB d h . + * 22 a c z l + * 23 q f n o + * 24 SHIFT r j EXIT + * 25 y SPACE u CR + * 26 s v m BS + * 27 w g k p + * 79 e t i b + * 80 UP LEFT RIGHT BLUE + **********************************************************/ + +static u8 alternate_key=FALSE; +static int col_gpio[MAX_COL] = { 0, 9, 10, 11 }; +static u8 row_gpio[MAX_ROW] = {18,19,20,21,22,23,24,25,26,27,79,80}; + +static struct { // matrix for real buttons + int key; + int alt_key; + u8 flags; // for recording last status and shift_bit + unsigned long tstamp; // timestamp (jiffies) + char *desc; +} palmtc_buttons[MAX_ROW][MAX_COL] = { +// Gpio Column+IRQ +// german keymap +/*18*/{{KEY_F1,KEY_F5,FALSE,0,"CAL/F1"}, //0 + {KEY_F2,KEY_F6,FALSE,0,"CONTACT/F2"}, //9 + {KEY_F3,KEY_F7,FALSE,0,"MAIL/F3"}, //10 + {KEY_F4,KEY_F8,FALSE,0,"WEB/F4"} },//11 +/*19*/ {{KEY_X,KEY_9,SHIFT_BIT|FALSE,0,"X"}, + {KEY_DOWN,KEY_PAGEDOWN,FALSE,0,"down/pgdown"} , + {KEY_LEFTCTRL,KEY_F9,FALSE,0,"cmd/ctrl/f9"}, + {KEY_ENTER,-1,FALSE,0,"Select/enter/;"} }, //1 free +/*20*/ {{KEY_POWER,-1,FALSE,0,"power"}, //0 + {-1,-1,FALSE,0,"unused 9/20"}, //9 + {-1,-1,FALSE,0,"unused 10/20"}, //10 + {-1,-1,FALSE,0,"unused 11/20"} }, //11 +/*21*/ {{KEY_TAB,KEY_BACKSLASH,SHIFT_BIT|FALSE,0,"tab/|"}, + {KEY_D,KEY_KPPLUS,FALSE,0,"D"}, + {KEY_H,KEY_MINUS,SHIFT_BIT|FALSE,0,"H/_"}, + {KEY_DOT,KEY_SEMICOLON,SHIFT_BIT|FALSE,0,"./:"} }, +/*22*/ {{KEY_A,KEY_2,SHIFT_BIT|FALSE,0,"A/@"}, + {KEY_C,KEY_0,SHIFT_BIT|FALSE,0,"C/)"}, + {KEY_Z,KEY_6,FALSE,0,"de:Z us:Y"}, + {KEY_L,KEY_EQUAL,FALSE,0,"L/\xDF"} }, // Keyoard shows "\xDF" but better use "=" +/*23*/ {{KEY_Q,KEY_1,FALSE,0,"Q"}, //0 + {KEY_F,KEY_KPMINUS,FALSE,0,"F"}, //9 + {KEY_N,KEY_COMMA,FALSE,0,"N"}, //10 + {KEY_O,KEY_9,FALSE,0,"O"} },//11 +/*24*/ {{KEY_LEFTSHIFT,KEY_7,SHIFT_BIT|FALSE,0,"shift/&"}, + {KEY_R,KEY_4,FALSE,0,"R"}, + {KEY_J,KEY_SLASH,FALSE,0,"J"}, + {KEY_LEFTALT,KEY_F10,FALSE,0,"exit/alt"} }, +/*25*/ {{KEY_Y,KEY_4,SHIFT_BIT|FALSE,0,"de:Y us:Z / $"}, // dollar is more usefull + {KEY_SPACE,KEY_ESC,FALSE,0,"space/esc"}, + {KEY_U,KEY_7,FALSE,0,"U"}, + {KEY_ENTER,KEY_SEMICOLON,FALSE,0,"enter"} }, +/*26*/ {{KEY_S,KEY_APOSTROPHE,SHIFT_BIT|FALSE,0,"S"}, + {KEY_V,KEY_3,SHIFT_BIT|FALSE,0,"V/#"}, + {KEY_M,KEY_SLASH,SHIFT_BIT|FALSE,0,"M/?"}, + {KEY_BACKSPACE,KEY_GRAVE,SHIFT_BIT|FALSE,0,"bs/~"} }, +/*27*/ {{KEY_W,KEY_2,FALSE,0,"W"}, + {KEY_G,KEY_KPASTERISK,FALSE,0,"G"}, + {KEY_K,KEY_APOSTROPHE,FALSE,0,"K"}, + {KEY_P,KEY_0,FALSE,0,"P"} }, +/*79*/ {{KEY_E,KEY_3,FALSE,0,"E"}, + {KEY_T,KEY_5,FALSE,0,"T"}, + {KEY_I,KEY_8,FALSE,0,"I"}, + {KEY_B,KEY_1,SHIFT_BIT|FALSE,0,"B/!"} }, +/*80*/ {{KEY_UP,KEY_PAGEUP,FALSE,0,"up"}, + {KEY_LEFT,KEY_HOME,FALSE,0,"left"}, + {KEY_RIGHT,KEY_END,FALSE,0,"right"}, + {PALMTC_BLUEKEY,-1,FALSE,0,"blue"} } +}; + +struct input_dev *keyboard_dev; + +static struct workqueue_struct *palmtc_kbd_workqueue; +static struct work_struct palmtc_kbd_task; + +static spinlock_t kbd_lock = SPIN_LOCK_UNLOCKED; + +static irqreturn_t palmtc_kbd_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + queue_work(palmtc_kbd_workqueue, &palmtc_kbd_task); + return IRQ_HANDLED; +} + +static void palmtc_kbd_queuework(void *data) +{ + unsigned long tdiff_msec, flags; + int gpio, row, col; + + // ignore irqs + for(col=0;col<MAX_COL;col++) { + set_irq_type (IRQ_GPIO(col_gpio[col]), IRQT_NOEDGE); + } + // set row-gpios high so we can pull them low later one-by-one in loop + spin_lock_irqsave(&kbd_lock, flags); + for (row=0; row<MAX_ROW;row++) { + GPSR(row_gpio[row]) = GPIO_bit(row_gpio[row]); + } + spin_unlock_irqrestore(&kbd_lock, flags); + + // run thru rows, pull row-gpio low and see if what gpio of the colums goes low with it + for(row = MAX_ROW-1; row >=0; row--) { // counting backwards, so we get BLUE first + spin_lock_irqsave(&kbd_lock, flags); + GPCR(row_gpio[row]) = GPIO_bit(row_gpio[row]); + spin_unlock_irqrestore(&kbd_lock, flags); + + udelay(50); + + for(col = MAX_COL-1; col >= 0; col--) { + spin_lock_irqsave(&kbd_lock, flags); + gpio = GET_GPIO(col_gpio[col]); + spin_unlock_irqrestore(&kbd_lock, flags); + + tdiff_msec = jiffies_to_msecs((unsigned long)((long)jiffies - + (long)palmtc_buttons[row][col].tstamp)); + + if(!gpio) { + if (! (palmtc_buttons[row][col].flags&PRESSED_BIT)) { //is not pressed at the moment + + palmtc_buttons[row][col].flags|=PRESSED_BIT; + palmtc_buttons[row][col].tstamp=jiffies; +#ifdef USE_DOUBLECLICK + if ( (tdiff_msec<DOUBLECLICK_TIME_MS) ) { + if(palmtc_buttons[row][col].alt_key >= 0) { // report "alternate" keys + input_report_key(keyboard_dev, KEY_BACKSPACE, 1); + input_report_key(keyboard_dev, KEY_BACKSPACE, 0); + if (palmtc_buttons[row][col].flags&SHIFT_BIT) + input_report_key(keyboard_dev,KEY_LEFTSHIFT,1); + input_report_key(keyboard_dev, palmtc_buttons[row][col].alt_key, 1); + input_sync(keyboard_dev); + alternate_key=FALSE; + } + } else { +#endif + if (palmtc_buttons[row][col].key==PALMTC_BLUEKEY) { + switch(alternate_key) { + case (PERMANENT): + alternate_key=FALSE; + break; + case (TRUE): + alternate_key=PERMANENT; + break; + case (FALSE): + alternate_key=TRUE; + break; + } + } else { + if (alternate_key) { + if(palmtc_buttons[row][col].alt_key >= 0) { + if (palmtc_buttons[row][col].flags&SHIFT_BIT) + input_report_key(keyboard_dev,KEY_LEFTSHIFT,1); + input_report_key(keyboard_dev, palmtc_buttons[row][col].alt_key, 1); + //DBG("alt: ms:%0lu char: %s\n",palmtc_buttons[row][col].tstamp,palmtc_buttons[row][col].desc); + input_sync(keyboard_dev); + } + if(! (alternate_key==PERMANENT)) { + alternate_key=FALSE; + } + } else { + if(palmtc_buttons[row][col].key >= 0) { + input_report_key(keyboard_dev, palmtc_buttons[row][col].key, 1); + input_sync(keyboard_dev); + } + } + } +#ifdef USE_DOUBLECLICK + } +#endif + } +#ifdef USE_RELEASE_TIMER + spin_lock_irqsave(&kbd_lock, flags); + if (timer_pending(&key_release_timer)) { + mod_timer (&key_release_timer,jiffies + msecs_to_jiffies(RELEASE_CHECK_TIME_MS)); + } else { + key_release_timer.expires = (jiffies + msecs_to_jiffies(RELEASE_CHECK_TIME_MS)); + add_timer (&key_release_timer); + } + spin_unlock_irqrestore(&kbd_lock, flags); +#endif + } else { // not pressed branch + if (palmtc_buttons[row][col].flags&PRESSED_BIT) { + + palmtc_buttons[row][col].flags ^= PRESSED_BIT; //xor bit to 0 + if (palmtc_buttons[row][col].key >= 0) { + input_report_key(keyboard_dev, palmtc_buttons[row][col].key, 0); + if ((palmtc_buttons[row][col].alt_key >= 0) && (palmtc_buttons[row][col].flags&SHIFT_BIT) ) + input_report_key(keyboard_dev,KEY_LEFTSHIFT,0); + input_report_key(keyboard_dev, palmtc_buttons[row][col].alt_key, 0); + //DBG("rel: %s i:%d\n",palmtc_buttons[row][col].desc,i); i=0; + input_sync(keyboard_dev); + } + } + } + } + // back to high for this row + spin_lock_irqsave(&kbd_lock, flags); + GPSR(row_gpio[row]) = GPIO_bit(row_gpio[row]); + spin_unlock_irqrestore(&kbd_lock, flags); + } + + // set row-gpios low again + spin_lock_irqsave(&kbd_lock, flags); + for (row=0; row<MAX_ROW;row++) { + GPCR(row_gpio[row]) = GPIO_bit(row_gpio[row]); + } + spin_unlock_irqrestore(&kbd_lock, flags); + + // restore irqs (cols) + for(col=0;col<MAX_COL;col++) { + set_irq_type (IRQ_GPIO(col_gpio[col]), IRQT_BOTHEDGE); + } +} + + +#ifdef USE_RELEASE_TIMER +/* + * This is called when the key release timer goes off. That's the + * only time it should be called. Check for changes in what keys + * are down. + */ +static void +release_timer_went_off(unsigned long unused) +{ +unsigned long flags; + + spin_lock_irqsave(&kbd_lock,flags); + + queue_work(palmtc_kbd_workqueue, &palmtc_kbd_task); + + spin_unlock_irqrestore(&kbd_lock, flags); +} +#endif + + +static int palmtc_kbd_probe(struct device *dev) +{ + int row, col; + unsigned long ret; + DBG("Probing device\n" ); + + keyboard_dev = input_allocate_device(); + + keyboard_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + + keyboard_dev->name = "Palm Tungsten C Keyboard"; + keyboard_dev->id.bustype = BUS_HOST; + + for(row=0;row<MAX_ROW;row++) { + for(col=0;col<MAX_COL;col++) { + if(palmtc_buttons[row][col].key >= 0) { + keyboard_dev->keybit[LONG(palmtc_buttons[row][col].key)] |= + BIT(palmtc_buttons[row][col].key); + } + if (palmtc_buttons[row][col].alt_key >= 0) { + keyboard_dev->keybit[LONG(palmtc_buttons[row][col].alt_key)] |= + BIT(palmtc_buttons[row][col].alt_key); + } + } + } + input_register_device(keyboard_dev); + + palmtc_kbd_workqueue = create_workqueue("palmtckbdw"); + INIT_WORK(&palmtc_kbd_task, palmtc_kbd_queuework, NULL); + +#ifdef USE_RELEASE_TIMER + init_timer (&key_release_timer); + key_release_timer.function = release_timer_went_off; +#endif + + for(col=0;col<MAX_COL;col++) { + ret = request_irq(IRQ_GPIO(col_gpio[col]), palmtc_kbd_irq_handler, + SA_SAMPLE_RANDOM,"palmtc-kbd", (void*)col_gpio[col]); + set_irq_type(IRQ_GPIO(col_gpio[col]), IRQT_BOTHEDGE); + } + return 0; +} + +static int palmtc_kbd_remove (struct device *dev) +{ + int col; + + DBG("removing device...\n"); + destroy_workqueue(palmtc_kbd_workqueue); + input_unregister_device(keyboard_dev); + + /* free irqs */ + for(col=0;col<MAX_COL;col++) { + free_irq(IRQ_GPIO(col_gpio[col]), (void*) col_gpio[col]); + } + +#ifdef USE_RELEASE_TIMER + del_timer (&key_release_timer); +#endif + + return 0; +} + +static struct device_driver palmtc_keyboard_driver = { + .name = "palmtc-kbd", + .bus = &platform_bus_type, + .probe = palmtc_kbd_probe, + .remove = palmtc_kbd_remove, +#ifdef CONFIG_PM + .suspend = NULL, + .resume = NULL, +#endif +}; + +static int __init palmtc_kbd_init(void) +{ + DBG("init\n"); + + return driver_register(&palmtc_keyboard_driver); +} + +static void __exit palmtc_kbd_cleanup(void) +{ + DBG("unloading...\n"); + + driver_unregister(&palmtc_keyboard_driver); +} + +module_init(palmtc_kbd_init); +module_exit(palmtc_kbd_cleanup); + +MODULE_AUTHOR("Holger Bocklet"); +MODULE_DESCRIPTION("Support for Palm TC Keyboard"); +MODULE_LICENSE("GPL"); Deleted: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_tsc2101.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_tsc2101.c 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_tsc2101.c 2006-10-14 07:15:41 UTC (rev 625) @@ -1,236 +0,0 @@ -/* - * Support for TSC2101 - * - * Author: Vladimir Pouzanov <far...@gm...> - * - * Parts of the code from tsc2101 module by Richard Purdie <ri...@o-...> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <asm/mach-types.h> -#include <asm/mach/arch.h> -#include <linux/interrupt.h> -#include <asm/arch/pxa-regs.h> -#include <asm/arch/pxa_keys.h> -#include <asm/arch/ssp.h> -#include <asm/arch/irqs.h> -#include <linux/sched.h> -#include <linux/workqueue.h> -#include <asm/hardware.h> -#include "tsc2101.h" - -#define PALMT3_GPIO_TSC2101_SS 26 -#define PALMT3_GPIO_TOUCHSCREEN 37 - -#define X_AXIS_MAX 3830 -#define X_AXIS_MIN 150 -#define Y_AXIS_MAX 3830 -#define Y_AXIS_MIN 190 -#define PRESSURE_MIN 0 -#define PRESSURE_MAX 20000 - -static struct ssp_dev palmt3_ssp_dev; -static struct workqueue_struct *palmt3_touchscreen_wq; - -struct tsc2101_ts_data { - int x,y,p; -}; -struct tsc2101_ts_data palmt3_ts_data; - -/*** TSC2101 I/O ops ***/ -int palmt3_tsc2101_send(int read, int command, int *values, int numval) -{ - int i; - - GPCR0 = GPIO_bit(PALMT3_GPIO_TSC2101_SS); - - ssp_write_word(&palmt3_ssp_dev, command | read); - /* dummy read */ - ssp_read_word(&palmt3_ssp_dev); - - for (i=0; i < numval; i++) { - if (read) { - ssp_write_word(&palmt3_ssp_dev, 0); - values[i]=ssp_read_word(&palmt3_ssp_dev); - } else { - ssp_write_word(&palmt3_ssp_dev, values[i]); - ssp_read_word(&palmt3_ssp_dev); - } - } - - GPSR0 = GPIO_bit(PALMT3_GPIO_TSC2101_SS); - return 0; -} - -/* FIXME static or not? */ -void palmt3_tsc2101_readdata(void *ts_data) -{ - int vx,vy,vp,z1,z2;//,fixadc=0; - u32 values[4],status; - vx=0; vy=0; vp=0; - palmt3_tsc2101_send(TSC2101_READ, TSC2101_REG_STATUS, &status, 1); - - // printk("status: %x ", status); - - if (status & (TSC2101_STATUS_XSTAT | TSC2101_STATUS_YSTAT | TSC2101_STATUS_Z1STAT - | TSC2101_STATUS_Z2STAT)) { - - /* Read X, Y, Z1 and Z2 */ - palmt3_tsc2101_send(TSC2101_READ, TSC2101_REG_X, &values[0], 4); - - vx=values[0]; - vy=values[1]; - z1=values[2]; - z2=values[3]; - - /* Calculate Pressure */ - if ((z1 != 0) && (vx!=0) && (vy!=0)) - vp = ((vx * (z2 -z1) / z1)); - else - vp=0; - } - /*printk("Touch at %d:%d raw, pressure: %d\n", vx,vy,vp);*/ - palmt3_ts_data.x = vx; - palmt3_ts_data.y = vy; - palmt3_ts_data.p = vp; - - /* - if (status & TSC2101_STATUS_BSTAT) { - devdata->platform->send(TSC2101_READ, TSC2101_REG_BAT, &values[0], 1); - devdata->miscdata.bat=values[0]; - fixadc=1; - } - if (status & TSC2101_STATUS_AX1STAT) { - devdata->platform->send(TSC2101_READ, TSC2101_REG_AUX1, &values[0], 1); - devdata->miscdata.aux1=values[0]; - fixadc=1; - } - if (status & TSC2101_STATUS_AX2STAT) { - devdata->platform->send(TSC2101_READ, TSC2101_REG_AUX2, &values[0], 1); - devdata->miscdata.aux2=values[0]; - fixadc=1; - } - if (status & TSC2101_STATUS_T1STAT) { - devdata->platform->send(TSC2101_READ, TSC2101_REG_TEMP1, &values[0], 1); - devdata->miscdata.temp1=values[0]; - fixadc=1; - } - if (status & TSC2101_STATUS_T2STAT) { - devdata->platform->send(TSC2101_READ, TSC2101_REG_TEMP2, &values[0], 1); - devdata->miscdata.temp2=values[0]; - fixadc=1; - } - if (fixadc) { - // Switch back to touchscreen autoscan - tsc2101_regwrite(devdata, TSC2101_REG_ADC, TSC2101_ADC_DEFAULT | TSC2101_ADC_PSM | TSC2101_ADC_ADMODE(0x2)); - } - */ -} - -/*** IRQ handlers ***/ -static void palmt3_touchscreen_handle(void* unused) -{ - palmt3_tsc2101_readdata(0); - // printk(KERN_INFO "palmt3_touchscreen_handle: Screen touched...\n"); -} - -irqreturn_t palmt3_touchscreen_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - static int initialised = 0; - static struct work_struct task; - - if (initialised == 0) { - INIT_WORK(&task, palmt3_touchscreen_handle, NULL); - initialised = 1; - } else { - PREPARE_WORK(&task, palmt3_touchscreen_handle, NULL); - } - - queue_work(palmt3_touchscreen_wq, &task); - - return IRQ_HANDLED; -} - -/*** INIT/CLEANUP stuff ***/ -#define TSC_REGWRITE(r, x) regval = x; palmt3_tsc2101_send(TSC2101_WRITE, r, ®val, 1); -#define TSC2101_ADC_DEFAULT (TSC2101_ADC_RES(TSC2101_ADC_RES_12BITP) | TSC2101_ADC_AVG(TSC2101_ADC_4AVG) | TSC2101_ADC_CL(TSC2101_ADC_CL_1MHZ_12BIT) | TSC2101_ADC_PV(TSC2101_ADC_PV_500us) | TSC2101_ADC_AVGFILT_MEAN) - -static int __init palmt3_tsc2101_init(void) -{ - int regval; - printk(KERN_INFO "palmt3_tsc2101: init\n"); - - printk(KERN_WARNING "FIXME: Resetting SSP, but that's bootloader job\n"); - SSCR0 &= ~SSCR0_SSE; - SSCR1 = SSCR1 & 0x3FFC; - - if (ssp_init(&palmt3_ssp_dev, 1, NULL)) - printk(KERN_ERR "palmt3_tsc2101: Unable to register SSP1 handler!\n"); - else { - // ssp_disable(&palmt3_ssp_dev); - // ssp_config(&palmt3_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x0f )), SSCR1_SPH, 0, 0); - ssp_enable(&palmt3_ssp_dev); - // hx2750_set_egpio(HX2750_EGPIO_TSC_PWR); - printk(KERN_INFO "palmt3_tsc2101: SSP initialized\n"); - } - - // reset TSC2101 - TSC_REGWRITE(TSC2101_REG_ADC, 0x4000); - printk(KERN_INFO "palmt3_tsc2101: re-enabling TSC2101\n"); - /* PINTDAV is data available only */ - TSC_REGWRITE(TSC2101_REG_STATUS, 0x4000); - - /* disable buffer mode */ - TSC_REGWRITE(TSC2101_REG_BUFMODE, 0x0); - - /* use internal reference, 100 usec power-up delay, - * power down between conversions, 1.25V internal reference */ - // TSC_REGWRITE(TSC2101_REG_REF, 0x16); - - /* enable touch detection, 84usec precharge time, 32 usec sense time */ - TSC_REGWRITE(TSC2101_REG_CONFIG, 0x08); - - /* 3 msec conversion delays */ - TSC_REGWRITE(TSC2101_REG_DELAY, 0x0900); - - /* - * TSC2101-controlled conversions - * 12-bit samples - * continuous X,Y,Z1,Z2 scan mode - * average (mean) 4 samples per coordinate - * 1 MHz internal conversion clock - * 500 usec panel voltage stabilization delay - */ - TSC_REGWRITE(TSC2101_REG_ADC, TSC2101_ADC_DEFAULT | TSC2101_ADC_PSM | TSC2101_ADC_ADMODE(0x2)); - -#if 0 - palmt3_touchscreen_wq = create_workqueue("T3 Touchscreen"); - if(request_irq(IRQ_GPIO(PALMT3_GPIO_TOUCHSCREEN), palmt3_touchscreen_irq, - SA_SAMPLE_RANDOM, "T3 Touchscreen GPIO", NULL)) { - printk(KERN_ERR "palmt3_tsc2101: Unable to register GPIO handler for %d\n", PALMT3_GPIO_TSC2101_SS); - } else { - set_irq_type (IRQ_GPIO(PALMT3_GPIO_TOUCHSCREEN), IRQT_BOTHEDGE); - /* FIXME is it really bothedge? Should check PalmOS */ - - printk(KERN_INFO "palmt3_tsc2101: Touchscreen GPIO registered\n"); - } -#endif - - return 0; -} - -static void __exit palmt3_tsc2101_cleanup(void) -{ - /* FIXME Unreg stuff */ - printk(KERN_INFO "palmt3_tsc2101: cleanup\n"); -} - -module_init(palmt3_tsc2101_init); -module_exit(palmt3_tsc2101_cleanup); - Deleted: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/tsc2101.h =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/tsc2101.h 2006-10-13 19:51:42 UTC (rev 624) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/tsc2101.h 2006-10-14 07:15:41 UTC (rev 625) @@ -1,154 +0,0 @@ -/* - * TI TSC2101 Hardware definitions - * - * Copyright 2005 Openedhand Ltd. - * - * Author: Richard Purdie <ri...@o-...> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -/* Address constructs */ -#define TSC2101_READ (1 << 15) /* Read Register */ -#define TSC2101_WRITE (0 << 15) /* Write Register */ -#define TSC2101_PAGE(x) ((x & 0xf) << 11) /* Memory Page to access */ -#define TSC2101_ADDR(x) ((x & 0x3f) << 5) /* Memory Address to access */ - -#define TSC2101_P0_REG(x) (TSC2101_PAGE(0) | TSC2101_ADDR(x)) -#define TSC2101_P1_REG(x) (TSC2101_PAGE(1) | TSC2101_ADDR(x)) -#define TSC2101_P2_REG(x) (TSC2101_PAGE(2) | TSC2101_ADDR(x)) -#define TSC2101_P3_REG(x) (TSC2101_PAGE(3) | TSC2101_ADDR(x)) - -/* Page 0 Registers */ -#define TSC2101_REG_X TSC2101_P0_REG(0x0) -#define TSC2101_REG_Y TSC2101_P0_REG(0x1) -#define TSC2101_REG_Z1 TSC2101_P0_REG(0x2) -#define TSC2101_REG_Z2 TSC2101_P0_REG(0x3) -#define TSC2101_REG_BAT TSC2101_P0_REG(0x5) -#define TSC2101_REG_AUX1 TSC2101_P0_REG(0x7) -#define TSC2101_REG_AUX2 TSC2101_P0_REG(0x8) -#define TSC2101_REG_TEMP1 TSC2101_P0_REG(0x9) -#define TSC2101_REG_TEMP2 TSC2101_P0_REG(0xa) - -/* Page 1 Registers */ -#define TSC2101_REG_ADC TSC2101_P1_REG(0x0) -#define TSC2101_REG_STATUS TSC2101_P1_REG(0x1) -#define TSC2101_REG_BUFMODE TSC2101_P1_REG(0x2) -#define TSC2101_REG_REF TSC2101_P1_REG(0x3) -#define TSC2101_REG_RESETCTL TSC2101_P1_REG(0x4) -#define TSC2101_REG_CONFIG TSC2101_P1_REG(0x5) -#define TSC2101_REG_TEMPMAX TSC2101_P1_REG(0x6) -#define TSC2101_REG_TEMPMIN TSC2101_P1_REG(0x7) -#define TSC2101_REG_AUX1MAX TSC2101_P1_REG(0x8) -#define TSC2101_REG_AUX1MIN TSC2101_P1_REG(0x9) -#define TSC2101_REG_AUX2MAX TSC2101_P1_REG(0xa) -#define TSC2101_REG_AUX2MIN TSC2101_P1_REG(0xb) -#define TSC2101_REG_MEASURE TSC2101_P1_REG(0xc) -#define TSC2101_REG_DELAY TSC2101_P1_REG(0xd) - -/* Page 2 Registers */ -#define TSC2101_REG_AUDIOCON1 TSC2101_P2_REG(0x0) -#define TSC2101_REG_HEADSETPGA TSC2101_P2_REG(0x1) -#define TSC2101_REG_DACPGA TSC2101_P2_REG(0x2) -#define TSC2101_REG_MIXERPGA TSC2101_P2_REG(0x3) -#define TSC2101_REG_AUDIOCON2 TSC2101_P2_REG(0x4) -#define TSC2101_REG_PWRDOWN TSC2101_P2_REG(0x5) -#define TSC2101_REG_AUDIOCON3 TSC2101_P2_REG(0x6) -#define TSC2101_REG_DAEFC(x) TSC2101_P2_REG(0x7+x) -#define TSC2101_REG_PLL1 TSC2101_P2_REG(0x1b) -#define TSC2101_REG_PLL2 TSC2101_P2_REG(0x1c) -#define TSC2101_REG_AUDIOCON4 TSC2101_P2_REG(0x1d) -#define TSC2101_REG_HANDSETPGA TSC2101_P2_REG(0x1e) -#define TSC2101_REG_BUZZPGA TSC2101_P2_REG(0x1f) -#define TSC2101_REG_AUDIOCON5 TSC2101_P2_REG(0x20) -#define TSC2101_REG_AUDIOCON6 TSC2101_P2_REG(0x21) -#define TSC2101_REG_AUDIOCON7 TSC2101_P2_REG(0x22) -#define TSC2101_REG_GPIO TSC2101_P2_REG(0x23) -#define TSC2101_REG_AGCCON TSC2101_P2_REG(0x24) -#define TSC2101_REG_DRVPWRDWN TSC2101_P2_REG(0x25) -#define TSC2101_REG_MICAGC TSC2101_P2_REG(0x26) -#define TSC2101_REG_CELLAGC TSC2101_P2_REG(0x27) - -/* Page 2 Registers */ -#define TSC2101_REG_BUFLOC(x) TSC2101_P3_REG(x) - -/* Status Register Masks */ - -#define TSC2101_STATUS_T2STAT (1 << 1) -#define TSC2101_STATUS_T1STAT (1 << 2) -#define TSC2101_STATUS_AX2STAT (1 << 3) -#define TSC2101_STATUS_AX1STAT (1 << 4) -#define TSC2101_STATUS_BSTAT (1 << 6) -#define TSC2101_STATUS_Z2STAT (1 << 7) -#define TSC2101_STATUS_Z1STAT (1 << 8) -#define TSC2101_STATUS_YSTAT (1 << 9) -#define TSC2101_STATUS_XSTAT (1 << 10) -#define TSC2101_STATUS_DAVAIL (1 << 11) -#define TSC2101_STATUS_HCTLM (1 << 12) -#define TSC2101_STATUS_PWRDN (1 << 13) -#define TSC2101_STATUS_PINTDAV_SHIFT (14) -#define TSC2101_STATUS_PINTDAV_MASK (0x03) - - - - - - - -#define TSC2101_ADC_PSM (1<<15) // pen status mode on ctrlreg adc -#define TSC2101_ADC_STS (1<<14) // stop continuous scanning. -#define TSC2101_ADC_AD3 (1<<13) -#define TSC2101_ADC_AD2 (1<<12) -#define TSC2101_ADC_AD1 (1<<11) -#define TSC2101_ADC_AD0 (1<<10) -#define TSC2101_ADC_ADMODE(x) ((x<<10) & TSC2101_ADC_ADMODE_MASK) -#define TSC2101_ADC_ADMODE_MASK (0xf<<10) - -#define TSC2101_ADC_RES(x) ((x<<8) & TSC2101_ADC_RES_MASK ) -#define TSC2101_ADC_RES_MASK (0x3<<8) -#define TSC2101_ADC_RES_12BITP (0) // 12-bit ADC resolution (default) -#define TSC2101_ADC_RES_8BIT (1) // 8-bit ADC resolution -#define TSC2101_ADC_RES_10BIT (2) // 10-bit ADC resolution -#define TSC2101_ADC_RES_12BIT (3) // 12-bit ADC resolution - -#define TSC2101_ADC_AVG(x) ((x<<6) & TSC2101_ADC_AVG_MASK ) -#define TSC2101_ADC_AVG_MASK (0x3<<6) -#define TSC2101_ADC_NOAVG (0) // a-d does no averaging -#define TSC2101_ADC_4AVG (1) // a-d does averaging of 4 samples -#define TSC2101_ADC_8AVG (2) // a-d does averaging of 8 samples -#define TSC2101_ADC_16AVG (3) // a-d does averaging of 16 samples - -#define TSC2101_ADC_CL(x) ((x<<4) & TSC2101_ADC_CL_MASK ) -#define TSC2101_ADC_CL_MASK (0x3<<4) -#define TSC2101_ADC_CL_8MHZ_8BIT (0) -#define TSC2101_ADC_CL_4MHZ_10BIT (1) -#define TSC2101_ADC_CL_2MHZ_12BIT (2) -#define TSC2101_ADC_CL_1MHZ_12BIT (3) -#define TSC2101_ADC_CL0 (1<< 4) - -/* ADC - Panel Voltage Stabilisation Time */ -#define TSC2101_ADC_PV(x) ((x<<1) & TSC2101_ADC_PV_MASK ) -#define TSC2101_ADC_PV_MASK (0x7<<1) -#define TSC2101_ADC_PV_100ms (0x7) /* 100ms */ -#define TSC2101_ADC_PV_50ms (0x6) /* 50ms */ -#define TSC2101_ADC_PV_10ms (0x5) /* 10ms */ -#define TSC2101_ADC_PV_5ms (0x4) /* 5ms */ -#define TSC2101_ADC_PV_1ms (0x3) /* 1ms */ -#define TSC2101_ADC_PV_500us (0x2) /* 500us */ -#define TSC2101_ADC_PV_100us (0x1) /* 100us */ -#define TSC2101_ADC_PV_0s (0x0) /* 0s */ - -#define TSC2101_ADC_AVGFILT_MEAN (0<<0) /* Mean Average Filter */ -#define TSC2101_ADC_AVGFILT_MEDIAN (1<<0) /* Median Average Filter */ - -#define TSC2101_ADC_x (1<< 0) // don't care - -#define TSC2101_CONFIG_DAV (1<<6) - -#define TSC2101_KEY_STC (1<<15) // keypad status -#define TSC2101_KEY_SCS (1<<14) // keypad scan status - - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <hol...@us...> - 2006-11-28 19:59:21
|
Revision: 684 http://svn.sourceforge.net/hackndev/?rev=684&view=rev Author: holger_bocklet Date: 2006-11-28 11:59:12 -0800 (Tue, 28 Nov 2006) Log Message: ----------- t|c touchscreen driver and updates for mach-pxa/palmtc Modified Paths: -------------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c Added Paths: ----------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc-gpio.h linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400_ts.c Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig 2006-11-28 11:35:37 UTC (rev 683) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Kconfig 2006-11-28 19:59:12 UTC (rev 684) @@ -13,3 +13,11 @@ This driver translates button presses on a Palm Tungsten C to Linux input subsystem events +config PALMTC_UCB1400_TS + tristate "Palm Tungsten C ucb1400 touchscreen support" + depends on MACH_OMAP_PALMTC + default y + help + This driver handles the UCB1400 AC97 codec, + which is used to support the touchscreen + Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile 2006-11-28 11:35:37 UTC (rev 683) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/Makefile 2006-11-28 19:59:12 UTC (rev 684) @@ -4,3 +4,5 @@ obj-$(CONFIG_MACH_OMAP_PALMTC) += palmtc.o obj-$(CONFIG_PALMTC_KEYBOARD) += palmtc_keyboard.o +obj-$(CONFIG_PALMTC_AC97) += palmtc_ac97.o +obj-$(CONFIG_PALMTC_UCB1400_TS) += ptc_ucb1400_ts.o Added: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc-gpio.h =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc-gpio.h (rev 0) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc-gpio.h 2006-11-28 19:59:12 UTC (rev 684) @@ -0,0 +1,26 @@ +/* + * palmtc-gpio.h + * + * Authors: Holger Bocklet <bit...@gm...> + * + */ + +#ifndef _PALMTC_GPIO_H_ +#define _PALMTC_GPIO_H_ + +#include <asm/arch/pxa-regs.h> + +/* Palm Tungsten C GPIOs */ +#define GPIO_NR_PALMTC_EARPHONE_DETECT 2 +#define GPIO_NR_PALMLD_USB_DETECT 4 +#define GPIO_NR_PALMLD_POWER_DETECT 5 +#define GPIO_NR_PALMTC_HOTSYNC_BUTTON 7 +#define GPIO_NR_PALMTC_SD_DETECT 12 // low->high wenn out, high->low when inserted +#define GPIO_NR_PALMLD_BL_POWER 16 + +//#define GPIO_NR_PALMLD_STD_RXD 46 /* IRDA */ +//#define GPIO_NR_PALMLD_STD_TXD 47 + +#define IRQ_GPIO_PALMTC_SD_DETECT IRQ_GPIO(GPIO_NR_PALMTC_SD_DETECT) + +#endif /* _PALMTC_GPIO_H_ */ Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c 2006-11-28 11:35:37 UTC (rev 683) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c 2006-11-28 19:59:12 UTC (rev 684) @@ -13,6 +13,7 @@ #include <asm/arch/hardware.h> #include <asm/mach/map.h> #include <asm/domain.h> +#include <asm/arch/mmc.h> #include <linux/device.h> #include <linux/platform_device.h> @@ -23,6 +24,7 @@ #include <asm/arch/pxa-regs.h> #include "../generic.h" +#include "palmtc-gpio.h" #define DEBUG @@ -57,10 +59,57 @@ }; + +static int palmtc_mci_init(struct device *dev, irqreturn_t (*palmtc_detect_int)(int, void *, struct pt_regs *), void *data) +{ + int err; + + /** + * Setup an interrupt for detecting card insert/remove events + */ + set_irq_type(IRQ_GPIO_PALMTC_SD_DETECT, IRQT_BOTHEDGE); + err = request_irq(IRQ_GPIO_PALMTC_SD_DETECT, palmtc_detect_int, + SA_INTERRUPT, "SD/MMC card detect", data); + + if(err) { + printk(KERN_ERR "palmld_mci_init: cannot request SD/MMC card detect IRQ\n"); + return -1; + } + + + printk("palmtc_mci_init: irq registered\n"); + + return 0; +} -static struct platform_device palmtc_audio_device = { +static void palmtc_mci_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIO_PALMTC_SD_DETECT, data); +} + +static struct pxamci_platform_data palmtc_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = palmtc_mci_init, + /* .setpower = palmld_mci_setpower, */ + .exit = palmtc_mci_exit, + +}; + + +static pxa2xx_audio_ops_t palmtc_audio_ops = { + /* + .startup = palmld_audio_startup, + .shutdown = mst_audio_shutdown, + .suspend = mst_audio_suspend, + .resume = mst_audio_resume, + */ +}; + + +static struct platform_device palmtc_ac97_device = { .name = "pxa2xx-ac97", .id = -1, + .dev = { .platform_data = &palmtc_audio_ops }, }; static struct platform_device palmtc_keyboard_device = { @@ -70,7 +119,7 @@ static struct platform_device *devices[] __initdata = { - &palmtc_audio_device, + &palmtc_ac97_device, &palmtc_keyboard_device, }; @@ -78,13 +127,18 @@ { set_pxa_fb_info(&palmtclcd); GCR &= ~GCR_PRIRDY_IEN; + + pxa_set_mci_info( &palmtc_mci_platform_data ); + platform_add_devices (devices, ARRAY_SIZE (devices)); + } MACHINE_START(OMAP_PALMTC, "Palm Tungsten C") /* Maintainer: P3T3, Petr Blaha <p3...@ce...> */ - .phys_ram = 0xa0000000, +// .phys_ram = 0xa0000000, .phys_io = 0x40000000, + .boot_params = 0xa0000100, .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, .map_io = pxa_map_io, .init_irq = pxa_init_irq, Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c 2006-11-28 11:35:37 UTC (rev 683) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc_keyboard.c 2006-11-28 19:59:12 UTC (rev 684) @@ -109,10 +109,10 @@ {KEY_D,KEY_KPPLUS,FALSE,0,"D"}, {KEY_H,KEY_MINUS,SHIFT_BIT|FALSE,0,"H/_"}, {KEY_DOT,KEY_SEMICOLON,SHIFT_BIT|FALSE,0,"./:"} }, -/*22*/ {{KEY_A,KEY_2,SHIFT_BIT|FALSE,0,"A/@"}, +/*22*/ {{KEY_A,KEY_EQUAL,FALSE,0,"A/@"}, // changed to "=" {KEY_C,KEY_0,SHIFT_BIT|FALSE,0,"C/)"}, {KEY_Z,KEY_6,FALSE,0,"de:Z us:Y"}, - {KEY_L,KEY_EQUAL,FALSE,0,"L/\xDF"} }, // Keyoard shows "\xDF" but better use "=" + {KEY_L,KEY_COMMA,SHIFT_BIT|FALSE,0,"L/\xDF"} }, // Keyoard shows "\xDF" but better use "<" /*23*/ {{KEY_Q,KEY_1,FALSE,0,"Q"}, //0 {KEY_F,KEY_KPMINUS,FALSE,0,"F"}, //9 {KEY_N,KEY_COMMA,FALSE,0,"N"}, //10 @@ -128,7 +128,7 @@ /*26*/ {{KEY_S,KEY_APOSTROPHE,SHIFT_BIT|FALSE,0,"S"}, {KEY_V,KEY_3,SHIFT_BIT|FALSE,0,"V/#"}, {KEY_M,KEY_SLASH,SHIFT_BIT|FALSE,0,"M/?"}, - {KEY_BACKSPACE,KEY_GRAVE,SHIFT_BIT|FALSE,0,"bs/~"} }, + {KEY_BACKSPACE,KEY_DOT,SHIFT_BIT|FALSE,0,"bs/>"} }, /*27*/ {{KEY_W,KEY_2,FALSE,0,"W"}, {KEY_G,KEY_KPASTERISK,FALSE,0,"G"}, {KEY_K,KEY_APOSTROPHE,FALSE,0,"K"}, @@ -310,7 +310,7 @@ { int row, col; unsigned long ret; - DBG("Probing device\n" ); + //DBG("Probing device\n" ); keyboard_dev = input_allocate_device(); @@ -346,6 +346,7 @@ SA_SAMPLE_RANDOM,"palmtc-kbd", (void*)col_gpio[col]); set_irq_type(IRQ_GPIO(col_gpio[col]), IRQT_BOTHEDGE); } + DBG("device enabled\n"); return 0; } Added: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h (rev 0) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2006-11-28 19:59:12 UTC (rev 684) @@ -0,0 +1,153 @@ +/* + * linux/drivers/mfd/ucb1400.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * 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. + */ + +/* ucb1400 aclink register mappings: */ + +#define UCB_IO_DATA 0x5a +#define UCB_IO_DIR 0x5c +#define UCB_IE_RIS 0x5e +#define UCB_IE_FAL 0x60 +#define UCB_IE_STATUS 0x62 +#define UCB_IE_CLEAR 0x62 +#define UCB_TS_CR 0x64 +#define UCB_ADC_CR 0x66 +#define UCB_ADC_DATA 0x68 +#define UCB_CSR1 0x6a +#define UCB_CSR2 0x6c + +#define UCB_CS_GPIO_AC97_INT (1 << 2) + +#define UCB_ADC_DAT(x) ((x) & 0x3ff) + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX (1 << 12) +#define UCB_TS_CR_TSMX (1 << 13) + +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DATA_MASK 0x3ff +#define UCB_ADC_DATA_VALID (1 << 15) + +#define UCB_ID_1400 0x4304 + +#define UCB_MODE 0x0d +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +#define UCB_NOSYNC 0 +#define UCB_SYNC 1 + +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +#define UCB_TS_X_MIN 110 +#define UCB_TS_X_MAX 860 +#define UCB_TS_Y_MIN 280 +#define UCB_TS_Y_MAX 960 +#define UCB_TS_PRESSURE_MIN 0 +#define UCB_TS_PRESSURE_MAX 740 + + +struct ucb1400_ts { + struct device *dev; + ac97_t *ac97; + struct input_dev *idev; + //spinlock_t lock; + //unsigned int irq; + struct task_struct *thread; + //struct completion irq_wait; + wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ + //struct semaphore ucb_ts_sem; + //u16 id; + //u8 pen_is_down; + int irq_in_work; + //struct ucb1400_ts_irq irq_handler; + struct completion thr_init; + struct completion thr_exit; + u8 last_status; + //unsigned int adcsync:UCB_NOSYNC; +}; + + +//#define classdev_to_ucb1400(cd) container_of(cd, struct ucb1400, cdev) + +//int ucb1400_register_driver(struct ucb1400_driver *); +//void ucb1400_unregister_driver(struct ucb1400_driver *); + + +//void ucb1400_io_set_dir(struct ucb1400 *ucb, unsigned int, unsigned int); +//void ucb1400_io_write(struct ucb1400 *ucb, unsigned int, unsigned int); +//unsigned int ucb1400_io_read(struct ucb1400 *ucb); + +//unsigned int ucb1400_adc_read(struct ucb1400 *ucb, int adc_channel, int sync); +//void ucb1400_adc_enable(struct ucb1400 *ucb); +//void ucb1400_adc_disable(struct ucb1400 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ + +//int ucb1400_hook_irq(struct ucb1400 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +//void ucb1400_enable_irq(struct ucb1400 *ucb, unsigned int idx, int edges); +//void ucb1400_disable_irq(struct ucb1400 *ucb, unsigned int idx, int edges); +//int ucb1400_free_irq(struct ucb1400 *ucb, unsigned int idx, void *devid); Added: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400_ts.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400_ts.c (rev 0) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400_ts.c 2006-11-28 19:59:12 UTC (rev 684) @@ -0,0 +1,569 @@ +/* + * linux/arch/arm/mach-pxa/palmtc/ptc_ucb1400_ts.c + * + * Touchscreendriver for Palm Tungsten C AC97 codec ucb1400 + * + * Author: Holger Bocklet <bit...@gm...> + * + * There's noting new under the sun, + * So this peace of software ist derived of + * - the ucb1x00-ts by Russell King, Pavel Machek and + * - patches for the ucb1400 by Nicols Pitre and + * - the wm97xx-driver by Liam Girdwood + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/jiffies.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/moduleparam.h> +#include <linux/wait.h> + +#include <asm/delay.h> +#include <asm/system.h> +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/irqs.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/ac97_codec.h> + +#include <linux/config.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kthread.h> +#include <linux/device.h> +#include <linux/mutex.h> + +#include "ptc_ucb1400.h" + +#define PALMTC_TS_DEBUG + +#ifdef PALMTC_TS_DEBUG +#define DBG(x...) \ + printk(KERN_INFO "PTC_TS: " x) +#else +#define DBG(x...) do { } while (0) +#endif + +#define TRUE 1 +#define FALSE 0 + +struct ts_reading { + u16 p; + u16 x; + u16 y; +}; + +//DECLARE_MUTEX(battery_update_mutex); + +// Pen sampling frequency (Hz) while touched. +// more is not possible cause jiffies are at 100Hz in mach-pxa (why ???) +static int cont_rate = 100; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate while pen down (Hz). Not more than ~100Hz on T|C"); + +/* codec AC97 IO access */ +int ucb1400_reg_read(struct ucb1400_ts *ucb_ts, u16 reg) +{ + if (ucb_ts->ac97) + return ucb_ts->ac97->bus->ops->read(ucb_ts->ac97, reg); + else + return -1; +} + +void ucb1400_reg_write(struct ucb1400_ts *ucb_ts, u16 reg, u16 val) +{ + if (ucb_ts->ac97) + ucb_ts->ac97->bus->ops->write(ucb_ts->ac97, reg, val); +} + +/** + * ucb1x00_adc_enable - enable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. + * Any code wishing to use the ADC converter must call this + * function prior to using it. + * FIXME: this (see below) will be later + * This function takes the ADC semaphore to prevent two or more + * concurrent uses, and therefore may sleep. As a result, it + * can only be called from process context, not interrupt + * context. + * + * You should release the ADC as soon as possible using + * ucb1x00_adc_disable. + */ +void ucb1400_adc_enable(struct ucb1400_ts *ucb_ts) +{ + //down(&ucb->adc_sem); + ucb1400_reg_write(ucb_ts, UCB_ADC_CR, UCB_ADC_ENA); +} + + +/** + * ucb1x00_adc_disable - disable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Disable the ADC converter and release the ADC semaphore. + */ +void ucb1400_adc_disable(struct ucb1400_ts *ucb_ts) +{ + u16 val=0; + val &= ~UCB_ADC_ENA; + ucb1400_reg_write(ucb_ts, UCB_ADC_CR, val); + //up(&ucb->adc_sem); +} + + +/** + * ucb1x00_adc_read - read the specified ADC channel + * @ucb: UCB1x00 structure describing chip + * @adc_channel: ADC channel mask + * @sync: wait for syncronisation pulse. + * + * Start an ADC conversion and wait for the result. + * This function currently sleeps waiting for the conversion to + * complete (2 frames max without sync). + */ +unsigned int ucb1400_adc_read(struct ucb1400_ts *ucb_ts, int adc_channel) +{ + unsigned int val; + + adc_channel |= UCB_ADC_ENA; + + ucb1400_reg_write(ucb_ts, UCB_ADC_CR, adc_channel); + ucb1400_reg_write(ucb_ts, UCB_ADC_CR, adc_channel | UCB_ADC_START); + + for (;;) { // TODO: use interrupt mode + val = ucb1400_reg_read(ucb_ts, UCB_ADC_DATA); + if (val & UCB_ADC_DATA_VALID) + break; + /* yield to other processes */ + set_current_state(TASK_INTERRUPTIBLE); //without sync its ~ 22 usec = 1 jiffie ???? + schedule_timeout(3); // try 1 + } + + if (val & UCB_ADC_DATA_VALID) { + return val & UCB_ADC_DATA_MASK; + } else { + return 0; + } +} + + +static inline void ucb1400_ts_report_event(struct ucb1400_ts *ucb_ts, struct ts_reading ts) +{ + input_report_abs(ucb_ts->idev, ABS_X, ts.x); + input_report_abs(ucb_ts->idev, ABS_Y, ts.y); + input_report_abs(ucb_ts->idev, ABS_PRESSURE, ts.p); + input_sync(ucb_ts->idev); +} + + +static inline void ucb1400_ts_event_pen_up(struct ucb1400_ts *ucb_ts) +{ + input_report_abs(ucb_ts->idev, ABS_PRESSURE, 0); + input_sync(ucb_ts->idev); +} + + +/* + * Switch touchscreen to given interrupt mode. +* param = UCB_IE_RIS || UCB_IE_FAL switch ts-mod to int on edge in param +* param != 0 leave edge as it is +* param == 0 switch ts - int off + */ +static inline void ucb1400_ts_mode_int(struct ucb1400_ts *ucb_ts, u16 param) +{ + u16 val; + + // clear remaining ints + val=ucb1400_reg_read(ucb_ts, UCB_IE_STATUS); + ucb1400_reg_write(ucb_ts, UCB_IE_CLEAR, val); + + if (param==0) { // switch off int + val=ucb1400_reg_read(ucb_ts, UCB_IE_FAL); + + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND ); + + val=ucb1400_reg_read(ucb_ts, UCB_IE_FAL); // edge-detection off + ucb1400_reg_write(ucb_ts, UCB_IE_FAL, val & (~UCB_IE_TSPX) & (~UCB_IE_TSMX)); + val=ucb1400_reg_read(ucb_ts, UCB_IE_FAL); + //DBG("ucb1400_intmode_OFF: 0x%x\n",val); + ucb1400_reg_write(ucb_ts, UCB_IE_RIS, val & (~UCB_IE_TSPX) & (~UCB_IE_TSMX)); + + } else { + + switch(param) { + case (UCB_IE_RIS): + val=ucb1400_reg_read(ucb_ts, UCB_IE_RIS); + ucb1400_reg_write(ucb_ts, UCB_IE_RIS, val | UCB_IE_TSPX); + val=ucb1400_reg_read(ucb_ts, UCB_IE_FAL); + ucb1400_reg_write(ucb_ts, UCB_IE_FAL, val & (~UCB_IE_TSPX) ); + //DBG(" ucb1400_intmode_Ris\n"); + break; + case (UCB_IE_FAL): + val=ucb1400_reg_read(ucb_ts, UCB_IE_FAL); + ucb1400_reg_write(ucb_ts, UCB_IE_FAL, val | UCB_IE_TSPX); + val=ucb1400_reg_read(ucb_ts, UCB_IE_RIS); + ucb1400_reg_write(ucb_ts, UCB_IE_RIS, val & (~UCB_IE_TSPX) ); + //DBG(" ucb1400_intmode_Fal\n"); + break; + } + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); + } +} + + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb_ts) +{ + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + //udelay(55); + + return ucb1400_adc_read(ucb_ts, UCB_ADC_INP_TSPY); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb_ts) +{ + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1400_adc_read(ucb_ts, UCB_ADC_INP_TSPY); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb_ts) +{ + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + ucb1400_reg_write(ucb_ts, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1400_adc_read(ucb_ts, UCB_ADC_INP_TSPX); +} + + +static inline int ucb1400_ts_pen_is_down(struct ucb1400_ts *ucb_ts) +{ + u16 val; + + val = ucb1400_reg_read(ucb_ts, UCB_TS_CR); + if (((val & UCB_TS_CR_TSPX) == 0) || ((val & UCB_TS_CR_TSMX) ==0 )) { + return TRUE; + } else { + return FALSE; + } +} + + +static void ucb1400_ts_take_reading(struct ucb1400_ts *ucb_ts, struct ts_reading *ts) +{ + ucb1400_adc_enable(ucb_ts); + + ts->x = ucb1400_ts_read_xpos(ucb_ts); + ts->y = ucb1400_ts_read_ypos(ucb_ts); + ts->p = ucb1400_ts_read_pressure(ucb_ts); + + ucb1400_adc_disable(ucb_ts); +} + +// ISR for ac97, shared with pxa2xx-ac97, and later with ucb1400-gpio and ucb1400-adc +static irqreturn_t ucb1400_ts_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + u32 status; + + struct ucb1400_ts *ucb_ts = (struct ucb1400_ts *) devid; + + status=GSR; + if (status & GSR_GSCI) { + GSR&=GSR_GSCI; + if (!(ucb_ts->irq_in_work)) { + //as we get all ac97-interrupts (they are shared), we can't turn them off + // we have to ignore them until slow-handler-thread (below) is thru + + ucb_ts->irq_in_work=TRUE; // FIXME this should be atomic, is it anyway??? + // we might miss an interrupt syncing with kthread this way if ints are comming quickly + // but its not a problem, we only need the last readout anyway + // and if we miss pen-up, we will test for pen-status anyway in kthread + wake_up_interruptible(&ucb_ts->pen_irq_wait); + } + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +/* +* The touchscreen reader thread. +*/ +static int ucb1400_ts_read_thread(void *data) +{ + unsigned long sleep_time; + u16 val; + int pen_was_down=FALSE; + unsigned long tstamp; // timestamp (jiffies) + struct ts_reading ts_values; + struct ucb1400_ts *ucb_ts = (struct ucb1400_ts *) data; + + /* set up thread context */ + ucb_ts->thread = current; + + daemonize("kucb1400d"); + + if (ucb_ts->ac97 == NULL) { + ucb_ts->thread = NULL; + printk(KERN_ERR "ac97-codec is NULL in read-thread, bailing out\n"); + } + + ucb_ts->irq_in_work=FALSE; + // read-rate in hertz => sleeptime in jiffies + sleep_time = msecs_to_jiffies(1000/cont_rate); + + ucb1400_ts_mode_int(ucb_ts, 0); + ucb1400_ts_mode_int(ucb_ts, UCB_IE_FAL); + complete(&ucb_ts->thr_init); + + /* touch reader loop */ + while (ucb_ts->thread) { + ucb_ts->irq_in_work = FALSE; + wait_event_interruptible( ucb_ts->pen_irq_wait, ucb_ts->irq_in_work); + // check if its our int + val=ucb1400_reg_read(ucb_ts, UCB_IE_STATUS); + if (val & UCB_IE_TSPX) { + do { + ucb1400_ts_mode_int(ucb_ts, 0); // int off / clear int + ucb1400_ts_take_reading(ucb_ts, &ts_values); + ucb1400_ts_mode_int(ucb_ts, UCB_IE_RIS); //int on rising edge=wait for penup + ucb1400_ts_report_event(ucb_ts, ts_values); + pen_was_down=TRUE; + //DBG("ucb1400_kthread x: %d y: %d p: %d, j:%lu\n",ts_values.x,ts_values.y,ts_values.p,jiffies); + + ucb_ts->irq_in_work=FALSE; + tstamp=jiffies; + // This marco needs a TRUE condition to return, Timeout alone without TRUE does NOT return, + // so make up a TRUE when timeout. A bit bloated though .... + wait_event_interruptible_timeout( ucb_ts->pen_irq_wait, (ucb_ts->irq_in_work) || + (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); + } while(! ucb_ts->irq_in_work); + + ucb1400_ts_mode_int(ucb_ts, UCB_IE_FAL); //int on falling edge=wait for pendwon + if (pen_was_down) { + ucb1400_ts_event_pen_up(ucb_ts); // do pen-up for input + pen_was_down=FALSE; + } else { + printk(KERN_ERR "Unexpected Programmer Failure: Pendown-IRQ without pendown ???\n"); + } + } + try_to_freeze(); + } + complete_and_exit(&ucb_ts->thr_exit, 0); +} + +static int __init ptc_ucb1400_ts_probe(struct device *dev) +{ + u16 val; + int err; + struct ucb1400_ts *ucb_ts; + int ret = -ENODEV; + + ucb_ts = kmalloc(sizeof(struct ucb1400_ts), GFP_KERNEL); + if (!ucb_ts) { + printk(KERN_ERR "Cant allocate memory for device struct\n"); + ret=-ENOMEM; + goto errout; + } + memset(ucb_ts, 0, sizeof(struct ucb1400_ts)); + ucb_ts->ac97 = to_ac97_t(dev); + dev->driver_data = ucb_ts; + val = ucb1400_reg_read(ucb_ts, AC97_VENDOR_ID2); + if (val != UCB_ID_1400) { + printk(KERN_ERR "no ucb1400 Chip ID (%04x) found: %04x\n",UCB_ID_1400, val); + ret=-ENODEV; + goto rollback1; + } + + printk(KERN_INFO "detected a ucb1400 codec on the ac97-bus, enabling touchscreen-driver"); + + /* setup the input device */ + ucb_ts->idev = input_allocate_device(); + if (ucb_ts->idev==NULL) { + printk(KERN_ERR "cannot allocate input-device for ucb1400-touchscreen\n"); + ret= -ENOMEM; + goto rollback1; + } + + ucb_ts->idev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + ucb_ts->idev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(ucb_ts->idev, ABS_X, UCB_TS_X_MIN, UCB_TS_X_MAX, 0, 0); + input_set_abs_params(ucb_ts->idev, ABS_Y, UCB_TS_Y_MIN, UCB_TS_Y_MAX, 0, 0); + input_set_abs_params(ucb_ts->idev, ABS_PRESSURE, UCB_TS_PRESSURE_MIN, UCB_TS_PRESSURE_MAX, 1, 1); + + ucb_ts->idev->name = "Palm Tungsten C touchscreen (ucb1400)"; + ucb_ts->idev->dev = dev; + input_register_device(ucb_ts->idev); + + // clear ucb1400-ints if there are + val=ucb1400_reg_read(ucb_ts, UCB_IE_STATUS); + ucb1400_reg_write(ucb_ts, UCB_IE_CLEAR, val); + + // start reader-thread + init_completion(&ucb_ts->thr_init); + init_completion(&ucb_ts->thr_exit); + init_waitqueue_head(&ucb_ts->pen_irq_wait); + ucb1400_ts_mode_int(ucb_ts, 0); + ret = kernel_thread(ucb1400_ts_read_thread, ucb_ts, CLONE_KERNEL); + if (ret > 0) { + wait_for_completion(&ucb_ts->thr_init); + if (ucb_ts->thread == NULL) { + printk(KERN_ERR "cannot start kernel-thread for ucb1400-touchscreen\n"); + ret=-EINVAL; + goto rollback2; + } + } + + // get our share of AC97-Bus interrupt + err = request_irq(IRQ_AC97, ucb1400_ts_irq, SA_SHIRQ, "ucb1400-ts", ucb_ts); + if (err) { + printk(KERN_ERR "ucb1400: IRQ request failed\n"); + ret=-ENODEV; + goto rollback3; + } + + ucb_ts->last_status=0; + val= ucb1400_reg_read(ucb_ts, UCB_CSR1); // remeber old int-status and set ints to ON + if (val & UCB_CS_GPIO_AC97_INT) { + ucb_ts->last_status&=UCB_CS_GPIO_AC97_INT; + } else { + ucb1400_reg_write(ucb_ts, UCB_CSR1, UCB_CS_GPIO_AC97_INT); // enable "gpio"-interrupt on ucb1400 + } + val=GCR; + if (val & GCR_GIE) { + ucb_ts->last_status&=GCR_GIE; + } else { + GCR|=GCR_GIE; // enable "gpio"-interrupt on pxa-ac97 + } + + //DBG("ucb1400_probe done\n"); + return 0; + +rollback3: + if (ucb_ts->thread) { + ucb_ts->thread = NULL; + ucb_ts->irq_in_work = TRUE; + wake_up_interruptible(&ucb_ts->pen_irq_wait); + wait_for_completion(&ucb_ts->thr_exit); + } +rollback2: + input_unregister_device(ucb_ts->idev); +rollback1: + kfree(ucb_ts); +errout: + return ret; +} + +static int ptc_ucb1400_ts_remove(struct device *dev) +{ + struct ucb1400_ts *ucb_ts = dev_get_drvdata(dev); + + // disable "GPIO"-int in pxa-ac97 if we found it disabled + if ((ucb_ts->last_status & GCR_GIE) == 0) + GCR&=~GCR_GIE; + if ((ucb_ts->last_status & UCB_CS_GPIO_AC97_INT) == 0) + ucb1400_reg_write(ucb_ts, UCB_CSR1, UCB_CS_GPIO_AC97_INT); + + ucb1400_ts_mode_int(ucb_ts, 0); + + //stop kthread + if (ucb_ts->thread) { + ucb_ts->thread = NULL; + ucb_ts->irq_in_work = TRUE; + wake_up_interruptible(&ucb_ts->pen_irq_wait); + wait_for_completion(&ucb_ts->thr_exit); + } + + input_unregister_device(ucb_ts->idev); + free_irq(IRQ_AC97, ucb_ts); + kfree(ucb_ts); + return 0; +} + +static struct device_driver ptc_ucb1400_ts_driver = { + .name = "ucb1400-ts", + .bus = &ac97_bus_type, + .probe = ptc_ucb1400_ts_probe, + .remove = ptc_ucb1400_ts_remove, +#ifdef CONFIG_PM + .suspend = NULL, //FIXME do resume and suspend + .resume = NULL, +#endif +}; + +static int __init ptc_ucb1400_ts_init(void) +{ + + return driver_register(&ptc_ucb1400_ts_driver); +} + +static void ptc_ucb1400_ts_exit(void) +{ + driver_unregister(&ptc_ucb1400_ts_driver); +} + +module_init(ptc_ucb1400_ts_init); +module_exit(ptc_ucb1400_ts_exit); + +MODULE_AUTHOR ("Holger Bocklet <bit...@gm...>"); +MODULE_DESCRIPTION ("UCB1400 touchscreen support for Palm Tungsten C"); +MODULE_LICENSE ("GPL"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <hol...@us...> - 2007-01-10 11:24:22
|
Revision: 747 http://svn.sourceforge.net/hackndev/?rev=747&view=rev Author: holger_bocklet Date: 2007-01-10 03:24:16 -0800 (Wed, 10 Jan 2007) Log Message: ----------- ptc_ucb1400 fix hang with apm-battery Modified Paths: -------------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c 2007-01-08 11:43:37 UTC (rev 746) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c 2007-01-10 11:24:16 UTC (rev 747) @@ -30,6 +30,8 @@ #include <linux/device.h> #include <linux/string.h> #include <linux/ctype.h> +#include <linux/time.h> +#include <linux/mutex.h> #include <asm/delay.h> #include <asm/arch/hardware.h> @@ -61,22 +63,31 @@ ac97_t *ac97; struct input_dev *idev; struct task_struct *thread; - wait_queue_head_t pen_irq_wait; - wait_queue_head_t adc_irq_wait; + //wait_queue_head_t pen_irq_wait; + //wait_queue_head_t adc_irq_wait; int pen_irq; int adc_irq; struct workqueue_struct *palmtc_ucb1400_workqueue; struct work_struct palmtc_ucb1400_work; struct completion thr_init; struct completion thr_exit; - struct semaphore adc_sem; + struct completion pen_irq_wait; + struct completion adc_irq_wait; + struct mutex adc_mutex; + struct mutex ucb_reg_mutex; u8 last_status; }; #ifdef CONFIG_APM +#define APM_MIN_INTERVAL 500 struct ucb1400 *ucb_static_copy; + struct mutex apm_mutex; + struct apm_power_info info_cache; + unsigned long cache_tstamp; #endif +//static struct timeval t; + struct ts_reading { u16 p; u16 x; @@ -89,7 +100,7 @@ // Pen sampling frequency (Hz) while touched. // more is not possible cause jiffies are at 100Hz in mach-pxa (why ???) -static int cont_rate = 100; +static int cont_rate = 80; module_param(cont_rate, int, 0); MODULE_PARM_DESC(cont_rate, "Sampling rate while pen down (Hz). Not more than ~100Hz on T|C"); @@ -120,17 +131,22 @@ * can only be called from process context, not interrupt * context. */ -void ucb1400_adc_enable(struct ucb1400 *ucb) +int ucb1400_adc_enable(struct ucb1400 *ucb) { u16 val=0; - down(&ucb->adc_sem); + + if (unlikely(mutex_lock_interruptible(&ucb->adc_mutex))) + return -ERESTARTSYS; ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); - val=ucb1400_reg_read(ucb, UCB_CSR2); - ucb1400_reg_write(ucb, UCB_CSR2, val | UCB_ADC_FILTER_ENA); - + mutex_lock(&ucb->ucb_reg_mutex); val=ucb1400_reg_read(ucb, UCB_IE_RIS); ucb1400_reg_write(ucb, UCB_IE_RIS, val | UCB_IE_ADC); + val=ucb1400_reg_read(ucb, UCB_IE_FAL); + ucb1400_reg_write(ucb, UCB_IE_FAL, val | UCB_IE_ADC); + mutex_unlock(&ucb->ucb_reg_mutex); + + return 0; } @@ -143,11 +159,17 @@ void ucb1400_adc_disable(struct ucb1400 *ucb) { u16 val=0; + + mutex_lock(&ucb->ucb_reg_mutex); val &= ~UCB_ADC_ENA; ucb1400_reg_write(ucb, UCB_ADC_CR, val); val=ucb1400_reg_read(ucb, UCB_IE_RIS); ucb1400_reg_write(ucb, UCB_IE_RIS, val & (~UCB_IE_ADC)); - up(&ucb->adc_sem); + val=ucb1400_reg_read(ucb, UCB_IE_FAL); + ucb1400_reg_write(ucb, UCB_IE_FAL, val & (~UCB_IE_ADC)); + mutex_unlock(&ucb->ucb_reg_mutex); + + mutex_unlock(&ucb->adc_mutex); } @@ -161,9 +183,10 @@ * This function currently sleeps waiting for the conversion to * complete (2 frames max without sync). */ -unsigned int ucb1400_adc_read(struct ucb1400 *ucb, int adc_channel) +unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel) { unsigned int val; + //unsigned long tstamp, sleep_time; adc_channel |= UCB_ADC_ENA; @@ -171,15 +194,38 @@ ucb1400_reg_write(ucb, UCB_ADC_CR, adc_channel | UCB_ADC_START); ucb->adc_irq=FALSE; + val = ucb1400_reg_read(ucb, UCB_ADC_DATA); + if ( !(val & UCB_ADC_DATA_VALID)) { + wait_for_completion_interruptible_timeout(&ucb->adc_irq_wait,UCB_ADC_TIMEOUT); + val = ucb1400_reg_read(ucb, UCB_ADC_DATA); + +/* ucb->adc_irq=FALSE; wait_event_interruptible( ucb->adc_irq_wait, ucb->adc_irq); - ucb->adc_irq=FALSE; + sleep_time=UCB_ADC_TIMEOUT; + tstamp=jiffies; + wait_event_interruptible_timeout( ucb->adc_irq_wait, (ucb->adc_irq) || + (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); +*/ + if (likely(ucb->adc_irq)) { + ucb->adc_irq=FALSE; + } else { + //do_gettimeofday(&t); + //DBG("adc read timeout: %ld %ld\n",t.tv_sec,t.tv_usec); + printk(KERN_ERR "%s: read error (timeout on adc%1u), value:0x%x\n", + __FUNCTION__,(adc_channel&0x0c)>>2, val); + + } - val = ucb1400_reg_read(ucb, UCB_ADC_DATA); - + if (unlikely ( !(val & UCB_ADC_DATA_VALID) ) ){ + printk(KERN_ERR "%s: read error (data invalid on adc%1u), value:0x%x\n", + __FUNCTION__,(adc_channel&0x0c)>>2, val); + } + } return val & UCB_ADC_DATA_MASK; } + // create sysfs-entries for adc+gpio #define UCB1400_SYSFS_ATTR_SHOW(name,input); \ static ssize_t ucb1400_sysfs_##name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ @@ -220,24 +266,27 @@ if (!(isdigit(buf[1]))) return -EINVAL; io_nr=simple_strtol(buf+1, NULL, 10); + + mutex_lock(&ucb->ucb_reg_mutex); val=ucb1400_reg_read(ucb, UCB_IO_DIR); ucb1400_reg_write(ucb, UCB_IO_DIR, val | (1 << io_nr)); // set output val=ucb1400_reg_read(ucb, UCB_IO_DATA); ucb1400_reg_write(ucb, UCB_IO_DATA, val & (~(1 << io_nr))); // unset bit + mutex_unlock(&ucb->ucb_reg_mutex); } else { if (strlen(buf)>2) return -EINVAL; if (!(isdigit(buf[0]))) return -EINVAL; io_nr=simple_strtol(buf, NULL, 10); + mutex_lock(&ucb->ucb_reg_mutex); val=ucb1400_reg_read(ucb, UCB_IO_DIR); ucb1400_reg_write(ucb, UCB_IO_DIR, val | (1 << io_nr)); // set output val=ucb1400_reg_read(ucb, UCB_IO_DATA); ucb1400_reg_write(ucb, UCB_IO_DATA, val | (1 << io_nr)); // set bit + mutex_unlock(&ucb->ucb_reg_mutex); } - val1=ucb1400_reg_read(ucb, UCB_IO_DIR); - val2=ucb1400_reg_read(ucb, UCB_IO_DATA); return 2; } static DEVICE_ATTR(gpio, 0664, ucb1400_sysfs_io_show, ucb1400_sysfs_io_store); @@ -246,8 +295,26 @@ static void palmtc_get_power_status(struct apm_power_info *info) { struct ucb1400 *ucb=ucb_static_copy; - u16 battery=0; + u16 battery, a,b,c; + int i; + + if (mutex_lock_interruptible(&apm_mutex)) + return; + //do_gettimeofday(&t); + //DBG("apm-call sem-down: %ld %ld\n",t.tv_sec,t.tv_usec); + + // if last call with hardware-read is < 5 Sek just return cached info + if ( ((unsigned long)((long)jiffies - (long)cache_tstamp) ) < APM_MIN_INTERVAL ) { + info->ac_line_status = info_cache.ac_line_status; + info->battery_status = info_cache.battery_status; + info->battery_flag = info_cache.battery_flag; + info->battery_life = info_cache.battery_life; + info->time = info_cache.time; + mutex_unlock(&apm_mutex); + return; + } + //DBG("apm-call read\n"); info->battery_flag = 0; battery=ucb1400_reg_read(ucb, UCB_IO_DATA); if (battery & UCB_IO_0) { @@ -258,11 +325,22 @@ info->ac_line_status = APM_AC_OFFLINE; } - while ( || - ucb1400_adc_enable(ucb); + if (ucb1400_adc_enable(ucb)) + return; + battery=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + for (i=0;(i<3);i++) { + a=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + b=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + c=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + if ((a==b) && (b==c)) { + battery=c; + break; + } else { + battery=(a+b+c+battery)/4; + } + } ucb1400_adc_disable(ucb); - DBG("batt: %u\n",battery); if (battery > UCB_BATT_HIGH) { info->battery_flag |= APM_BATTERY_FLAG_HIGH; @@ -282,8 +360,16 @@ if (info->battery_life>100) info->battery_life=100; info->time = info->battery_life * 6; - //info->battery_life = ((info->time * 100) / UCB_BATT_LIFE); info->units = APM_UNITS_MINS; + + info_cache.ac_line_status=info->ac_line_status; + info_cache.battery_status=info->battery_status; + info_cache.battery_flag=info->battery_flag; + info_cache.battery_life=info->battery_life; + info_cache.time=info->time; + cache_tstamp=jiffies; + + mutex_unlock(&apm_mutex); } #endif @@ -316,6 +402,8 @@ u16 val; // clear remaining ints + mutex_lock(&ucb->ucb_reg_mutex); + val=ucb1400_reg_read(ucb, UCB_IE_STATUS); ucb1400_reg_write(ucb, UCB_IE_CLEAR, val); @@ -331,6 +419,7 @@ val=ucb1400_reg_read(ucb, UCB_IE_RIS); ucb1400_reg_write(ucb, UCB_IE_RIS, val & (~UCB_IE_TSPX) & (~UCB_IE_TSMX)); //ucb1400_reg_write(ucb, UCB_IE_RIS, val & (~UCB_IE_TSPX)); + //DBG("ts irq-off"); } else { @@ -354,6 +443,8 @@ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_INT); } + mutex_unlock(&ucb->ucb_reg_mutex); + //DBG("ts irqcontrol: fal: 0x%x ris: 0x%x\n",ucb1400_reg_read(ucb, UCB_IE_FAL),ucb1400_reg_read(ucb, UCB_IE_RIS)); } @@ -420,15 +511,18 @@ } -static void ucb1400_ts_take_reading(struct ucb1400 *ucb, struct ts_reading *ts) +static int ucb1400_ts_take_reading(struct ucb1400 *ucb, struct ts_reading *ts) { - ucb1400_adc_enable(ucb); + //ucb1400_adc_enable(ucb); + if (ucb1400_adc_enable(ucb)) + return -ERESTARTSYS; ts->x = ucb1400_ts_read_xpos(ucb); ts->y = ucb1400_ts_read_ypos(ucb); ts->p = ucb1400_ts_read_pressure(ucb); ucb1400_adc_disable(ucb); + return 0; } // ISR for ac97, shared with pxa2xx-ac97, and later with ucb1400-gpio and ucb1400-adc @@ -438,7 +532,7 @@ struct ucb1400 *ucb = (struct ucb1400 *) devid; status=GSR; - if (status & GSR_GSCI) { + if (likely(status & GSR_GSCI)) { GSR&=GSR_GSCI; queue_work(ucb->palmtc_ucb1400_workqueue, &ucb->palmtc_ucb1400_work); @@ -455,28 +549,33 @@ struct ucb1400 *ucb = (struct ucb1400 *) data; u16 val,val1,val2; - // check which irq + //mutex_trylock(&ucb->ucb_reg_mutex); don't: this delays irqs ! + // check which irq and clear val=ucb1400_reg_read(ucb, UCB_IE_STATUS); + ucb1400_reg_write(ucb, UCB_IE_CLEAR, val); val1=ucb1400_reg_read(ucb, UCB_IE_EXTRA); - // clear irqs - ucb1400_reg_write(ucb, UCB_IE_CLEAR, val); ucb1400_reg_write(ucb, UCB_IE_EXTRA, val1); + //mutex_unlock(&ucb->ucb_reg_mutex); - if (val & UCB_IE_TSPX) { + //if ((val & UCB_IE_ADC) && (ucb->adc_irq==FALSE)) { + if ((val & UCB_IE_ADC) ) { + ucb->adc_irq=TRUE; + complete(&ucb->adc_irq_wait); + //wake_up_interruptible(&ucb->adc_irq_wait); + } + //if ((val & UCB_IE_TSPX) && (ucb->pen_irq==FALSE)) { + if ((val & UCB_IE_TSPX)) { ucb->pen_irq=TRUE; - //DBG("irq_wq: TS\n"); - wake_up_interruptible(&ucb->pen_irq_wait); + complete(&ucb->pen_irq_wait); + //do_gettimeofday(&t); + //DBG("irq_wq_TS: %ld %ld\n",t.tv_sec,t.tv_usec); + //wake_up_interruptible(&ucb->pen_irq_wait); } - if (val & UCB_IE_ADC) { - ucb->adc_irq=TRUE; - //DBG("irq_wq: ADC\n"); - wake_up_interruptible(&ucb->adc_irq_wait); - } if (val & UCB_IE_IO) { //show it val1=ucb1400_reg_read(ucb, UCB_IO_DIR); val2=ucb1400_reg_read(ucb, UCB_IO_DATA); - //DBG("gpio_irq: irq: 0x%u dir: %u data: %u\n",val,val1,val2); + DBG("gpio_irq: irq: 0x%u dir: %u data: %u\n",val,val1,val2); } } @@ -488,7 +587,7 @@ { unsigned long sleep_time; int pen_was_down=FALSE; - unsigned long tstamp; // timestamp (jiffies) + //unsigned long tstamp; // timestamp (jiffies) struct ts_reading ts_values; struct ucb1400 *ucb = (struct ucb1400 *) data; @@ -512,21 +611,27 @@ /* touch reader loop */ while (ucb->thread) { ucb->pen_irq=FALSE; - wait_event_interruptible( ucb->pen_irq_wait, ucb->pen_irq); + //do_gettimeofday(&t); + //DBG("kthread-1: %ld %ld\n",t.tv_sec,t.tv_usec); + //wait_event_interruptible( ucb->pen_irq_wait, ucb->pen_irq); + wait_for_completion_interruptible(&ucb->pen_irq_wait); if (ucb->thread) { // are we still alive? do { ucb1400_ts_mode_int(ucb, 0); // int off / clear int - ucb1400_ts_take_reading(ucb, &ts_values); + if (! (ucb1400_ts_take_reading(ucb, &ts_values))) { + ucb1400_ts_mode_int(ucb, UCB_IE_RIS); //int on rising edge=wait for penup ucb1400_ts_report_event(ucb, ts_values); pen_was_down=TRUE; //DBG("kthread x: %d y: %d p: %d\n",ts_values.x,ts_values.y,ts_values.p); - tstamp=jiffies; + //tstamp=jiffies; // This marco needs a TRUE condition to return, Timeout alone without TRUE does NOT return, // so make up a TRUE when timeout. A bit bloated though .... ucb->pen_irq=FALSE; - wait_event_interruptible_timeout( ucb->pen_irq_wait, (ucb->pen_irq) || - (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); + wait_for_completion_interruptible_timeout(&ucb->pen_irq_wait,sleep_time); + //wait_event_interruptible_timeout( ucb->pen_irq_wait, (ucb->pen_irq) || + // (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); + } } while(! ucb->pen_irq); if (ucb->thread) @@ -596,9 +701,14 @@ ucb1400_reg_write(ucb, UCB_IE_EXTRA, val); // start reader-thread for touchscreen + ucb->pen_irq=FALSE; + ucb->adc_irq=FALSE; init_completion(&ucb->thr_init); init_completion(&ucb->thr_exit); - init_waitqueue_head(&ucb->pen_irq_wait); + init_completion(&ucb->pen_irq_wait); + mutex_init(&ucb->ucb_reg_mutex); + //init_MUTEX(&ucb->ucb_reg_sem); + //init_waitqueue_head(&ucb->pen_irq_wait); //ucb1400_ts_mode_int(ucb, 0); ret = kernel_thread(ucb1400_ts_read_thread, ucb, CLONE_KERNEL); if (ret > 0) { @@ -614,8 +724,14 @@ INIT_WORK(&ucb->palmtc_ucb1400_work, palmtc_ucb1400_irq_queuework, ucb); //adc - init_waitqueue_head(&ucb->adc_irq_wait); - sema_init(&ucb->adc_sem, 1); + val=ucb1400_reg_read(ucb, UCB_CSR2); + ucb1400_reg_write(ucb, UCB_CSR2, val | UCB_ADC_FILTER_ENA); + + //init_waitqueue_head(&ucb->adc_irq_wait); + init_completion(&ucb->adc_irq_wait); + mutex_init(&ucb->adc_mutex); + //init_MUTEX(&ucb->adc_sem); + //sema_init(&ucb->adc_sem, 1); // get our share of AC97-Bus interrupt err = request_irq(IRQ_AC97, ucb1400_irq, SA_SHIRQ, "ucb1400", ucb); @@ -653,8 +769,18 @@ device_create_file(dev, &dev_attr_gpio); #ifdef CONFIG_APM + //sema_init(&apm_sem, 1); + info_cache.ac_line_status=APM_AC_UNKNOWN; + info_cache.battery_status=APM_BATTERY_STATUS_UNKNOWN; + info_cache.battery_flag=APM_BATTERY_FLAG_UNKNOWN; + info_cache.battery_life=-1; + info_cache.time=-1; + info_cache.units=APM_UNITS_MINS; + cache_tstamp=0; + //init_MUTEX(&apm_sem); + mutex_init(&apm_mutex); + ucb_static_copy=ucb; //sorry for this, apm-power-function doesnt have a parm to pass dev... apm_get_power_status = palmtc_get_power_status; - ucb_static_copy=ucb; //sorry for this, apm-power-function doesnt have a parm to pass dev... #endif @@ -664,7 +790,9 @@ if (ucb->thread) { ucb->thread = NULL; ucb->pen_irq = TRUE; - wake_up_interruptible(&ucb->pen_irq_wait); + complete_all(&ucb->pen_irq_wait); + complete_all(&ucb->adc_irq_wait); + //wake_up_interruptible(&ucb->pen_irq_wait); wait_for_completion(&ucb->thr_exit); } rollback2: @@ -690,6 +818,9 @@ #ifdef CONFIG_APM apm_get_power_status = NULL;; #endif + ucb->adc_irq = TRUE; + complete_all(&ucb->adc_irq_wait); + //wake_up_interruptible(&ucb->adc_irq_wait); // disable "GPIO"-int in pxa-ac97 if we found it disabled if ((ucb->last_status & GCR_GIE) == 0) @@ -700,14 +831,12 @@ ucb1400_reg_write(ucb, UCB_CSR1, val); } - ucb->adc_irq = TRUE; - wake_up_interruptible(&ucb->adc_irq_wait); - //stop kthread if (ucb->thread) { ucb->thread = NULL; ucb->pen_irq = TRUE; - wake_up_interruptible(&ucb->pen_irq_wait); + complete_all(&ucb->pen_irq_wait); + //wake_up_interruptible(&ucb->pen_irq_wait); wait_for_completion(&ucb->thr_exit); } Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2007-01-08 11:43:37 UTC (rev 746) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2007-01-10 11:24:16 UTC (rev 747) @@ -43,7 +43,7 @@ #define UCB_IE_TSMX (1 << 13) #define UCB_IE_TCLIP (1 << 14) #define UCB_IE_ACLIP (1 << 15) -#define UCB_IE_IO (0x1FF) +#define UCB_IE_IO (0x3FF) #define UCB_TC_B_VOICE_ENA (1 << 3) #define UCB_TC_B_CLIP (1 << 4) @@ -89,8 +89,8 @@ #define UCB_ADC_FILTER_ENA (1 << 12) -#define UCB_ADC_DATA_MASK 0x3ff -#define UCB_ADC_DATA_VALID (1 << 15) +#define UCB_ADC_DATA_MASK 0x3ff +#define UCB_ADC_DATA_VALID (1 << 15) #define UCB_ID_1400 0x4304 @@ -122,3 +122,4 @@ #define UCB_BATT_CRITICAL 480 #define UCB_BATT_LIFE 600 // battery duration in minutes +#define UCB_ADC_TIMEOUT 100 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <hol...@us...> - 2007-01-11 12:10:48
|
Revision: 753 http://svn.sourceforge.net/hackndev/?rev=753&view=rev Author: holger_bocklet Date: 2007-01-11 04:10:39 -0800 (Thu, 11 Jan 2007) Log Message: ----------- minor cleanup for ptc_ucb1400 and enabled backlight-control Modified Paths: -------------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c 2007-01-10 17:49:39 UTC (rev 752) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/palmtc.c 2007-01-11 12:10:39 UTC (rev 753) @@ -22,13 +22,14 @@ #include <asm/arch/pxa-dmabounce.h> #include <asm/arch/pxafb.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/pxapwm-bl.h> #include "../generic.h" #include "palmtc-gpio.h" #define DEBUG -static void palm_backlight_power(int on) +static void palmtc_backlight_power(int on) { if(on) { PWM_CTRL1 = 0x1; @@ -55,7 +56,7 @@ .lccr0 = 0x07B008F9, .lccr3 = 0x04700004, - .pxafb_backlight_power = palm_backlight_power, + .pxafb_backlight_power = palmtc_backlight_power, }; @@ -95,7 +96,17 @@ }; +/* Backlight ***/ +static struct pxapwmbl_platform_data palmtc_backlight_data = { + .pwm = 1, + .max_intensity = 0x100, //120 + .default_intensity = 0x50, //11a + .limit_mask = 0x7F, + .prescaler = 1, // 7 + .period = 0x12B, // 16c +}; + static pxa2xx_audio_ops_t palmtc_audio_ops = { /* .startup = palmld_audio_startup, @@ -105,7 +116,14 @@ */ }; +static struct platform_device palmtc_backlight = { + .name = "pxapwm-bl", + .dev = { + .platform_data = &palmtc_backlight_data, + }, +}; + static struct platform_device palmtc_ac97_device = { .name = "pxa2xx-ac97", .id = -1, @@ -121,6 +139,7 @@ static struct platform_device *devices[] __initdata = { &palmtc_ac97_device, &palmtc_keyboard_device, + &palmtc_backlight, }; static void __init palmtc_init(void) Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c 2007-01-10 17:49:39 UTC (rev 752) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c 2007-01-11 12:10:39 UTC (rev 753) @@ -79,7 +79,7 @@ }; #ifdef CONFIG_APM -#define APM_MIN_INTERVAL 500 +#define APM_MIN_INTERVAL 1000 struct ucb1400 *ucb_static_copy; struct mutex apm_mutex; struct apm_power_info info_cache; @@ -356,10 +356,15 @@ info->battery_status = APM_BATTERY_STATUS_CRITICAL; } } - info->battery_life = ((battery - UCB_BATT_CRITICAL) * 125) / 100; - if (info->battery_life>100) + info->battery_life = ((battery - UCB_BATT_CRITICAL) / (UCB_BATT_HIGH-UCB_BATT_CRITICAL) ) * 100; //battery_life = % + if (info->battery_life>100) { info->battery_life=100; - info->time = info->battery_life * 6; + } else { + if (info->battery_life<0) { + info->battery_life=0; + } + } + info->time = info->battery_life * UCB_BATT_DURATION / 100; info->units = APM_UNITS_MINS; info_cache.ac_line_status=info->ac_line_status; Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2007-01-10 17:49:39 UTC (rev 752) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2007-01-11 12:10:39 UTC (rev 753) @@ -115,11 +115,8 @@ #define UCB_TS_FUZZ 15 #define UCB_BATT_HIGH 560 // adc "ticks" not mV ! -#define UCB_BATT_HIGH_MIN_MUL 20000 // multiplikator adc-ticks => minutes -#define UCB_BATT_NORM_MIN_MUL 1073 // multiplikator adc-ticks => minutes #define UCB_BATT_LOW 490 -#define UCB_BATT_LOW_MIN_MUL 2941 // multiplikator adc-ticks => minutes #define UCB_BATT_CRITICAL 480 -#define UCB_BATT_LIFE 600 // battery duration in minutes -#define UCB_ADC_TIMEOUT 100 +#define UCB_BATT_DURATION 600 // battery duration in minutes +#define UCB_ADC_TIMEOUT 50 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |