| Jaya Kumar | 4ce255c | 2009-01-01 17:51:01 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * am300epd.c -- Platform device for AM300 EPD kit | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2008, Jaya Kumar | 
|  | 5 | * | 
|  | 6 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 7 | * License. See the file COPYING in the main directory of this archive for | 
|  | 8 | * more details. | 
|  | 9 | * | 
|  | 10 | * This work was made possible by help and equipment support from E-Ink | 
|  | 11 | * Corporation. http://support.eink.com/community | 
|  | 12 | * | 
|  | 13 | * This driver is written to be used with the Broadsheet display controller. | 
|  | 14 | * on the AM300 EPD prototype kit/development kit with an E-Ink 800x600 | 
|  | 15 | * Vizplex EPD on a Gumstix board using the Broadsheet interface board. | 
|  | 16 | * | 
|  | 17 | */ | 
|  | 18 |  | 
|  | 19 | #include <linux/module.h> | 
|  | 20 | #include <linux/kernel.h> | 
|  | 21 | #include <linux/errno.h> | 
|  | 22 | #include <linux/string.h> | 
|  | 23 | #include <linux/delay.h> | 
|  | 24 | #include <linux/interrupt.h> | 
|  | 25 | #include <linux/fb.h> | 
|  | 26 | #include <linux/init.h> | 
|  | 27 | #include <linux/platform_device.h> | 
|  | 28 | #include <linux/irq.h> | 
|  | 29 | #include <linux/gpio.h> | 
|  | 30 |  | 
|  | 31 | #include <mach/gumstix.h> | 
|  | 32 | #include <mach/mfp-pxa25x.h> | 
|  | 33 | #include <mach/pxafb.h> | 
|  | 34 |  | 
|  | 35 | #include "generic.h" | 
|  | 36 |  | 
|  | 37 | #include <video/broadsheetfb.h> | 
|  | 38 |  | 
|  | 39 | static unsigned int panel_type = 6; | 
|  | 40 | static struct platform_device *am300_device; | 
|  | 41 | static struct broadsheet_board am300_board; | 
|  | 42 |  | 
|  | 43 | static unsigned long am300_pin_config[] __initdata = { | 
|  | 44 | GPIO16_GPIO, | 
|  | 45 | GPIO17_GPIO, | 
|  | 46 | GPIO32_GPIO, | 
|  | 47 | GPIO48_GPIO, | 
|  | 48 | GPIO49_GPIO, | 
|  | 49 | GPIO51_GPIO, | 
|  | 50 | GPIO74_GPIO, | 
|  | 51 | GPIO75_GPIO, | 
|  | 52 | GPIO76_GPIO, | 
|  | 53 | GPIO77_GPIO, | 
|  | 54 |  | 
|  | 55 | /* this is the 16-bit hdb bus 58-73 */ | 
|  | 56 | GPIO58_GPIO, | 
|  | 57 | GPIO59_GPIO, | 
|  | 58 | GPIO60_GPIO, | 
|  | 59 | GPIO61_GPIO, | 
|  | 60 |  | 
|  | 61 | GPIO62_GPIO, | 
|  | 62 | GPIO63_GPIO, | 
|  | 63 | GPIO64_GPIO, | 
|  | 64 | GPIO65_GPIO, | 
|  | 65 |  | 
|  | 66 | GPIO66_GPIO, | 
|  | 67 | GPIO67_GPIO, | 
|  | 68 | GPIO68_GPIO, | 
|  | 69 | GPIO69_GPIO, | 
|  | 70 |  | 
|  | 71 | GPIO70_GPIO, | 
|  | 72 | GPIO71_GPIO, | 
|  | 73 | GPIO72_GPIO, | 
|  | 74 | GPIO73_GPIO, | 
|  | 75 | }; | 
|  | 76 |  | 
|  | 77 | /* register offsets for gpio control */ | 
|  | 78 | #define PWR_GPIO_PIN	16 | 
|  | 79 | #define CFG_GPIO_PIN	17 | 
|  | 80 | #define RDY_GPIO_PIN	32 | 
|  | 81 | #define DC_GPIO_PIN	48 | 
|  | 82 | #define RST_GPIO_PIN	49 | 
|  | 83 | #define LED_GPIO_PIN	51 | 
|  | 84 | #define RD_GPIO_PIN	74 | 
|  | 85 | #define WR_GPIO_PIN	75 | 
|  | 86 | #define CS_GPIO_PIN	76 | 
|  | 87 | #define IRQ_GPIO_PIN	77 | 
|  | 88 |  | 
|  | 89 | /* hdb bus */ | 
|  | 90 | #define DB0_GPIO_PIN	58 | 
|  | 91 | #define DB15_GPIO_PIN	73 | 
|  | 92 |  | 
|  | 93 | static int gpios[] = { PWR_GPIO_PIN, CFG_GPIO_PIN, RDY_GPIO_PIN, DC_GPIO_PIN, | 
|  | 94 | RST_GPIO_PIN, RD_GPIO_PIN, WR_GPIO_PIN, CS_GPIO_PIN, | 
|  | 95 | IRQ_GPIO_PIN, LED_GPIO_PIN }; | 
|  | 96 | static char *gpio_names[] = { "PWR", "CFG", "RDY", "DC", "RST", "RD", "WR", | 
|  | 97 | "CS", "IRQ", "LED" }; | 
|  | 98 |  | 
|  | 99 | static int am300_wait_event(struct broadsheetfb_par *par) | 
|  | 100 | { | 
|  | 101 | /* todo: improve err recovery */ | 
|  | 102 | wait_event(par->waitq, gpio_get_value(RDY_GPIO_PIN)); | 
|  | 103 | return 0; | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | static int am300_init_gpio_regs(struct broadsheetfb_par *par) | 
|  | 107 | { | 
|  | 108 | int i; | 
|  | 109 | int err; | 
|  | 110 | char dbname[8]; | 
|  | 111 |  | 
|  | 112 | for (i = 0; i < ARRAY_SIZE(gpios); i++) { | 
|  | 113 | err = gpio_request(gpios[i], gpio_names[i]); | 
|  | 114 | if (err) { | 
|  | 115 | dev_err(&am300_device->dev, "failed requesting " | 
|  | 116 | "gpio %s, err=%d\n", gpio_names[i], err); | 
|  | 117 | goto err_req_gpio; | 
|  | 118 | } | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | /* we also need to take care of the hdb bus */ | 
|  | 122 | for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) { | 
|  | 123 | sprintf(dbname, "DB%d", i); | 
|  | 124 | err = gpio_request(i, dbname); | 
|  | 125 | if (err) { | 
|  | 126 | dev_err(&am300_device->dev, "failed requesting " | 
|  | 127 | "gpio %d, err=%d\n", i, err); | 
|  | 128 | while (i >= DB0_GPIO_PIN) | 
|  | 129 | gpio_free(i--); | 
|  | 130 | i = ARRAY_SIZE(gpios) - 1; | 
|  | 131 | goto err_req_gpio; | 
|  | 132 | } | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | /* setup the outputs and init values */ | 
|  | 136 | gpio_direction_output(PWR_GPIO_PIN, 0); | 
|  | 137 | gpio_direction_output(CFG_GPIO_PIN, 1); | 
|  | 138 | gpio_direction_output(DC_GPIO_PIN, 0); | 
|  | 139 | gpio_direction_output(RD_GPIO_PIN, 1); | 
|  | 140 | gpio_direction_output(WR_GPIO_PIN, 1); | 
|  | 141 | gpio_direction_output(CS_GPIO_PIN, 1); | 
|  | 142 | gpio_direction_output(RST_GPIO_PIN, 0); | 
|  | 143 |  | 
|  | 144 | /* setup the inputs */ | 
|  | 145 | gpio_direction_input(RDY_GPIO_PIN); | 
|  | 146 | gpio_direction_input(IRQ_GPIO_PIN); | 
|  | 147 |  | 
|  | 148 | /* start the hdb bus as an input */ | 
|  | 149 | for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) | 
|  | 150 | gpio_direction_output(i, 0); | 
|  | 151 |  | 
|  | 152 | /* go into command mode */ | 
|  | 153 | gpio_set_value(CFG_GPIO_PIN, 1); | 
|  | 154 | gpio_set_value(RST_GPIO_PIN, 0); | 
|  | 155 | msleep(10); | 
|  | 156 | gpio_set_value(RST_GPIO_PIN, 1); | 
|  | 157 | msleep(10); | 
|  | 158 | am300_wait_event(par); | 
|  | 159 |  | 
|  | 160 | return 0; | 
|  | 161 |  | 
|  | 162 | err_req_gpio: | 
|  | 163 | while (i > 0) | 
|  | 164 | gpio_free(gpios[i--]); | 
|  | 165 |  | 
|  | 166 | return err; | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | static int am300_init_board(struct broadsheetfb_par *par) | 
|  | 170 | { | 
|  | 171 | return am300_init_gpio_regs(par); | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | static void am300_cleanup(struct broadsheetfb_par *par) | 
|  | 175 | { | 
|  | 176 | int i; | 
|  | 177 |  | 
|  | 178 | free_irq(IRQ_GPIO(RDY_GPIO_PIN), par); | 
|  | 179 |  | 
|  | 180 | for (i = 0; i < ARRAY_SIZE(gpios); i++) | 
|  | 181 | gpio_free(gpios[i]); | 
|  | 182 |  | 
|  | 183 | for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) | 
|  | 184 | gpio_free(i); | 
|  | 185 |  | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 | static u16 am300_get_hdb(struct broadsheetfb_par *par) | 
|  | 189 | { | 
|  | 190 | u16 res = 0; | 
|  | 191 | int i; | 
|  | 192 |  | 
|  | 193 | for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++) | 
|  | 194 | res |= (gpio_get_value(DB0_GPIO_PIN + i)) ? (1 << i) : 0; | 
|  | 195 |  | 
|  | 196 | return res; | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | static void am300_set_hdb(struct broadsheetfb_par *par, u16 data) | 
|  | 200 | { | 
|  | 201 | int i; | 
|  | 202 |  | 
|  | 203 | for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++) | 
|  | 204 | gpio_set_value(DB0_GPIO_PIN + i, (data >> i) & 0x01); | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 |  | 
|  | 208 | static void am300_set_ctl(struct broadsheetfb_par *par, unsigned char bit, | 
|  | 209 | u8 state) | 
|  | 210 | { | 
|  | 211 | switch (bit) { | 
|  | 212 | case BS_CS: | 
|  | 213 | gpio_set_value(CS_GPIO_PIN, state); | 
|  | 214 | break; | 
|  | 215 | case BS_DC: | 
|  | 216 | gpio_set_value(DC_GPIO_PIN, state); | 
|  | 217 | break; | 
|  | 218 | case BS_WR: | 
|  | 219 | gpio_set_value(WR_GPIO_PIN, state); | 
|  | 220 | break; | 
|  | 221 | } | 
|  | 222 | } | 
|  | 223 |  | 
|  | 224 | static int am300_get_panel_type(void) | 
|  | 225 | { | 
|  | 226 | return panel_type; | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | static irqreturn_t am300_handle_irq(int irq, void *dev_id) | 
|  | 230 | { | 
|  | 231 | struct broadsheetfb_par *par = dev_id; | 
|  | 232 |  | 
|  | 233 | wake_up(&par->waitq); | 
|  | 234 | return IRQ_HANDLED; | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | static int am300_setup_irq(struct fb_info *info) | 
|  | 238 | { | 
|  | 239 | int ret; | 
|  | 240 | struct broadsheetfb_par *par = info->par; | 
|  | 241 |  | 
|  | 242 | ret = request_irq(IRQ_GPIO(RDY_GPIO_PIN), am300_handle_irq, | 
|  | 243 | IRQF_DISABLED|IRQF_TRIGGER_RISING, | 
|  | 244 | "AM300", par); | 
|  | 245 | if (ret) | 
|  | 246 | dev_err(&am300_device->dev, "request_irq failed: %d\n", ret); | 
|  | 247 |  | 
|  | 248 | return ret; | 
|  | 249 | } | 
|  | 250 |  | 
|  | 251 | static struct broadsheet_board am300_board = { | 
|  | 252 | .owner			= THIS_MODULE, | 
|  | 253 | .init			= am300_init_board, | 
|  | 254 | .cleanup		= am300_cleanup, | 
|  | 255 | .set_hdb		= am300_set_hdb, | 
|  | 256 | .get_hdb		= am300_get_hdb, | 
|  | 257 | .set_ctl		= am300_set_ctl, | 
|  | 258 | .wait_for_rdy		= am300_wait_event, | 
|  | 259 | .get_panel_type		= am300_get_panel_type, | 
|  | 260 | .setup_irq		= am300_setup_irq, | 
|  | 261 | }; | 
|  | 262 |  | 
|  | 263 | int __init am300_init(void) | 
|  | 264 | { | 
|  | 265 | int ret; | 
|  | 266 |  | 
|  | 267 | pxa2xx_mfp_config(ARRAY_AND_SIZE(am300_pin_config)); | 
|  | 268 |  | 
|  | 269 | /* request our platform independent driver */ | 
|  | 270 | request_module("broadsheetfb"); | 
|  | 271 |  | 
|  | 272 | am300_device = platform_device_alloc("broadsheetfb", -1); | 
|  | 273 | if (!am300_device) | 
|  | 274 | return -ENOMEM; | 
|  | 275 |  | 
|  | 276 | /* the am300_board that will be seen by broadsheetfb is a copy */ | 
|  | 277 | platform_device_add_data(am300_device, &am300_board, | 
|  | 278 | sizeof(am300_board)); | 
|  | 279 |  | 
|  | 280 | ret = platform_device_add(am300_device); | 
|  | 281 |  | 
|  | 282 | if (ret) { | 
|  | 283 | platform_device_put(am300_device); | 
|  | 284 | return ret; | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | return 0; | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | module_param(panel_type, uint, 0); | 
|  | 291 | MODULE_PARM_DESC(panel_type, "Select the panel type: 6, 8, 97"); | 
|  | 292 |  | 
|  | 293 | MODULE_DESCRIPTION("board driver for am300 epd kit"); | 
|  | 294 | MODULE_AUTHOR("Jaya Kumar"); | 
|  | 295 | MODULE_LICENSE("GPL"); |