|
From: <z7...@us...> - 2007-01-08 10:24:31
|
Revision: 740
http://svn.sourceforge.net/hackndev/?rev=740&view=rev
Author: z72ka
Date: 2007-01-08 02:24:30 -0800 (Mon, 08 Jan 2007)
Log Message:
-----------
New touchcscreen/battery driver
Added Paths:
-----------
linux4palm/linux/trunk/arch/arm/mach-pxa/palmz72/palmz72_ac97.c
Added: linux4palm/linux/trunk/arch/arm/mach-pxa/palmz72/palmz72_ac97.c
===================================================================
--- linux4palm/linux/trunk/arch/arm/mach-pxa/palmz72/palmz72_ac97.c (rev 0)
+++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmz72/palmz72_ac97.c 2007-01-08 10:24:30 UTC (rev 740)
@@ -0,0 +1,552 @@
+/*
+ * linux/arch/arm/mach-pxa/palmz72/palmz72_ac97.c
+ *
+ * Touchscreen/battery driver for Palm Zire 72 WM9712 AC97 codec
+ * Author: Jan Herman <2h...@se...>
+ * Based on palmld_ac97.c code from Alex Osborne
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/battery.h>
+
+#include <asm/apm.h>
+#include <asm/delay.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/irqs.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+#include <asm/arch/palmz72-ac97.h>
+#include <asm/arch/palmz72-gpio.h>
+#include <asm/arch/palmz72-init.h>
+
+
+#define X_AXIS_MAX 3900
+#define X_AXIS_MIN 350
+#define Y_AXIS_MAX 3750
+#define Y_AXIS_MIN 320
+#define PRESSURE_MIN 0
+#define PRESSURE_MAX 300
+
+#define DIG2_INIT 0x0001 /* initial value for digitiser2 reg */
+
+#define DEFAULT_PRESSURE_TRESHOLD 47200 /* default pressure treshold for pen up/down */
+#define DEFAULT_X_AXIS_FUZZ 5 /* default x axis noise treshold */
+#define DEFAULT_Y_AXIS_FUZZ 40 /* default y axis noise treshold */
+#define PRESSURE_FUZZ 4 /* default pressure noise treshold */
+
+#define palmz72_ac97_WORK_QUEUE_NAME "palmz72_ac97_workqueue"
+
+
+/* module parameters */
+
+static int ptrsh = DEFAULT_PRESSURE_TRESHOLD;
+module_param(ptrsh, int, 0);
+MODULE_PARM_DESC(ptrsh, "pressure treshold for pen up/down");
+
+static int dbglvl = 0; // debug disabled
+module_param(dbglvl, int, 0);
+MODULE_PARM_DESC(dbglvl, "debug level (0 is disabled)");
+
+static int xdjtrsh = DEFAULT_X_AXIS_FUZZ;
+module_param(xdjtrsh, int, 0);
+MODULE_PARM_DESC(xdjtrsh, "treshold for x axis jitter");
+
+static int ydjtrsh = DEFAULT_Y_AXIS_FUZZ;
+module_param(ydjtrsh, int, 0);
+MODULE_PARM_DESC(ydjtrsh, "treshold for y axis jitter");
+
+
+static DECLARE_MUTEX_LOCKED(queue_sem);
+static DECLARE_MUTEX(digitiser_sem);
+static DECLARE_MUTEX(battery_update_mutex);
+
+static struct workqueue_struct *palmz72_ac97_workqueue;
+static struct work_struct palmz72_ac97_irq_task;
+
+struct input_dev *palmz72_ac97_input;
+struct device *palmz72_ac97_dev;
+
+static ac97_t *ac97;
+
+static int battery_registered = 0;
+static unsigned long last_battery_update = 0;
+static int current_voltage;
+static int previous_voltage;
+static u16 d2base;
+
+/*
+ * ac97 codec
+ */
+
+void wm97xx_gpio_func(int gpio, int func)
+{
+ int GEn;
+ GEn = ac97->bus->ops->read(ac97, 0x56);
+ if(func)
+ GEn |= gpio;
+ else
+ GEn &= ~gpio;
+ ac97->bus->ops->write(ac97, 0x56, GEn);
+}
+
+
+void wm97xx_gpio_mode(int gpio, int config, int polarity, int sticky, int wakeup)
+{
+ int GCn, GPn, GSn, GWn;
+ GCn = ac97->bus->ops->read(ac97, 0x4c);
+ GPn = ac97->bus->ops->read(ac97, 0x4e);
+ GSn = ac97->bus->ops->read(ac97, 0x50);
+ GWn = ac97->bus->ops->read(ac97, 0x52);
+
+ if(config)
+ GCn |= gpio;
+ else
+ GCn &= ~gpio;
+
+ if(polarity)
+ GPn |= gpio;
+ else
+ GPn &= ~gpio;
+
+ if(sticky)
+ GSn |= gpio;
+ else
+ GSn &= ~gpio;
+
+ if(wakeup)
+ GWn |= gpio;
+ else
+ GWn &= ~gpio;
+
+ ac97->bus->ops->write(ac97, 0x4c, GCn);
+ ac97->bus->ops->write(ac97, 0x4e, GPn);
+ ac97->bus->ops->write(ac97, 0x50, GSn);
+ ac97->bus->ops->write(ac97, 0x52, GWn);
+}
+
+
+static void wm97xx_set_digitiser_power(int value)
+{
+ ac97->bus->ops->write(ac97, AC97_WM97XX_DIGITISER2, d2base | value);
+}
+
+
+/*
+ * note: for the TX there's some code that gets enabled in linux/sound/pxa2xx-ac97.c
+ * (ifdef CONFIG_MACH_PALMZ72) that tries to implement some recommended procedure for
+ * reading/writing reg 0x54 from a Intel's document
+ * (PXA27x Specification Update: 28007109.pdf sec.: E54)
+ */
+
+static int palmz72_ac97_take_reading(int adcsel)
+{
+
+ int timeout = 15;
+ u16 r76 = 0;
+ u16 r7a;
+
+ r76 |= adcsel; // set ADCSEL (ADC source)
+ r76 |= WM97XX_DELAY(3); // set settling time delay
+ r76 &= ~(1<<11); // COO = 0 (single measurement)
+ r76 &= ~(1<<10); // CTC = 0 (polling mode)
+ r76 |= (1<<15); // initiate measurement (POLL)
+
+ ac97->bus->ops->write(ac97, 0x76, r76);
+
+ // wait settling time
+ udelay ((3 * AC97_LINK_FRAME) + 167);
+
+ // wait for POLL to go low
+ while((ac97->bus->ops->read(ac97, 0x76) & 0x8000) && timeout){
+ udelay(AC97_LINK_FRAME);
+ timeout--;
+ }
+
+ if (timeout == 0){
+ printk("palmz72_ac97: discarding reading due to POLL wait timout on 0x76\n");
+ return 0;
+ }
+
+ r7a = ac97->bus->ops->read(ac97, 0x7a);
+
+ if ((r7a & WM97XX_ADCSEL_MASK) != adcsel){
+ printk("palmz72_ac97: discarding reading -> wrong ADC source\n");
+ return 0;
+ }
+
+ return (int) r7a;
+
+}
+
+
+static void palmz72_ac97_pendown(void)
+{
+ int xread, yread, pressure;
+ int valid_coords=0, btn_pressed = 0;
+
+ /* take readings until the pen goes up */
+ do {
+ /* take readings */
+ xread = palmz72_ac97_take_reading(WM97XX_ADCSEL_X);
+ yread = palmz72_ac97_take_reading(WM97XX_ADCSEL_Y);
+ pressure = palmz72_ac97_take_reading(WM97XX_ADCSEL_PRES);
+
+ // printk("Pressure is %d:\n", pressure);
+
+ valid_coords = (xread & 0xfff) && (yread & 0xfff) && (pressure & 0xfff);
+
+ if(valid_coords && (pressure < ptrsh)) {
+ btn_pressed = 1;
+ input_report_key(palmz72_ac97_input, BTN_TOUCH, 1);
+ input_report_abs(palmz72_ac97_input, ABS_X, xread & 0xfff);
+ input_report_abs(palmz72_ac97_input, ABS_Y, yread & 0xfff);
+ input_report_abs(palmz72_ac97_input, ABS_PRESSURE, pressure & 0xfff);
+ input_sync(palmz72_ac97_input);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ set_current_state(TASK_RUNNING);
+ }
+ } while( valid_coords );
+
+
+ if (btn_pressed) {
+ input_report_key(palmz72_ac97_input, BTN_TOUCH, 0);
+ input_report_abs(palmz72_ac97_input, ABS_X, 0);
+ input_report_abs(palmz72_ac97_input, ABS_Y, 0);
+ input_report_abs(palmz72_ac97_input, ABS_PRESSURE, 0);
+ input_sync(palmz72_ac97_input);
+ }
+
+}
+
+
+static void palmz72_ac97_irq_work(void *data)
+{
+ u16 levels;
+ u16 polarity;
+
+ levels = ac97->bus->ops->read(ac97, 0x54);
+ polarity = ac97->bus->ops->read(ac97, 0x4e);
+
+ if(polarity & levels & WM97XX_GPIO_13) {
+ // power up digitiser:
+ down(&digitiser_sem);
+ wm97xx_set_digitiser_power(WM97XX_PRP_DET_DIG);
+
+ palmz72_ac97_pendown();
+
+ /* power down digitiser to conserve power */
+ wm97xx_set_digitiser_power(WM97XX_PRP_DET);
+ ac97->bus->ops->write(ac97, 0x4e, polarity & ~WM97XX_GPIO_13);
+ up(&digitiser_sem);
+ }
+ else {
+ ac97->bus->ops->write(ac97, 0x4e, polarity | WM97XX_GPIO_13);
+ }
+
+ ac97->bus->ops->write(ac97, 0x54, levels & ~WM97XX_GPIO_13);
+
+ udelay(1);
+ up(&queue_sem);
+ enable_irq(IRQ_GPIO_PALMZ72_WM9712_IRQ);
+}
+
+
+static irqreturn_t palmz72_ac97_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (down_trylock(&queue_sem) == 0){
+ disable_irq(IRQ_GPIO_PALMZ72_WM9712_IRQ);
+ queue_work(palmz72_ac97_workqueue, &palmz72_ac97_irq_task);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* battery */
+
+
+void palmz72_battery_read_adc(int force)
+{
+ u16 vread;
+
+ if(!force && ((last_battery_update + 10 *HZ) > jiffies))
+ return;
+
+ if(down_trylock(&battery_update_mutex))
+ return;
+
+ down(&digitiser_sem);
+ wm97xx_set_digitiser_power(WM97XX_PRP_DET_DIG);
+ vread = palmz72_ac97_take_reading(WM97XX_ADCSEL_BMON);
+ wm97xx_set_digitiser_power(WM97XX_PRP_DET);
+ up(&digitiser_sem);
+
+ previous_voltage = current_voltage;
+ current_voltage = vread & 0xfff;
+ last_battery_update = jiffies;
+
+ up(&battery_update_mutex);
+}
+
+
+int palmz72_battery_min_voltage(struct battery *b)
+{
+ return PALMZ72_BAT_MIN_VOLTAGE;
+}
+
+
+int palmz72_battery_max_voltage(struct battery *b)
+{
+ return PALMZ72_BAT_MAX_VOLTAGE; /* mV */
+}
+
+// V-batt = ADCSEL_BMON * 1,889 + 767,8 [mV]
+
+int palmz72_battery_get_voltage(struct battery *b)
+{
+ if (battery_registered){
+ palmz72_battery_read_adc(0);
+ //printk("Battery [mV]: %d\n", current_voltage + PALMZ72_BMON_TO_VBATT );
+ return current_voltage * 1889/1000 + 7678/10;
+ }
+ else{
+ printk("palmz72_battery: cannot get voltage -> battery driver unregistered\n");
+ return 0;
+ }
+}
+
+
+int palmz72_battery_get_status(struct battery *b)
+{
+ int ac_connected = GET_GPIO(GPIO_NR_PALMZ72_POWER_DETECT);
+ int usb_connected = !GET_GPIO(GPIO_NR_PALMZ72_USB_DETECT);
+
+ if (current_voltage <= 0)
+ return BATTERY_STATUS_UNKNOWN;
+
+ if (ac_connected || usb_connected){
+ // TODO: ok maybe this is too stupid ... to be reviewed
+ if ( ( current_voltage > previous_voltage ) || (current_voltage <= PALMZ72_BAT_MAX_VOLTAGE) )
+ return BATTERY_STATUS_CHARGING;
+ return BATTERY_STATUS_NOT_CHARGING;
+ }
+ else
+ return BATTERY_STATUS_DISCHARGING;
+}
+
+
+struct battery palmz72_battery = {
+ .name = "palmz72_battery",
+ .id = "battery0",
+ .get_min_voltage = palmz72_battery_min_voltage,
+ .get_max_voltage = palmz72_battery_max_voltage,
+ .get_voltage = palmz72_battery_get_voltage,
+ .get_status = palmz72_battery_get_status,
+};
+
+
+
+
+static int __init palmz72_ac97_probe(struct device *dev)
+{
+ int err;
+ u16 d2 = DIG2_INIT; // init d1 too?
+
+ if(!machine_is_palmz72())
+ return -ENODEV;
+
+ ac97 = to_ac97_t(dev);
+
+ set_irq_type(IRQ_GPIO_PALMZ72_WM9712_IRQ, IRQT_RISING);
+
+ err = request_irq(IRQ_GPIO_PALMZ72_WM9712_IRQ, palmz72_ac97_irq_handler,
+ SA_INTERRUPT, "WM9712 pendown IRQ", dev);
+
+ if(err) {
+ printk(KERN_ERR "palmz72_ac97_probe: cannot request pen down IRQ\n");
+ return -1;
+ }
+
+ /* reset levels */
+ ac97->bus->ops->write(ac97, 0x54, 0);
+
+ /* disable digitiser to save power, enable pen-down detect */
+ d2 |= WM97XX_PRP_DET;
+ d2base = d2;
+ ac97->bus->ops->write(ac97, AC97_WM97XX_DIGITISER2, d2base);
+
+ /* enable interrupts on codec's gpio 2 (connected to cpu gpio 27) */
+ wm97xx_gpio_mode(WM97XX_GPIO_2, WM97XX_GPIO_OUT, WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE);
+ wm97xx_gpio_func(WM97XX_GPIO_2, 0);
+
+ /* enable pen detect interrupt */
+ wm97xx_gpio_mode(WM97XX_GPIO_13, WM97XX_GPIO_OUT, WM97XX_GPIO_POL_HIGH,
+ WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE);
+
+ /* turn off irq gpio inverting */
+ ac97->bus->ops->write(ac97, 0x58, ac97->bus->ops->read(ac97, 0x58)&~1);
+
+ /* turn on the digitiser and pen down detector */
+ ac97->bus->ops->write(ac97, AC97_WM97XX_DIGITISER2, d2base | WM97XX_PRP_DETW);
+
+ /* setup the input device */
+ palmz72_ac97_input = input_allocate_device();
+ if (palmz72_ac97_input == NULL){
+ printk ("palmz72_ac97_probe: cannot allocate input device\n");
+ return -ENOMEM;
+ }
+
+ palmz72_ac97_input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+ palmz72_ac97_input->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(palmz72_ac97_input, ABS_X, X_AXIS_MIN, X_AXIS_MAX, xdjtrsh, 0);
+ input_set_abs_params(palmz72_ac97_input, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, ydjtrsh, 0);
+ input_set_abs_params(palmz72_ac97_input, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, PRESSURE_FUZZ, 0);
+
+ palmz72_ac97_input->name = "palmz72 touchscreen";
+ palmz72_ac97_input->dev = dev;
+ palmz72_ac97_input->id.bustype = BUS_HOST;
+ input_register_device(palmz72_ac97_input);
+
+ /* register battery */
+
+ if(battery_class_register(&palmz72_battery)) {
+ printk(KERN_ERR "palmz72_ac97_probe: could not register battery class\n");
+ }
+ else{
+ battery_registered = 1;
+ }
+
+ /* setup work queue */
+ palmz72_ac97_workqueue = create_workqueue(palmz72_ac97_WORK_QUEUE_NAME);
+ INIT_WORK(&palmz72_ac97_irq_task, palmz72_ac97_irq_work, dev);
+
+ up(&queue_sem);
+ return 0;
+}
+
+static int palmz72_ac_is_connected (void){
+ int ret = !(GET_GPIO(GPIO_NR_PALMZ72_POWER_DETECT));
+ if (ret)
+ ret = 1;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+/* APM */
+static void palmz72_apm_get_power_status(struct apm_power_info *info)
+{
+ int min, max, curr, percent;
+
+ curr = palmz72_battery_get_voltage(NULL);
+ min = palmz72_battery_min_voltage(NULL);
+ max = palmz72_battery_max_voltage(NULL);
+
+ curr = curr - min;
+ if (curr < 0) curr = 0;
+ max = max - min;
+
+ percent = curr*100/max;
+
+ info->battery_life = percent;
+
+ info->ac_line_status = palmz72_ac_is_connected() ? APM_AC_ONLINE : APM_AC_OFFLINE;
+
+ if (info->ac_line_status) {
+ info->battery_status = APM_BATTERY_STATUS_CHARGING;
+ } else {
+ if (percent > 50)
+ info->battery_status = APM_BATTERY_STATUS_HIGH;
+ else if (percent < 5)
+ info->battery_status = APM_BATTERY_STATUS_CRITICAL;
+ else
+ info->battery_status = APM_BATTERY_STATUS_LOW;
+ }
+
+ info->time = percent * PALMZ72_MAX_LIFE_MINS/100;
+ info->units = APM_UNITS_MINS;
+}
+
+typedef void (*apm_get_power_status_t)(struct apm_power_info*);
+
+int set_apm_get_power_status(apm_get_power_status_t t)
+{
+ apm_get_power_status = t;
+
+ return 0;
+}
+
+
+/* end of APM implementing */
+
+static int palmz72_ac97_remove (struct device *dev)
+{
+ // TODO: stop running tasks if any?
+
+ battery_class_unregister(&palmz72_battery);
+ ac97 = NULL;
+ input_unregister_device(palmz72_ac97_input);
+ return 0;
+}
+
+
+static struct device_driver palmz72_ac97_driver = {
+ .name = "palmz72_ac97 (WM9712)",
+ .bus = &ac97_bus_type,
+ .owner = THIS_MODULE,
+ .probe = palmz72_ac97_probe,
+ .remove = palmz72_ac97_remove,
+
+#ifdef CONFIG_PM
+ .suspend = NULL,
+ .resume = NULL,
+#endif
+};
+
+
+static int __init palmz72_ac97_init(void)
+{
+ driver_register(&palmz72_ac97_driver);
+
+/* register battery to APM layer */
+#ifdef CONFIG_PM
+ apm_get_power_status = palmz72_apm_get_power_status;
+ return 0;
+#endif
+
+}
+
+
+static void __exit palmz72_ac97_exit(void)
+{
+ driver_unregister(&palmz72_ac97_driver);
+}
+
+
+module_init(palmz72_ac97_init);
+module_exit(palmz72_ac97_exit);
+
+MODULE_AUTHOR ("Jan Herman <2h...@se...>");
+MODULE_DESCRIPTION ("WM9712 AC97 codec support for PalmOne Zire 72");
+MODULE_LICENSE ("GPL");
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|