1/* 2 * zylonite-wm97xx.c -- Zylonite Continuous Touch screen driver 3 * 4 * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC. 5 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 6 * Parts Copyright : Ian Molton <spyro@f2s.com> 7 * Andrew Zabolotny <zap@homelink.ru> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 * 14 * Notes: 15 * This is a wm97xx extended touch driver supporting interrupt driven 16 * and continuous operation on Marvell Zylonite development systems 17 * (which have a WM9713 on board). 18 */ 19 20#include <linux/module.h> 21#include <linux/moduleparam.h> 22#include <linux/kernel.h> 23#include <linux/delay.h> 24#include <linux/gpio.h> 25#include <linux/irq.h> 26#include <linux/interrupt.h> 27#include <linux/io.h> 28#include <linux/wm97xx.h> 29 30#include <mach/hardware.h> 31#include <mach/mfp.h> 32#include <mach/regs-ac97.h> 33 34struct continuous { 35 u16 id; /* codec id */ 36 u8 code; /* continuous code */ 37 u8 reads; /* number of coord reads per read cycle */ 38 u32 speed; /* number of coords per second */ 39}; 40 41#define WM_READS(sp) ((sp / HZ) + 1) 42 43static const struct continuous cinfo[] = { 44 { WM9713_ID2, 0, WM_READS(94), 94 }, 45 { WM9713_ID2, 1, WM_READS(120), 120 }, 46 { WM9713_ID2, 2, WM_READS(154), 154 }, 47 { WM9713_ID2, 3, WM_READS(188), 188 }, 48}; 49 50/* continuous speed index */ 51static int sp_idx; 52 53/* 54 * Pen sampling frequency (Hz) in continuous mode. 55 */ 56static int cont_rate = 200; 57module_param(cont_rate, int, 0); 58MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); 59 60/* 61 * Pressure readback. 62 * 63 * Set to 1 to read back pen down pressure 64 */ 65static int pressure; 66module_param(pressure, int, 0); 67MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); 68 69/* 70 * AC97 touch data slot. 71 * 72 * Touch screen readback data ac97 slot 73 */ 74static int ac97_touch_slot = 5; 75module_param(ac97_touch_slot, int, 0); 76MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); 77 78 79/* flush AC97 slot 5 FIFO machines */ 80static void wm97xx_acc_pen_up(struct wm97xx *wm) 81{ 82 int i; 83 84 msleep(1); 85 86 for (i = 0; i < 16; i++) 87 MODR; 88} 89 90static int wm97xx_acc_pen_down(struct wm97xx *wm) 91{ 92 u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; 93 int reads = 0; 94 static u16 last, tries; 95 96 /* When the AC97 queue has been drained we need to allow time 97 * to buffer up samples otherwise we end up spinning polling 98 * for samples. The controller can't have a suitably low 99 * threshold set to use the notifications it gives. 100 */ 101 msleep(1); 102 103 if (tries > 5) { 104 tries = 0; 105 return RC_PENUP; 106 } 107 108 x = MODR; 109 if (x == last) { 110 tries++; 111 return RC_AGAIN; 112 } 113 last = x; 114 do { 115 if (reads) 116 x = MODR; 117 y = MODR; 118 if (pressure) 119 p = MODR; 120 121 dev_dbg(wm->dev, "Raw coordinates: x=%x, y=%x, p=%x\n", 122 x, y, p); 123 124 /* are samples valid */ 125 if ((x & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_X || 126 (y & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_Y || 127 (p & WM97XX_ADCSEL_MASK) != WM97XX_ADCSEL_PRES) 128 goto up; 129 130 /* coordinate is good */ 131 tries = 0; 132 input_report_abs(wm->input_dev, ABS_X, x & 0xfff); 133 input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); 134 input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); 135 input_report_key(wm->input_dev, BTN_TOUCH, (p != 0)); 136 input_sync(wm->input_dev); 137 reads++; 138 } while (reads < cinfo[sp_idx].reads); 139up: 140 return RC_PENDOWN | RC_AGAIN; 141} 142 143static int wm97xx_acc_startup(struct wm97xx *wm) 144{ 145 int idx; 146 147 /* check we have a codec */ 148 if (wm->ac97 == NULL) 149 return -ENODEV; 150 151 /* Go you big red fire engine */ 152 for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { 153 if (wm->id != cinfo[idx].id) 154 continue; 155 sp_idx = idx; 156 if (cont_rate <= cinfo[idx].speed) 157 break; 158 } 159 wm->acc_rate = cinfo[sp_idx].code; 160 wm->acc_slot = ac97_touch_slot; 161 dev_info(wm->dev, 162 "zylonite accelerated touchscreen driver, %d samples/sec\n", 163 cinfo[sp_idx].speed); 164 165 return 0; 166} 167 168static void wm97xx_irq_enable(struct wm97xx *wm, int enable) 169{ 170 if (enable) 171 enable_irq(wm->pen_irq); 172 else 173 disable_irq_nosync(wm->pen_irq); 174} 175 176static struct wm97xx_mach_ops zylonite_mach_ops = { 177 .acc_enabled = 1, 178 .acc_pen_up = wm97xx_acc_pen_up, 179 .acc_pen_down = wm97xx_acc_pen_down, 180 .acc_startup = wm97xx_acc_startup, 181 .irq_enable = wm97xx_irq_enable, 182 .irq_gpio = WM97XX_GPIO_2, 183}; 184 185static int zylonite_wm97xx_probe(struct platform_device *pdev) 186{ 187 struct wm97xx *wm = platform_get_drvdata(pdev); 188 int gpio_touch_irq; 189 190 if (cpu_is_pxa320()) 191 gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15); 192 else 193 gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26); 194 195 wm->pen_irq = gpio_to_irq(gpio_touch_irq); 196 irq_set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH); 197 198 wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, 199 WM97XX_GPIO_POL_HIGH, 200 WM97XX_GPIO_STICKY, 201 WM97XX_GPIO_WAKE); 202 wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, 203 WM97XX_GPIO_POL_HIGH, 204 WM97XX_GPIO_NOTSTICKY, 205 WM97XX_GPIO_NOWAKE); 206 207 return wm97xx_register_mach_ops(wm, &zylonite_mach_ops); 208} 209 210static int zylonite_wm97xx_remove(struct platform_device *pdev) 211{ 212 struct wm97xx *wm = platform_get_drvdata(pdev); 213 214 wm97xx_unregister_mach_ops(wm); 215 216 return 0; 217} 218 219static struct platform_driver zylonite_wm97xx_driver = { 220 .probe = zylonite_wm97xx_probe, 221 .remove = zylonite_wm97xx_remove, 222 .driver = { 223 .name = "wm97xx-touch", 224 }, 225}; 226module_platform_driver(zylonite_wm97xx_driver); 227 228/* Module information */ 229MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 230MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite"); 231MODULE_LICENSE("GPL"); 232