Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 94196
b: refs/heads/master
c: 03c33a4
h: refs/heads/master
v: v3
  • Loading branch information
Jaya Kumar authored and Linus Torvalds committed Apr 28, 2008
1 parent 525c4a7 commit 4f986fa
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 283 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 963654a9c919d18f8b9137f8ffd9d2d30a139269
refs/heads/master: 03c33a4f002b2521debf1efc269cade983b6e86a
16 changes: 7 additions & 9 deletions trunk/Documentation/fb/metronomefb.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Metronomefb
-----------
Maintained by Jaya Kumar <jayakumar.lkml.gmail.com>
Last revised: Nov 20, 2007
Last revised: Mar 10, 2008

Metronomefb is a driver for the Metronome display controller. The controller
is from E-Ink Corporation. It is intended to be used to drive the E-Ink
Expand All @@ -11,20 +11,18 @@ display media here http://www.e-ink.com/products/matrix/metronome.html .
Metronome is interfaced to the host CPU through the AMLCD interface. The
host CPU generates the control information and the image in a framebuffer
which is then delivered to the AMLCD interface by a host specific method.
Currently, that's implemented for the PXA's LCDC controller. The display and
error status are each pulled through individual GPIOs.
The display and error status are each pulled through individual GPIOs.

Metronomefb was written for the PXA255/gumstix/lyre combination and
therefore currently has board set specific code in it. If other boards based on
other architectures are available, then the host specific code can be separated
and abstracted out.
Metronomefb is platform independent and depends on a board specific driver
to do all physical IO work. Currently, an example is implemented for the
PXA board used in the AM-200 EPD devkit. This example is am200epd.c

Metronomefb requires waveform information which is delivered via the AMLCD
interface to the metronome controller. The waveform information is expected to
be delivered from userspace via the firmware class interface. The waveform file
can be compressed as long as your udev or hotplug script is aware of the need
to uncompress it before delivering it. metronomefb will ask for waveform.wbf
which would typically go into /lib/firmware/waveform.wbf depending on your
to uncompress it before delivering it. metronomefb will ask for metronome.wbf
which would typically go into /lib/firmware/metronome.wbf depending on your
udev/hotplug setup. I have only tested with a single waveform file which was
originally labeled 23P01201_60_WT0107_MTC. I do not know what it stands for.
Caution should be exercised when manipulating the waveform as there may be
Expand Down
16 changes: 10 additions & 6 deletions trunk/drivers/video/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ config FB_DEFERRED_IO
depends on FB
default y

config FB_METRONOME
tristate
depends on FB
depends on FB_DEFERRED_IO

config FB_SVGALIB
tristate
depends on FB
Expand Down Expand Up @@ -1927,19 +1932,18 @@ config FB_XILINX
framebuffer. ML300 carries a 640*480 LCD display on the board,
ML403 uses a standard DB15 VGA connector.

config FB_METRONOME
tristate "Metronome display controller support"
config FB_AM200EPD
tristate "AM-200 E-Ink EPD devkit support"
depends on FB && ARCH_PXA && MMU
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
select FB_METRONOME
help
This enables support for the Metronome display controller. Tested
with an E-Ink 800x600 display and Gumstix Connex through an AMLCD
interface. Please read <file:Documentation/fb/metronomefb.txt>
for more information.
This enables support for the Metronome display controller used on
the E-Ink AM-200 EPD devkit.

config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/video/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o

# Hardware specific drivers go first
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o
obj-$(CONFIG_FB_AM200EPD) += am200epd.o
obj-$(CONFIG_FB_ARC) += arcfb.o
obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o
obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o
Expand Down
295 changes: 295 additions & 0 deletions trunk/drivers/video/am200epd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
/*
* linux/drivers/video/am200epd.c -- Platform device for AM200 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.
*
* Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
*
* 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 Metronome display controller.
* on the AM200 EPD prototype kit/development kit with an E-Ink 800x600
* Vizplex EPD on a Gumstix board using the Lyre 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/list.h>
#include <linux/uaccess.h>
#include <linux/irq.h>

#include <video/metronomefb.h>

#include <asm/arch/pxa-regs.h>

/* register offsets for gpio control */
#define LED_GPIO_PIN 51
#define STDBY_GPIO_PIN 48
#define RST_GPIO_PIN 49
#define RDY_GPIO_PIN 32
#define ERR_GPIO_PIN 17
#define PCBPWR_GPIO_PIN 16

