/* * Driver interface to the touchscreen on the iPAQ H4000 * * Copyright (C) 2003 Joshua Wise * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * * HAL code based on h5400_asic_io.c, which is * Copyright (C) 2003 Compaq Computer Corporation. * * TS Code: Joshua Wise June 2003 * * InputDev Code: Luke Kenneth Casson Leighton (Project Xanadux) * */ #include #include #include #include #include #include #include #include #include #include #include #include // doesnt exist in 2.6 ... //#include #include #include #include #include #include #define SAMPLE_TIMEOUT 10 /* sample every 10ms */ /**** temporary FIXME: These should be completed and put into asm/mach/pxa-regs.h see the IntelĀ® PXA255 Processor Developer's Manual ****/ #define SSCR0_SSE_ENABLED 0x80 /* bit 7 of SSCR0; 1=on 0=off*/ #define SSCR0_ECS_USE_SSPEXTCLK 0x40 /* bit 6 of SSCR0; 1=SSPEXTCLK 0=on-chip*/ #define SSCR0_FRF_SPI 0x0 /* bits 5:4 of SSCR0 binary 00*/ #define SSCR0_FRF_SSP 0x10 /* bits 5:4 of SSCR0 binary 01*/ #define SSCR0_FRF_MICROWIRE 0x20 /* bits 5:4 of SSCR0 binary 10*/ #define SSCR1_MWDS_16BIT 0x20 /* bit 5 of SSCR1; 1=16bit */ #define SSCR1_SPH 0x10 /* bit 4 of SSCR1; motorola*/ #define SSCR1_SPO_FALLING 0x8 /* bit 3 of SSCR1; motorola*/ #define SSCR1_LBM 0x4 /* bit 2 of SSCR1 */ #define SSCR1_TIE 0x2 /* bit 1 of SSCR1 */ #define SSCR1_RIE 0x1 /* bit 0 of SSCR1 */ #define SSSR_RNE 0x8 /* bit 3 of SSSR */ #define SSSR_BSY 0x10 /* bit 4 of SSSR */ // FIXME input.h #define BUS_PXA_NSSP 0x20 static char ads7846_name[] = "ads7846"; static char ads7846_phys[] = "touchscreen/ads7846"; static struct input_dev dev_ads7846; //#define SAMPLE_TIMEOUT 40/* sample every 10ms */ #define CONVERSION_TIMEOUT 2/* wait 1ms for a conversion */ #undef UBERBUGGY static struct timer_list timer; typedef enum { ADS_DO_NOTHING = 0, ADS_PEN_DOWN_EXPECT_CONVERT, ADS_PEN_DOWN_CONVERT_DONE, ADS_PEN_UP, } ADS_PEN_STATE; static int ads_state = ADS_DO_NOTHING; static unsigned int spi_ctrl(unsigned char ctrlbyte) { /* prevent a race condition in the while loops */ int antirace; SSDR = ctrlbyte; /* wait until we have completed sending. */ antirace = 100000; while ((SSSR & SSSR_BSY) && antirace) { udelay(10); antirace--; }; if (antirace == 0) { printk("%s: warning: timeout while waiting for sssr_bsy to clear\n", __FUNCTION__); }; antirace = 100000; /* wait for the receive fifo to become not empty. */ while ((~SSSR & SSSR_RNE) && antirace) { udelay(10); antirace--; }; if (antirace == 0) { printk("%s: warning: timeout while waiting for sssr_rne to set\n", __FUNCTION__); }; return SSDR & 0xFFFF; }; #define CTRL_START 0x80 #define CTRL_YPOS 0x10 #define CTRL_Z1POS 0x30 #define CTRL_Z2POS 0x40 #define CTRL_XPOS 0x50 static irqreturn_t h4000_pen_up(int irq, void* data, struct pt_regs *regs) { struct input_dev *dev = (struct input_dev *)data; // nss_cmd_adc_stopscan_c3d2(0x2); //nssp_disable(); /* tell the input thingy that we have been released. */ input_report_abs(dev, ABS_PRESSURE, 0); input_sync(dev); ads_state = ADS_PEN_UP; return IRQ_HANDLED; } static irqreturn_t h4000_pen_down(int irq, void* data, struct pt_regs *regs) { int buswait = 4; // nss_cmd_adc_stopscan_c3d2(0x2); // nss_cmd_adc_staradsan( // ADS2200_CTRLREG_ADC_PSM_ADS2200 | // controlled by penirq // ADS2200_CTRLREG_ADC_AD1 | // ADS2200_CTRLREG_ADC_RES(ADS2200_CTRLREG_ADC_RES_12BIT) | // ADS2200_CTRLREG_ADC_AVG(ADS2200_CTRLREG_ADC_16AVG) | // ADS2200_CTRLREG_ADC_CL(ADS2200_CTRLREG_ADC_CL_1MHZ_12BIT) | // ADS2200_CTRLREG_ADC_PV(ADS2200_CTRLREG_ADC_PV_100uS)); // printk("%s: GRER: %8x\n", __FUNCTION__, GRER0); while (((GRER0 & GPIO_bit(GPIO18_RDY))) && buswait) { udelay(1000); buswait--; } if (buswait) { // nss_cmd_adc_stopscan_c3d2(0x2); ads_state = ADS_PEN_DOWN_EXPECT_CONVERT; /* and, timerize it. */ disable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N)); mod_timer (&timer, jiffies + (CONVERSION_TIMEOUT * HZ) / 1000); } else { // unsigned short r4; // nss_initialise(); // r4 = get_key(0, 4); // printk("%s: bus wait timeout: %d\n", __FUNCTION__, r4); //nssp_disable(); // ads_state = ADS_PEN_UP; } return IRQ_HANDLED; } static irqreturn_t h4000_pen_down_read(int irq, void* data, struct pt_regs *regs) { unsigned int bytes[4]; struct input_dev *dev = (struct input_dev *)data; //nssp_enable(); //nss_initialise(); /* read positions. */ // bytes[0] = spi_ctrl(ADS2200_READ_DATAREG_X); // bytes[1] = spi_ctrl(ADS2200_READ_DATAREG_Y); // bytes[2] = spi_ctrl(ADS2200_READ_DATAREG_Z1); // bytes[3] = spi_ctrl(ADS2200_READ_DATAREG_Z2); bytes[0] = spi_ctrl(CTRL_START | CTRL_XPOS); bytes[0] ^= 0xfff; bytes[1] = spi_ctrl(CTRL_START | CTRL_YPOS); bytes[2] = spi_ctrl(CTRL_START | CTRL_Z1POS); bytes[3] = spi_ctrl(CTRL_START | CTRL_Z2POS); // nss_cmd_adc_staradsan_83d6(0x2); //nssp_disable(); /* tell the input thingy about the position */ /*h3600_hal_touchpanel(bytes[3], bytes[0], 1);*/ /* FIXME: find out why the read data reg conversion isn't completing * on time */ if (bytes[0] != 0xffff && bytes[1] != 0xffff) { input_report_abs(dev, ABS_X, bytes[0] & 0xfff); input_report_abs(dev, ABS_Y, bytes[1] & 0xfff); input_report_abs(dev, ABS_PRESSURE, 0xfff); input_sync(dev); } ads_state = ADS_PEN_DOWN_CONVERT_DONE; /* output positions. */ /* You're an uberdebuggy, NonToxic * YOU"RE AN UBERDEBUGGY */ //#if 1 //#ifdef UBERBUGGY printk("%s: %03X-%03x-%03x-%03x\n", __FUNCTION__, bytes[0], bytes[1], bytes[2], bytes[3]); //#endif //#endif /* and, timerize it. */ disable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N)); mod_timer (&timer, jiffies + (SAMPLE_TIMEOUT * HZ) / 1000); return IRQ_HANDLED; } static irqreturn_t h4000_pen(int irq, void* data, struct pt_regs *regs) { int pressed; // struct input_dev *dev = (struct input_dev *)data; //unsigned char ctrlbyte; // unsigned int bytes[4]; pressed = GPLR(GPIO_NR_H4000_PEN_IRQ_N) & GPIO_bit(GPIO_NR_H4000_PEN_IRQ_N); pressed = !pressed; //#if 0 //#ifdev UBERBUGGY // printk("%s: pressed: %d state: %d\n", __FUNCTION__, pressed, ads_state); //#endif //#endif /* tell the HAL about the position */ // FIXME: need a replacement for linux 2.6 //h3600_hal_touchpanel(bytes[3], bytes[0], 1); // /* and, timerize it. */ // mod_timer (&timer, jiffies + (SAMPLE_TIMEOUT * HZ) / 1000); if (ads_state == ADS_PEN_DOWN_CONVERT_DONE) { // a conversion has been completed, we need to check if // pen is up: if so, do pen up. if (!pressed) { return h4000_pen_up(irq, data, regs); }; // a conversion has been completed, pen is still down, start again ads_state = ADS_DO_NOTHING; } if (ads_state == ADS_PEN_UP && pressed) ads_state = ADS_DO_NOTHING; if (ads_state == ADS_DO_NOTHING) { return h4000_pen_down(irq, data, regs); } /* initiated pen down already, been expecting convert to finish */ if (ads_state == ADS_PEN_DOWN_EXPECT_CONVERT) return h4000_pen_down_read(irq, data, regs); return IRQ_HANDLED; }; static void h4000_ts_timer(unsigned long nr) { h4000_pen(0,(void*)nr,NULL); enable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N)); }; int h4000_ts_open( struct input_dev *dev ) { // if ( !(machine_is_h4000() || machine_is_h4000()) ) { // printk("%s: unknown iPAQ model\n", __FUNCTION__/*, h3600_generic_name()*/ ); // return -ENODEV; // } printk("%s: initializing the touchscreen.\n", __FUNCTION__); /* disable before mucking about */ SSCR0 &= ~SSCR0_SSE_ENABLED; /* set to Microwire mode */ SSCR0 &= ~0x00000030; SSCR0 |= SSCR0_FRF_MICROWIRE; /* don't use the external clock */ SSCR0 &= ~SSCR0_ECS_USE_SSPEXTCLK; /* set to 12-bit mode */ SSCR0 &= ~0xF; SSCR0 |= 0xB; /* set to 100mhz. */ SSCR0 &= ~0xFF00; SSCR0 |= 0x1100; /* 17 decimal */ /* set to 8-bit command packets */ SSCR1 &= ~SSCR1_MWDS_16BIT; /* set polarity and phase to 0 */ SSCR1 &= ~SSCR1_SPO_FALLING; SSCR1 &= ~SSCR1_SPH; /* no interrupts, and no loopback mode. */ SSCR1 &= ~(SSCR1_RIE|SSCR1_TIE|SSCR1_LBM); /* now to set up GPIOs... */ GPDR(GPIO23_SCLK) |= GPIO_bit(GPIO23_SCLK); GPDR(GPIO24_SFRM) |= GPIO_bit(GPIO24_SFRM); GPDR(GPIO25_STXD) |= GPIO_bit(GPIO25_STXD); GPDR(GPIO26_SRXD) &= ~GPIO_bit(GPIO26_SRXD); //pxa_gpio_mode(GPIO23_SCLK_MD); pxa_gpio_mode(GPIO23_SCLK_md); //??? pxa_gpio_mode(GPIO24_SFRM_MD); pxa_gpio_mode(GPIO25_STXD_MD); pxa_gpio_mode(GPIO26_SRXD_MD); /* and enable it! */ SSCR0 |= SSCR0_SSE_ENABLED; printk("%s: init timer...\n", __FUNCTION__); /* set up the timer. */ init_timer(&timer); timer.function = h4000_ts_timer; timer.data = (unsigned long)dev; printk("%s: set irq...\n", __FUNCTION__); /* now set up the pen action GPIO */ set_irq_type(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N), IRQT_BOTHEDGE); request_irq (IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N), h4000_pen, SA_SAMPLE_RANDOM, "h4000_ts", (void*)dev); /* we may have to do a read here to make sure that penint comes on, in case it's in a weird state. */ spi_ctrl(CTRL_START); printk("%s: set irq...\n", __FUNCTION__); return 0; } void h4000_ts_close( struct input_dev *dev ) { disable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N)); SSCR0 &= ~SSCR0_SSE_ENABLED; /* make sure to free the IRQ... */ free_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N), dev); /* now free the timer... */ del_timer_sync (&timer); } static int h4000_ts_setup(struct input_dev *dev) { memset(dev, 0x0, sizeof(*dev)); init_input_dev(dev); dev->private = dev; dev->name = ads7846_name; dev->phys = ads7846_phys; dev->open = h4000_ts_open; dev->close = h4000_ts_close; set_bit(EV_ABS, dev->evbit); set_bit(ABS_X, dev->absbit); set_bit(ABS_Y, dev->absbit); set_bit(ABS_PRESSURE, dev->absbit); dev->absmin[ABS_PRESSURE] = 0; dev->absmax[ABS_PRESSURE] = 1; // dev->absmax[ABS_PRESSURE] = 0xfff; dev->absmax[ABS_X] = 0xfff; dev->absmax[ABS_Y] = 0xfff; dev->absfuzz[ABS_X] = 2; dev->absfuzz[ABS_Y] = 2; dev->absflat[ABS_X] = 4; dev->absflat[ABS_Y] = 4; dev->id.bustype = BUS_PXA_NSSP; dev->id.vendor = 0xffff; dev->id.product = 0xffff; dev->id.version = 0xffff; return 0; } static void h4000_ts_uninit(struct input_dev *dev) { dev->private = NULL; } static int __init h4000_ts_init(void) { h4000_ts_setup(&dev_ads7846); input_register_device(&dev_ads7846); return 0; } static void __exit h4000_ts_exit(void) { input_unregister_device(&dev_ads7846); h4000_ts_uninit(&dev_ads7846); } module_init(h4000_ts_init) module_exit(h4000_ts_exit) MODULE_AUTHOR("Joshua Wise"); MODULE_DESCRIPTION("Touchscreen support for the iPAQ H4000 (ADS7846 over SPI)"); MODULE_DESCRIPTION(" "); MODULE_LICENSE("Dual BSD/GPL");