fbdev: platforming metronomefb and am200epd
This patch splits metronomefb into the platform independent metronomefb and the platform dependent am200epd. Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com> Cc: "Antonino A. Daplas" <adaplas@pol.net> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
963654a9c9
commit
03c33a4f00
@ -1,7 +1,7 @@
|
|||||||
Metronomefb
|
Metronomefb
|
||||||
-----------
|
-----------
|
||||||
Maintained by Jaya Kumar <jayakumar.lkml.gmail.com>
|
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
|
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
|
is from E-Ink Corporation. It is intended to be used to drive the E-Ink
|
||||||
@ -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
|
Metronome is interfaced to the host CPU through the AMLCD interface. The
|
||||||
host CPU generates the control information and the image in a framebuffer
|
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.
|
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
|
The display and error status are each pulled through individual GPIOs.
|
||||||
error status are each pulled through individual GPIOs.
|
|
||||||
|
|
||||||
Metronomefb was written for the PXA255/gumstix/lyre combination and
|
Metronomefb is platform independent and depends on a board specific driver
|
||||||
therefore currently has board set specific code in it. If other boards based on
|
to do all physical IO work. Currently, an example is implemented for the
|
||||||
other architectures are available, then the host specific code can be separated
|
PXA board used in the AM-200 EPD devkit. This example is am200epd.c
|
||||||
and abstracted out.
|
|
||||||
|
|
||||||
Metronomefb requires waveform information which is delivered via the AMLCD
|
Metronomefb requires waveform information which is delivered via the AMLCD
|
||||||
interface to the metronome controller. The waveform information is expected to
|
interface to the metronome controller. The waveform information is expected to
|
||||||
be delivered from userspace via the firmware class interface. The waveform file
|
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
|
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
|
to uncompress it before delivering it. metronomefb will ask for metronome.wbf
|
||||||
which would typically go into /lib/firmware/waveform.wbf depending on your
|
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
|
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.
|
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
|
Caution should be exercised when manipulating the waveform as there may be
|
||||||
|
@ -173,6 +173,11 @@ config FB_DEFERRED_IO
|
|||||||
depends on FB
|
depends on FB
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config FB_METRONOME
|
||||||
|
tristate
|
||||||
|
depends on FB
|
||||||
|
depends on FB_DEFERRED_IO
|
||||||
|
|
||||||
config FB_SVGALIB
|
config FB_SVGALIB
|
||||||
tristate
|
tristate
|
||||||
depends on FB
|
depends on FB
|
||||||
@ -1927,19 +1932,18 @@ config FB_XILINX
|
|||||||
framebuffer. ML300 carries a 640*480 LCD display on the board,
|
framebuffer. ML300 carries a 640*480 LCD display on the board,
|
||||||
ML403 uses a standard DB15 VGA connector.
|
ML403 uses a standard DB15 VGA connector.
|
||||||
|
|
||||||
config FB_METRONOME
|
config FB_AM200EPD
|
||||||
tristate "Metronome display controller support"
|
tristate "AM-200 E-Ink EPD devkit support"
|
||||||
depends on FB && ARCH_PXA && MMU
|
depends on FB && ARCH_PXA && MMU
|
||||||
select FB_SYS_FILLRECT
|
select FB_SYS_FILLRECT
|
||||||
select FB_SYS_COPYAREA
|
select FB_SYS_COPYAREA
|
||||||
select FB_SYS_IMAGEBLIT
|
select FB_SYS_IMAGEBLIT
|
||||||
select FB_SYS_FOPS
|
select FB_SYS_FOPS
|
||||||
select FB_DEFERRED_IO
|
select FB_DEFERRED_IO
|
||||||
|
select FB_METRONOME
|
||||||
help
|
help
|
||||||
This enables support for the Metronome display controller. Tested
|
This enables support for the Metronome display controller used on
|
||||||
with an E-Ink 800x600 display and Gumstix Connex through an AMLCD
|
the E-Ink AM-200 EPD devkit.
|
||||||
interface. Please read <file:Documentation/fb/metronomefb.txt>
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
config FB_VIRTUAL
|
config FB_VIRTUAL
|
||||||
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
|
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
|
||||||
|
@ -29,6 +29,7 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
|
|||||||
|
|
||||||
# Hardware specific drivers go first
|
# Hardware specific drivers go first
|
||||||
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o
|
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o
|
||||||
|
obj-$(CONFIG_FB_AM200EPD) += am200epd.o
|
||||||
obj-$(CONFIG_FB_ARC) += arcfb.o
|
obj-$(CONFIG_FB_ARC) += arcfb.o
|
||||||
obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o
|
obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o
|
||||||
obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o
|
obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o
|
||||||
|
295
drivers/video/am200epd.c
Normal file
295
drivers/video/am200epd.c
Normal file
@ -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");
|
@ -13,12 +13,10 @@
|
|||||||
* Corporation. http://support.eink.com/community
|
* Corporation. http://support.eink.com/community
|
||||||
*
|
*
|
||||||
* This driver is written to be used with the Metronome display controller.
|
* This driver is written to be used with the Metronome display controller.
|
||||||
* It was tested with an E-Ink 800x600 Vizplex EPD on a Gumstix Connex board
|
* It is intended to be architecture independent. A board specific driver
|
||||||
* using the Lyre interface board.
|
* must be used to perform all the physical IO interactions. An example
|
||||||
|
* is provided as am200epd.c
|
||||||
*
|
*
|
||||||
* General notes:
|
|
||||||
* - User must set metronomefb_enable=1 to enable it.
|
|
||||||
* - See Documentation/fb/metronomefb.txt for how metronome works.
|
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@ -38,9 +36,11 @@
|
|||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
|
||||||
#include <asm/arch/pxa-regs.h>
|
#include <video/metronomefb.h>
|
||||||
|
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
|
||||||
#define DEBUG 1
|
#define DEBUG 1
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a)
|
#define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a)
|
||||||
@ -53,35 +53,6 @@
|
|||||||
#define DPY_W 832
|
#define DPY_W 832
|
||||||
#define DPY_H 622
|
#define DPY_H 622
|
||||||
|
|
||||||
struct metromem_desc {
|
|
||||||
u32 mFDADR0;
|
|
||||||
u32 mFSADR0;
|
|
||||||
u32 mFIDR0;
|
|
||||||
u32 mLDCMD0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct metromem_cmd {
|
|
||||||
u16 opcode;
|
|
||||||
u16 args[((64-2)/2)];
|
|
||||||
u16 csum;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct metronomefb_par {
|
|
||||||
unsigned char *metromem;
|
|
||||||
struct metromem_desc *metromem_desc;
|
|
||||||
struct metromem_cmd *metromem_cmd;
|
|
||||||
unsigned char *metromem_wfm;
|
|
||||||
unsigned char *metromem_img;
|
|
||||||
u16 *metromem_img_csum;
|
|
||||||
u16 *csum_table;
|
|
||||||
int metromemsize;
|
|
||||||
dma_addr_t metromem_dma;
|
|
||||||
dma_addr_t metromem_desc_dma;
|
|
||||||
struct fb_info *info;
|
|
||||||
wait_queue_head_t waitq;
|
|
||||||
u8 frame_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* frame differs from image. frame includes non-visible pixels */
|
/* frame differs from image. frame includes non-visible pixels */
|
||||||
struct epd_frame {
|
struct epd_frame {
|
||||||
int fw; /* frame width */
|
int fw; /* frame width */
|
||||||
@ -120,8 +91,7 @@ static struct fb_var_screeninfo metronomefb_var __devinitdata = {
|
|||||||
.transp = { 0, 0, 0 },
|
.transp = { 0, 0, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int metronomefb_enable;
|
/* the waveform structure that is coming from userspace firmware */
|
||||||
|
|
||||||
struct waveform_hdr {
|
struct waveform_hdr {
|
||||||
u8 stuff[32];
|
u8 stuff[32];
|
||||||
|
|
||||||
@ -301,165 +271,6 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 metronome_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 metronome_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 __devinit metronome_init_gpio_regs(void)
|
|
||||||
{
|
|
||||||
metronome_init_gpio_pin(LED_GPIO_PIN, 0);
|
|
||||||
metronome_set_gpio_output(LED_GPIO_PIN, 0);
|
|
||||||
|
|
||||||
metronome_init_gpio_pin(STDBY_GPIO_PIN, 0);
|
|
||||||
metronome_set_gpio_output(STDBY_GPIO_PIN, 0);
|
|
||||||
|
|
||||||
metronome_init_gpio_pin(RST_GPIO_PIN, 0);
|
|
||||||
metronome_set_gpio_output(RST_GPIO_PIN, 0);
|
|
||||||
|
|
||||||
metronome_init_gpio_pin(RDY_GPIO_PIN, 1);
|
|
||||||
|
|
||||||
metronome_init_gpio_pin(ERR_GPIO_PIN, 1);
|
|
||||||
|
|
||||||
metronome_init_gpio_pin(PCBPWR_GPIO_PIN, 0);
|
|
||||||
metronome_set_gpio_output(PCBPWR_GPIO_PIN, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void metronome_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 metronome_enable_lcd_controller(struct metronomefb_par *par)
|
|
||||||
{
|
|
||||||
LCSR = 0xffffffff;
|
|
||||||
FDADR0 = par->metromem_desc_dma;
|
|
||||||
LCCR0 |= LCCR0_ENB;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __devinit metronome_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 */
|
|
||||||
metronome_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 = (epd_frame_table[0].fw/2 - 1) /* pixels per line */
|
|
||||||
| (27 << 10) /* hsync pulse width - 1 */
|
|
||||||
| (33 << 16) /* eol pixel count */
|
|
||||||
| (33 << 24); /* bol pixel count */
|
|
||||||
|
|
||||||
LCCR2 = (epd_frame_table[0].fh - 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 */
|
|
||||||
|
|
||||||
/* setup dma descriptor */
|
|
||||||
par->metromem_desc->mFDADR0 = par->metromem_desc_dma;
|
|
||||||
par->metromem_desc->mFSADR0 = par->metromem_dma;
|
|
||||||
par->metromem_desc->mFIDR0 = 0;
|
|
||||||
par->metromem_desc->mLDCMD0 = epd_frame_table[0].fw
|
|
||||||
* epd_frame_table[0].fh;
|
|
||||||
/* reenable lcd controller */
|
|
||||||
metronome_enable_lcd_controller(par);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int metronome_display_cmd(struct metronomefb_par *par)
|
static int metronome_display_cmd(struct metronomefb_par *par)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -493,8 +304,7 @@ static int metronome_display_cmd(struct metronomefb_par *par)
|
|||||||
par->metromem_cmd->csum = cs;
|
par->metromem_cmd->csum = cs;
|
||||||
par->metromem_cmd->opcode = opcode; /* display cmd */
|
par->metromem_cmd->opcode = opcode; /* display cmd */
|
||||||
|
|
||||||
i = wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ);
|
return par->board->met_wait_event_intr(par);
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
|
static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
|
||||||
@ -518,13 +328,12 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
|
|||||||
par->metromem_cmd->csum = cs;
|
par->metromem_cmd->csum = cs;
|
||||||
|
|
||||||
msleep(1);
|
msleep(1);
|
||||||
metronome_set_gpio_output(RST_GPIO_PIN, 1);
|
par->board->set_rst(par, 1);
|
||||||
|
|
||||||
msleep(1);
|
msleep(1);
|
||||||
metronome_set_gpio_output(STDBY_GPIO_PIN, 1);
|
par->board->set_stdby(par, 1);
|
||||||
|
|
||||||
i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ);
|
return par->board->met_wait_event(par);
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit metronome_config_cmd(struct metronomefb_par *par)
|
static int __devinit metronome_config_cmd(struct metronomefb_par *par)
|
||||||
@ -569,8 +378,7 @@ static int __devinit metronome_config_cmd(struct metronomefb_par *par)
|
|||||||
par->metromem_cmd->csum = cs;
|
par->metromem_cmd->csum = cs;
|
||||||
par->metromem_cmd->opcode = 0xCC10; /* config cmd */
|
par->metromem_cmd->opcode = 0xCC10; /* config cmd */
|
||||||
|
|
||||||
i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ);
|
return par->board->met_wait_event(par);
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit metronome_init_cmd(struct metronomefb_par *par)
|
static int __devinit metronome_init_cmd(struct metronomefb_par *par)
|
||||||
@ -596,16 +404,19 @@ static int __devinit metronome_init_cmd(struct metronomefb_par *par)
|
|||||||
par->metromem_cmd->csum = cs;
|
par->metromem_cmd->csum = cs;
|
||||||
par->metromem_cmd->opcode = 0xCC20; /* init cmd */
|
par->metromem_cmd->opcode = 0xCC20; /* init cmd */
|
||||||
|
|
||||||
i = wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ);
|
return par->board->met_wait_event(par);
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit metronome_init_regs(struct metronomefb_par *par)
|
static int __devinit metronome_init_regs(struct metronomefb_par *par)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
metronome_init_gpio_regs();
|
par->board->init_gpio_regs(par);
|
||||||
metronome_init_lcdc_regs(par);
|
|
||||||
|
par->board->init_lcdc_regs(par);
|
||||||
|
|
||||||
|
/* now that lcd is setup, setup dma descriptor */
|
||||||
|
par->board->post_dma_setup(par);
|
||||||
|
|
||||||
res = metronome_powerup_cmd(par);
|
res = metronome_powerup_cmd(par);
|
||||||
if (res)
|
if (res)
|
||||||
@ -616,8 +427,6 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par)
|
|||||||
return res;
|
return res;
|
||||||
|
|
||||||
res = metronome_init_cmd(par);
|
res = metronome_init_cmd(par);
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -632,7 +441,7 @@ static void metronomefb_dpy_update(struct metronomefb_par *par)
|
|||||||
|
|
||||||
cksum = calc_img_cksum((u16 *) par->metromem_img,
|
cksum = calc_img_cksum((u16 *) par->metromem_img,
|
||||||
(epd_frame_table[0].fw * DPY_H)/2);
|
(epd_frame_table[0].fw * DPY_H)/2);
|
||||||
*((u16 *) (par->metromem_img) +
|
*((u16 *)(par->metromem_img) +
|
||||||
(epd_frame_table[0].fw * DPY_H)/2) = cksum;
|
(epd_frame_table[0].fw * DPY_H)/2) = cksum;
|
||||||
metronome_display_cmd(par);
|
metronome_display_cmd(par);
|
||||||
}
|
}
|
||||||
@ -641,8 +450,8 @@ static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
u16 csum = 0;
|
u16 csum = 0;
|
||||||
u16 *buf = (u16 __force *) (par->info->screen_base + index);
|
u16 *buf = (u16 __force *)(par->info->screen_base + index);
|
||||||
u16 *img = (u16 *) (par->metromem_img + index);
|
u16 *img = (u16 *)(par->metromem_img + index);
|
||||||
|
|
||||||
/* swizzle from vm to metromem and recalc cksum at the same time*/
|
/* swizzle from vm to metromem and recalc cksum at the same time*/
|
||||||
for (i = 0; i < PAGE_SIZE/2; i++) {
|
for (i = 0; i < PAGE_SIZE/2; i++) {
|
||||||
@ -733,7 +542,7 @@ static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
|
|||||||
count = total_size - p;
|
count = total_size - p;
|
||||||
}
|
}
|
||||||
|
|
||||||
dst = (void __force *) (info->screen_base + p);
|
dst = (void __force *)(info->screen_base + p);
|
||||||
|
|
||||||
if (copy_from_user(dst, buf, count))
|
if (copy_from_user(dst, buf, count))
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
@ -759,18 +568,10 @@ static struct fb_deferred_io metronomefb_defio = {
|
|||||||
.deferred_io = metronomefb_dpy_deferred_io,
|
.deferred_io = metronomefb_dpy_deferred_io,
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t metronome_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 __devinit metronomefb_probe(struct platform_device *dev)
|
static int __devinit metronomefb_probe(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct fb_info *info;
|
struct fb_info *info;
|
||||||
|
struct metronome_board *board;
|
||||||
int retval = -ENOMEM;
|
int retval = -ENOMEM;
|
||||||
int videomemorysize;
|
int videomemorysize;
|
||||||
unsigned char *videomemory;
|
unsigned char *videomemory;
|
||||||
@ -779,17 +580,26 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
|
|||||||
int cmd_size, wfm_size, img_size, padding_size, totalsize;
|
int cmd_size, wfm_size, img_size, padding_size, totalsize;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* pick up board specific routines */
|
||||||
|
board = dev->dev.platform_data;
|
||||||
|
if (!board)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* try to count device specific driver, if can't, platform recalls */
|
||||||
|
if (!try_module_get(board->owner))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
/* we have two blocks of memory.
|
/* we have two blocks of memory.
|
||||||
info->screen_base which is vm, and is the fb used by apps.
|
info->screen_base which is vm, and is the fb used by apps.
|
||||||
par->metromem which is physically contiguous memory and
|
par->metromem which is physically contiguous memory and
|
||||||
contains the display controller commands, waveform,
|
contains the display controller commands, waveform,
|
||||||
processed image data and padding. this is the data pulled
|
processed image data and padding. this is the data pulled
|
||||||
by the pxa255's LCD controller and pushed to Metronome */
|
by the device's LCD controller and pushed to Metronome */
|
||||||
|
|
||||||
videomemorysize = (DPY_W*DPY_H);
|
videomemorysize = (DPY_W*DPY_H);
|
||||||
videomemory = vmalloc(videomemorysize);
|
videomemory = vmalloc(videomemorysize);
|
||||||
if (!videomemory)
|
if (!videomemory)
|
||||||
return retval;
|
return -ENOMEM;
|
||||||
|
|
||||||
memset(videomemory, 0, videomemorysize);
|
memset(videomemory, 0, videomemorysize);
|
||||||
|
|
||||||
@ -797,7 +607,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
|
|||||||
if (!info)
|
if (!info)
|
||||||
goto err_vfree;
|
goto err_vfree;
|
||||||
|
|
||||||
info->screen_base = (char __iomem *) videomemory;
|
info->screen_base = (char __force __iomem *)videomemory;
|
||||||
info->fbops = &metronomefb_ops;
|
info->fbops = &metronomefb_ops;
|
||||||
|
|
||||||
info->var = metronomefb_var;
|
info->var = metronomefb_var;
|
||||||
@ -805,6 +615,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
|
|||||||
info->fix.smem_len = videomemorysize;
|
info->fix.smem_len = videomemorysize;
|
||||||
par = info->par;
|
par = info->par;
|
||||||
par->info = info;
|
par->info = info;
|
||||||
|
par->board = board;
|
||||||
init_waitqueue_head(&par->waitq);
|
init_waitqueue_head(&par->waitq);
|
||||||
|
|
||||||
/* this table caches per page csum values. */
|
/* this table caches per page csum values. */
|
||||||
@ -849,11 +660,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
|
|||||||
par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size
|
par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size
|
||||||
+ img_size + padding_size;
|
+ img_size + padding_size;
|
||||||
|
|
||||||
/* load the waveform in. assume mode 3, temp 31 for now */
|
/* load the waveform in. assume mode 3, temp 31 for now
|
||||||
/* a) request the waveform file from userspace
|
a) request the waveform file from userspace
|
||||||
b) process waveform and decode into metromem */
|
b) process waveform and decode into metromem */
|
||||||
|
retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
|
||||||
retval = request_firmware(&fw_entry, "waveform.wbf", &dev->dev);
|
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
printk(KERN_ERR "metronomefb: couldn't get waveform\n");
|
printk(KERN_ERR "metronomefb: couldn't get waveform\n");
|
||||||
goto err_dma_free;
|
goto err_dma_free;
|
||||||
@ -867,13 +677,8 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
|
|||||||
}
|
}
|
||||||
release_firmware(fw_entry);
|
release_firmware(fw_entry);
|
||||||
|
|
||||||
retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), metronome_handle_irq,
|
if (board->setup_irq(info))
|
||||||
IRQF_DISABLED, "Metronome", info);
|
|
||||||
if (retval) {
|
|
||||||
dev_err(&dev->dev, "request_irq failed: %d\n", retval);
|
|
||||||
goto err_ld_wfm;
|
goto err_ld_wfm;
|
||||||
}
|
|
||||||
set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQT_FALLING);
|
|
||||||
|
|
||||||
retval = metronome_init_regs(par);
|
retval = metronome_init_regs(par);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
@ -913,7 +718,7 @@ err_cmap:
|
|||||||
err_fb_rel:
|
err_fb_rel:
|
||||||
framebuffer_release(info);
|
framebuffer_release(info);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
free_irq(IRQ_GPIO(RDY_GPIO_PIN), info);
|
board->free_irq(info);
|
||||||
err_ld_wfm:
|
err_ld_wfm:
|
||||||
release_firmware(fw_entry);
|
release_firmware(fw_entry);
|
||||||
err_dma_free:
|
err_dma_free:
|
||||||
@ -923,6 +728,7 @@ err_csum_table:
|
|||||||
vfree(par->csum_table);
|
vfree(par->csum_table);
|
||||||
err_vfree:
|
err_vfree:
|
||||||
vfree(videomemory);
|
vfree(videomemory);
|
||||||
|
module_put(board->owner);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -939,7 +745,8 @@ static int __devexit metronomefb_remove(struct platform_device *dev)
|
|||||||
vfree(par->csum_table);
|
vfree(par->csum_table);
|
||||||
unregister_framebuffer(info);
|
unregister_framebuffer(info);
|
||||||
vfree((void __force *)info->screen_base);
|
vfree((void __force *)info->screen_base);
|
||||||
free_irq(IRQ_GPIO(RDY_GPIO_PIN), info);
|
par->board->free_irq(info);
|
||||||
|
module_put(par->board->owner);
|
||||||
framebuffer_release(info);
|
framebuffer_release(info);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -949,48 +756,21 @@ static struct platform_driver metronomefb_driver = {
|
|||||||
.probe = metronomefb_probe,
|
.probe = metronomefb_probe,
|
||||||
.remove = metronomefb_remove,
|
.remove = metronomefb_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.name = "metronomefb",
|
.name = "metronomefb",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *metronomefb_device;
|
|
||||||
|
|
||||||
static int __init metronomefb_init(void)
|
static int __init metronomefb_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
return platform_driver_register(&metronomefb_driver);
|
||||||
|
|
||||||
if (!metronomefb_enable) {
|
|
||||||
printk(KERN_ERR
|
|
||||||
"Use metronomefb_enable to enable the device\n");
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = platform_driver_register(&metronomefb_driver);
|
|
||||||
if (!ret) {
|
|
||||||
metronomefb_device = platform_device_alloc("metronomefb", 0);
|
|
||||||
if (metronomefb_device)
|
|
||||||
ret = platform_device_add(metronomefb_device);
|
|
||||||
else
|
|
||||||
ret = -ENOMEM;
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
platform_device_put(metronomefb_device);
|
|
||||||
platform_driver_unregister(&metronomefb_driver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit metronomefb_exit(void)
|
static void __exit metronomefb_exit(void)
|
||||||
{
|
{
|
||||||
platform_device_unregister(metronomefb_device);
|
|
||||||
platform_driver_unregister(&metronomefb_driver);
|
platform_driver_unregister(&metronomefb_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_param(metronomefb_enable, uint, 0);
|
|
||||||
MODULE_PARM_DESC(metronomefb_enable, "Enable communication with Metronome");
|
|
||||||
|
|
||||||
module_init(metronomefb_init);
|
module_init(metronomefb_init);
|
||||||
module_exit(metronomefb_exit);
|
module_exit(metronomefb_exit);
|
||||||
|
|
||||||
|
62
include/video/metronomefb.h
Normal file
62
include/video/metronomefb.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* metronomefb.h - definitions for the metronome framebuffer driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 by 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LINUX_METRONOMEFB_H_
|
||||||
|
#define _LINUX_METRONOMEFB_H_
|
||||||
|
|
||||||
|
/* address and control descriptors used by metronome controller */
|
||||||
|
struct metromem_desc {
|
||||||
|
u32 mFDADR0;
|
||||||
|
u32 mFSADR0;
|
||||||
|
u32 mFIDR0;
|
||||||
|
u32 mLDCMD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* command structure used by metronome controller */
|
||||||
|
struct metromem_cmd {
|
||||||
|
u16 opcode;
|
||||||
|
u16 args[((64-2)/2)];
|
||||||
|
u16 csum;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* struct used by metronome. board specific stuff comes from *board */
|
||||||
|
struct metronomefb_par {
|
||||||
|
unsigned char *metromem;
|
||||||
|
struct metromem_desc *metromem_desc;
|
||||||
|
struct metromem_cmd *metromem_cmd;
|
||||||
|
unsigned char *metromem_wfm;
|
||||||
|
unsigned char *metromem_img;
|
||||||
|
u16 *metromem_img_csum;
|
||||||
|
u16 *csum_table;
|
||||||
|
int metromemsize;
|
||||||
|
dma_addr_t metromem_dma;
|
||||||
|
dma_addr_t metromem_desc_dma;
|
||||||
|
struct fb_info *info;
|
||||||
|
struct metronome_board *board;
|
||||||
|
wait_queue_head_t waitq;
|
||||||
|
u8 frame_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* board specific routines */
|
||||||
|
struct metronome_board {
|
||||||
|
struct module *owner;
|
||||||
|
void (*free_irq)(struct fb_info *);
|
||||||
|
void (*init_gpio_regs)(struct metronomefb_par *);
|
||||||
|
void (*init_lcdc_regs)(struct metronomefb_par *);
|
||||||
|
void (*post_dma_setup)(struct metronomefb_par *);
|
||||||
|
void (*set_rst)(struct metronomefb_par *, int);
|
||||||
|
void (*set_stdby)(struct metronomefb_par *, int);
|
||||||
|
int (*met_wait_event)(struct metronomefb_par *);
|
||||||
|
int (*met_wait_event_intr)(struct metronomefb_par *);
|
||||||
|
int (*setup_irq)(struct fb_info *);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user