#define AF_SEL_GPIO_N 0x3
#define GAFR0_U_OFFSET(pin) ((pin - 16) * 2)
#define GAFR1_L_OFFSET(pin) ((pin - 32) * 2)
#define GAFR1_U_OFFSET(pin) ((pin - 48) * 2)
#define GPDR1_OFFSET(pin) (pin - 32)
#define GPCR1_OFFSET(pin) (pin - 32)
#define GPSR1_OFFSET(pin) (pin - 32)
#define GPCR0_OFFSET(pin) (pin)
#define GPSR0_OFFSET(pin) (pin)

static void am200_set_gpio_output(int pin, int val)
{
u8 index;

index = pin >> 4;

switch (index) {
case 1:
if (val)
GPSR0 |= (1 << GPSR0_OFFSET(pin));
else
GPCR0 |= (1 << GPCR0_OFFSET(pin));
break;
case 2:
break;
case 3:
if (val)
GPSR1 |= (1 << GPSR1_OFFSET(pin));
else
GPCR1 |= (1 << GPCR1_OFFSET(pin));
break;
default:
printk(KERN_ERR "unimplemented\n");
}
}

static void __devinit am200_init_gpio_pin(int pin, int dir)
{
u8 index;
/* dir 0 is output, 1 is input
- do 2 things here:
- set gpio alternate function to standard gpio
- set gpio direction to input or output */

index = pin >> 4;
switch (index) {
case 1:
GAFR0_U &= ~(AF_SEL_GPIO_N << GAFR0_U_OFFSET(pin));

if (dir)
GPDR0 &= ~(1 << pin);
else
GPDR0 |= (1 << pin);
break;
case 2:
GAFR1_L &= ~(AF_SEL_GPIO_N << GAFR1_L_OFFSET(pin));

if (dir)
GPDR1 &= ~(1 << GPDR1_OFFSET(pin));
else
GPDR1 |= (1 << GPDR1_OFFSET(pin));
break;
case 3:
GAFR1_U &= ~(AF_SEL_GPIO_N << GAFR1_U_OFFSET(pin));

if (dir)
GPDR1 &= ~(1 << GPDR1_OFFSET(pin));
else
GPDR1 |= (1 << GPDR1_OFFSET(pin));
break;
default:
printk(KERN_ERR "unimplemented\n");
}
}

static void am200_init_gpio_regs(struct metronomefb_par *par)
{
am200_init_gpio_pin(LED_GPIO_PIN, 0);
am200_set_gpio_output(LED_GPIO_PIN, 0);

am200_init_gpio_pin(STDBY_GPIO_PIN, 0);
am200_set_gpio_output(STDBY_GPIO_PIN, 0);

am200_init_gpio_pin(RST_GPIO_PIN, 0);
am200_set_gpio_output(RST_GPIO_PIN, 0);

am200_init_gpio_pin(RDY_GPIO_PIN, 1);

am200_init_gpio_pin(ERR_GPIO_PIN, 1);

am200_init_gpio_pin(PCBPWR_GPIO_PIN, 0);
am200_set_gpio_output(PCBPWR_GPIO_PIN, 0);
}

static void am200_disable_lcd_controller(struct metronomefb_par *par)
{
LCSR = 0xffffffff; /* Clear LCD Status Register */
LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */

/* we reset and just wait for things to settle */
msleep(200);
}

static void am200_enable_lcd_controller(struct metronomefb_par *par)
{
LCSR = 0xffffffff;
FDADR0 = par->metromem_desc_dma;
LCCR0 |= LCCR0_ENB;
}

