| /* |
| * am300epd.c -- Platform device for AM300 EPD kit |
| * |
| * Copyright (C) 2008, Jaya Kumar |
| * |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file COPYING in the main directory of this archive for |
| * more details. |
| * |
| * This work was made possible by help and equipment support from E-Ink |
| * Corporation. http://support.eink.com/community |
| * |
| * This driver is written to be used with the Broadsheet display controller. |
| * on the AM300 EPD prototype kit/development kit with an E-Ink 800x600 |
| * Vizplex EPD on a Gumstix board using the Broadsheet interface board. |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/errno.h> |
| #include <linux/string.h> |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| #include <linux/fb.h> |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #include <linux/irq.h> |
| #include <linux/gpio.h> |
| |
| #include <mach/gumstix.h> |
| #include <mach/mfp-pxa25x.h> |
| #include <linux/platform_data/video-pxafb.h> |
| |
| #include "generic.h" |
| |
| #include <video/broadsheetfb.h> |
| |
| static unsigned int panel_type = 6; |
| static struct platform_device *am300_device; |
| static struct broadsheet_board am300_board; |
| |
| static unsigned long am300_pin_config[] __initdata = { |
| GPIO16_GPIO, |
| GPIO17_GPIO, |
| GPIO32_GPIO, |
| GPIO48_GPIO, |
| GPIO49_GPIO, |
| GPIO51_GPIO, |
| GPIO74_GPIO, |
| GPIO75_GPIO, |
| GPIO76_GPIO, |
| GPIO77_GPIO, |
| |
| /* this is the 16-bit hdb bus 58-73 */ |
| GPIO58_GPIO, |
| GPIO59_GPIO, |
| GPIO60_GPIO, |
| GPIO61_GPIO, |
| |
| GPIO62_GPIO, |
| GPIO63_GPIO, |
| GPIO64_GPIO, |
| GPIO65_GPIO, |
| |
| GPIO66_GPIO, |
| GPIO67_GPIO, |
| GPIO68_GPIO, |
| GPIO69_GPIO, |
| |
| GPIO70_GPIO, |
| GPIO71_GPIO, |
| GPIO72_GPIO, |
| GPIO73_GPIO, |
| }; |
| |
| /* register offsets for gpio control */ |
| #define PWR_GPIO_PIN 16 |
| #define CFG_GPIO_PIN 17 |
| #define RDY_GPIO_PIN 32 |
| #define DC_GPIO_PIN 48 |
| #define RST_GPIO_PIN 49 |
| #define LED_GPIO_PIN 51 |
| #define RD_GPIO_PIN 74 |
| #define WR_GPIO_PIN 75 |
| #define CS_GPIO_PIN 76 |
| #define IRQ_GPIO_PIN 77 |
| |
| /* hdb bus */ |
| #define DB0_GPIO_PIN 58 |
| #define DB15_GPIO_PIN 73 |
| |
| static int gpios[] = { PWR_GPIO_PIN, CFG_GPIO_PIN, RDY_GPIO_PIN, DC_GPIO_PIN, |
| RST_GPIO_PIN, RD_GPIO_PIN, WR_GPIO_PIN, CS_GPIO_PIN, |
| IRQ_GPIO_PIN, LED_GPIO_PIN }; |
| static char *gpio_names[] = { "PWR", "CFG", "RDY", "DC", "RST", "RD", "WR", |
| "CS", "IRQ", "LED" }; |
| |
| static int am300_wait_event(struct broadsheetfb_par *par) |
| { |
| /* todo: improve err recovery */ |
| wait_event(par->waitq, gpio_get_value(RDY_GPIO_PIN)); |
| return 0; |
| } |
| |
| static int am300_init_gpio_regs(struct broadsheetfb_par *par) |
| { |
| int i; |
| int err; |
| char dbname[8]; |
| |
| for (i = 0; i < ARRAY_SIZE(gpios); i++) { |
| err = gpio_request(gpios[i], gpio_names[i]); |
| if (err) { |
| dev_err(&am300_device->dev, "failed requesting " |
| "gpio %s, err=%d\n", gpio_names[i], err); |
| goto err_req_gpio; |
| } |
| } |
| |
| /* we also need to take care of the hdb bus */ |
| for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) { |
| sprintf(dbname, "DB%d", i); |
| err = gpio_request(i, dbname); |
| if (err) { |
| dev_err(&am300_device->dev, "failed requesting " |
| "gpio %d, err=%d\n", i, err); |
| goto err_req_gpio2; |
| } |
| } |
| |
| /* setup the outputs and init values */ |
| gpio_direction_output(PWR_GPIO_PIN, 0); |
| gpio_direction_output(CFG_GPIO_PIN, 1); |
| gpio_direction_output(DC_GPIO_PIN, 0); |
| gpio_direction_output(RD_GPIO_PIN, 1); |
| gpio_direction_output(WR_GPIO_PIN, 1); |
| gpio_direction_output(CS_GPIO_PIN, 1); |
| gpio_direction_output(RST_GPIO_PIN, 0); |
| |
| /* setup the inputs */ |
| gpio_direction_input(RDY_GPIO_PIN); |
| gpio_direction_input(IRQ_GPIO_PIN); |
| |
| /* start the hdb bus as an input */ |
| for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) |
| gpio_direction_output(i, 0); |
| |
| /* go into command mode */ |
| gpio_set_value(CFG_GPIO_PIN, 1); |
| gpio_set_value(RST_GPIO_PIN, 0); |
| msleep(10); |
| gpio_set_value(RST_GPIO_PIN, 1); |
| msleep(10); |
| am300_wait_event(par); |
| |
| return 0; |
| |
| err_req_gpio2: |
| while (--i >= DB0_GPIO_PIN) |
| gpio_free(i); |
| i = ARRAY_SIZE(gpios); |
| err_req_gpio: |
| while (--i >= 0) |
| gpio_free(gpios[i]); |
| |
| return err; |
| } |
| |
| static int am300_init_board(struct broadsheetfb_par *par) |
| { |
| return am300_init_gpio_regs(par); |
| } |
| |
| static void am300_cleanup(struct broadsheetfb_par *par) |
| { |
| int i; |
| |
| free_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), par); |
| |
| for (i = 0; i < ARRAY_SIZE(gpios); i++) |
| gpio_free(gpios[i]); |
| |
| for (i = DB0_GPIO_PIN; i <= DB15_GPIO_PIN; i++) |
| gpio_free(i); |
| |
| } |
| |
| static u16 am300_get_hdb(struct broadsheetfb_par *par) |
| { |
| u16 res = 0; |
| int i; |
| |
| for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++) |
| res |= (gpio_get_value(DB0_GPIO_PIN + i)) ? (1 << i) : 0; |
| |
| return res; |
| } |
| |
| static void am300_set_hdb(struct broadsheetfb_par *par, u16 data) |
| { |
| int i; |
| |
| for (i = 0; i <= (DB15_GPIO_PIN - DB0_GPIO_PIN) ; i++) |
| gpio_set_value(DB0_GPIO_PIN + i, (data >> i) & 0x01); |
| } |
| |
| |
| static void am300_set_ctl(struct broadsheetfb_par *par, unsigned char bit, |
| u8 state) |
| { |
| switch (bit) { |
| case BS_CS: |
| gpio_set_value(CS_GPIO_PIN, state); |
| break; |
| case BS_DC: |
| gpio_set_value(DC_GPIO_PIN, state); |
| break; |
| case BS_WR: |
| gpio_set_value(WR_GPIO_PIN, state); |
| break; |
| } |
| } |
| |
| static int am300_get_panel_type(void) |
| { |
| return panel_type; |
| } |
| |
| static irqreturn_t am300_handle_irq(int irq, void *dev_id) |
| { |
| struct broadsheetfb_par *par = dev_id; |
| |
| wake_up(&par->waitq); |
| return IRQ_HANDLED; |
| } |
| |
| static int am300_setup_irq(struct fb_info *info) |
| { |
| int ret; |
| struct broadsheetfb_par *par = info->par; |
| |
| ret = request_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), am300_handle_irq, |
| IRQF_TRIGGER_RISING, "AM300", par); |
| if (ret) |
| dev_err(&am300_device->dev, "request_irq failed: %d\n", ret); |
| |
| return ret; |
| } |
| |
| static struct broadsheet_board am300_board = { |
| .owner = THIS_MODULE, |
| .init = am300_init_board, |
| .cleanup = am300_cleanup, |
| .set_hdb = am300_set_hdb, |
| .get_hdb = am300_get_hdb, |
| .set_ctl = am300_set_ctl, |
| .wait_for_rdy = am300_wait_event, |
| .get_panel_type = am300_get_panel_type, |
| .setup_irq = am300_setup_irq, |
| }; |
| |
| int __init am300_init(void) |
| { |
| int ret; |
| |
| pxa2xx_mfp_config(ARRAY_AND_SIZE(am300_pin_config)); |
| |
| /* request our platform independent driver */ |
| request_module("broadsheetfb"); |
| |
| am300_device = platform_device_alloc("broadsheetfb", -1); |
| if (!am300_device) |
| return -ENOMEM; |
| |
| /* the am300_board that will be seen by broadsheetfb is a copy */ |
| platform_device_add_data(am300_device, &am300_board, |
| sizeof(am300_board)); |
| |
| ret = platform_device_add(am300_device); |
| |
| if (ret) { |
| platform_device_put(am300_device); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| module_param(panel_type, uint, 0); |
| MODULE_PARM_DESC(panel_type, "Select the panel type: 37, 6, 97"); |
| |
| MODULE_DESCRIPTION("board driver for am300 epd kit"); |
| MODULE_AUTHOR("Jaya Kumar"); |
| MODULE_LICENSE("GPL"); |