mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
296 lines
6.4 KiB
296 lines
6.4 KiB
/* |
|
* 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 "gumstix.h" |
|
#include "mfp-pxa25x.h" |
|
#include <mach/irqs.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");
|
|
|