static void am200_init_lcdc_regs(struct metronomefb_par *par)
{
/* here we do:
- disable the lcd controller
- setup lcd control registers
- setup dma descriptor
- reenable lcd controller
*/

/* disable the lcd controller */
am200_disable_lcd_controller(par);

/* setup lcd control registers */
LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS
| LCCR0_QDM | LCCR0_BM | LCCR0_OUM;

LCCR1 = (par->info->var.xres/2 - 1) /* pixels per line */
| (27 << 10) /* hsync pulse width - 1 */
| (33 << 16) /* eol pixel count */
| (33 << 24); /* bol pixel count */

LCCR2 = (par->info->var.yres - 1) /* lines per panel */
| (24 << 10) /* vsync pulse width - 1 */
| (2 << 16) /* eof pixel count */
| (0 << 24); /* bof pixel count */

LCCR3 = 2 /* pixel clock divisor */
| (24 << 8) /* AC Bias pin freq */
| LCCR3_16BPP /* BPP */
| LCCR3_PCP; /* PCP falling edge */

}

static void am200_post_dma_setup(struct metronomefb_par *par)
{
par->metromem_desc->mFDADR0 = par->metromem_desc_dma;
par->metromem_desc->mFSADR0 = par->metromem_dma;
par->metromem_desc->mFIDR0 = 0;
par->metromem_desc->mLDCMD0 = par->info->var.xres
* par->info->var.yres;
am200_enable_lcd_controller(par);
}

static void am200_free_irq(struct fb_info *info)
{
free_irq(IRQ_GPIO(RDY_GPIO_PIN), info);
}

static irqreturn_t am200_handle_irq(int irq, void *dev_id)
{
struct fb_info *info = dev_id;
struct metronomefb_par *par = info->par;

wake_up_interruptible(&par->waitq);
return IRQ_HANDLED;
}

static int am200_setup_irq(struct fb_info *info)
{
int retval;

retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), am200_handle_irq,
IRQF_DISABLED, "AM200", info);
if (retval) {
printk(KERN_ERR "am200epd: request_irq failed: %d\n", retval);
return retval;
}

return set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQT_FALLING);
}

static void am200_set_rst(struct metronomefb_par *par, int state)
{
am200_set_gpio_output(RST_GPIO_PIN, state);
}

static void am200_set_stdby(struct metronomefb_par *par, int state)
{
am200_set_gpio_output(STDBY_GPIO_PIN, state);
}

static int am200_wait_event(struct metronomefb_par *par)
{
return wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ);
}

static int am200_wait_event_intr(struct metronomefb_par *par)
{
return wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ);
}

static struct metronome_board am200_board = {
.owner = THIS_MODULE,
.free_irq = am200_free_irq,
.setup_irq = am200_setup_irq,
.init_gpio_regs = am200_init_gpio_regs,
.init_lcdc_regs = am200_init_lcdc_regs,
.post_dma_setup = am200_post_dma_setup,
.set_rst = am200_set_rst,
.set_stdby = am200_set_stdby,
.met_wait_event = am200_wait_event,
.met_wait_event_intr = am200_wait_event_intr,
};

static struct platform_device *am200_device;

static int __init am200_init(void)
{
int ret;

/* request our platform independent driver */
request_module("metronomefb");

am200_device = platform_device_alloc("metronomefb", -1);
if (!am200_device)
return -ENOMEM;

platform_device_add_data(am200_device, &am200_board,
sizeof(am200_board));

/* this _add binds metronomefb to am200. metronomefb refcounts am200 */
ret = platform_device_add(am200_device);

if (ret)
platform_device_put(am200_device);

return ret;
}

static void __exit am200_exit(void)
{
platform_device_unregister(am200_device);
}

module_init(am200_init);
module_exit(am200_exit);

MODULE_DESCRIPTION("board driver for am200 metronome epd kit");
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
Loading

0 comments on commit 4f986fa

Please sign in to comment.