|
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.
|