2008-01-30 09:53:35

by Bryan Wu

[permalink] [raw]
Subject: [PATCH 1/1] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

From: Michael Hennerich <[email protected]>

Signed-off-by: Michael Hennerich <[email protected]>
Signed-off-by: Bryan Wu <[email protected]>
---
drivers/video/Kconfig | 53 ++--
drivers/video/Makefile | 1 +
drivers/video/bfin-t350mcqb-fb.c | 685 ++++++++++++++++++++++++++++++++++++++
3 files changed, 719 insertions(+), 20 deletions(-)
create mode 100644 drivers/video/bfin-t350mcqb-fb.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 758435f..6e3bb9d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -553,6 +553,19 @@ config FB_BF54X_LQ043
help
This is the framebuffer device driver for a SHARP LQ043T1DG01 TFT LCD

+config FB_BFIN_T350MCQB
+ tristate "Varitronix COG-T350MCQB TFT LCD display (BF527 EZKIT)"
+ depends on FB && BLACKFIN
+ select BFIN_GPTIMERS
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the framebuffer device driver for a Varitronix VL-PS-COG-T350MCQB-01 display TFT LCD
+ This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI
+ It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
+
+
config FB_STI
tristate "HP STI frame buffer device support"
depends on FB && PARISC
@@ -565,7 +578,7 @@ config FB_STI
BIOS routines contained in a ROM chip in HP PA-RISC based machines.
Enabling this option will implement the linux framebuffer device
using calls to the STI BIOS routines for initialisation.
-
+
If you enable this option, you will get a planar framebuffer device
/dev/fb which will work on the most common HP graphic cards of the
NGLE family, including the artist chips (in the 7xx and Bxxx series),
@@ -1019,36 +1032,36 @@ config FB_I810
select FB_CFB_IMAGEBLIT
select VGASTATE
help
- This driver supports the on-board graphics built in to the Intel 810
+ This driver supports the on-board graphics built in to the Intel 810
and 815 chipsets. Say Y if you have and plan to use such a board.

To compile this driver as a module, choose M here: the
module will be called i810fb.

- For more information, please read
+ For more information, please read
<file:Documentation/fb/intel810.txt>

config FB_I810_GTF
bool "use VESA Generalized Timing Formula"
depends on FB_I810
help
- If you say Y, then the VESA standard, Generalized Timing Formula
+ If you say Y, then the VESA standard, Generalized Timing Formula
or GTF, will be used to calculate the required video timing values
- per video mode. Since the GTF allows nondiscrete timings
+ per video mode. Since the GTF allows nondiscrete timings
(nondiscrete being a range of values as opposed to discrete being a
- set of values), you'll be able to use any combination of horizontal
+ set of values), you'll be able to use any combination of horizontal
and vertical resolutions, and vertical refresh rates without having
to specify your own timing parameters. This is especially useful
- to maximize the performance of an aging display, or if you just
- have a display with nonstandard dimensions. A VESA compliant
+ to maximize the performance of an aging display, or if you just
+ have a display with nonstandard dimensions. A VESA compliant
monitor is recommended, but can still work with non-compliant ones.
- If you need or want this, then select this option. The timings may
- not be compliant with Intel's recommended values. Use at your own
+ If you need or want this, then select this option. The timings may
+ not be compliant with Intel's recommended values. Use at your own
risk.

- If you say N, the driver will revert to discrete video timings
+ If you say N, the driver will revert to discrete video timings
using a set recommended by Intel in their documentation.
-
+
If unsure, say N.

config FB_I810_I2C
@@ -1172,10 +1185,10 @@ config FB_MATROX_G
framebuffer section. G450/G550 secondary head and digital output
are supported without additional modules.

- The driver starts in monitor mode. You must use the matroxset tool
- (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
- swap primary and secondary head outputs, or to change output mode.
- Secondary head driver always start in 640x480 resolution and you
+ The driver starts in monitor mode. You must use the matroxset tool
+ (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
+ swap primary and secondary head outputs, or to change output mode.
+ Secondary head driver always start in 640x480 resolution and you
must use fbset to change it.

Do not forget that second head supports only 16 and 32 bpp
@@ -1273,7 +1286,7 @@ config FB_RADEON
"I2C support" and "I2C bit-banging support" in the character devices
section.

- If you say M here then "I2C support" and "I2C bit-banging support"
+ If you say M here then "I2C support" and "I2C bit-banging support"
can be build either as modules or built-in.

There is a product page at
@@ -1285,7 +1298,7 @@ config FB_RADEON_I2C
select FB_DDC
default y
help
- Say Y here if you want DDC/I2C support for your Radeon board.
+ Say Y here if you want DDC/I2C support for your Radeon board.

config FB_RADEON_BACKLIGHT
bool "Support for backlight control"
@@ -1466,7 +1479,7 @@ config FB_NEOMAGIC
select VGASTATE
help
This driver supports notebooks with NeoMagic PCI chips.
- Say Y if you have such a graphics card.
+ Say Y if you have such a graphics card.

To compile this driver as a module, choose M here: the
module will be called neofb.
@@ -1512,7 +1525,7 @@ config FB_VOODOO1
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
---help---
- Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
+ Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
Voodoo2 (cvg) based graphics card.

To compile this driver as a module, choose M here: the
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 83e02b3..03371c7 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_FB_EFI) += efifb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o
obj-$(CONFIG_FB_OF) += offb.o
obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
+obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o

# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c
new file mode 100644
index 0000000..a2bb2de
--- /dev/null
+++ b/drivers/video/bfin-t350mcqb-fb.c
@@ -0,0 +1,685 @@
+/*
+ * File: drivers/video/bfin-t350mcqb-fb.c
+ * Based on:
+ * Author: Michael Hennerich <[email protected]>
+ *
+ * Created:
+ * Description: Blackfin LCD Framebufer driver
+ *
+ *
+ * Modified:
+ * Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <asm/blackfin.h>
+#include <asm/irq.h>
+#include <asm/dma-mapping.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <asm/gptimers.h>
+
+#define NO_BL_SUPPORT
+
+#define LCD_X_RES 320 /* Horizontal Resolution */
+#define LCD_Y_RES 240 /* Vertical Resolution */
+#define LCD_BPP 24 /* Bit Per Pixel */
+
+#define DMA_BUS_SIZE 16
+#define LCD_CLK (12*1000*1000) /* 12MHz */
+
+#define CLOCKS_PER_PIX 3
+
+ /*
+ * HS and VS timing parameters (all in number of PPI clk ticks)
+ */
+
+#define U_LINE 1 /* Blanking Lines */
+
+#define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */
+#define H_PERIOD (408 * CLOCKS_PER_PIX) /* HS period */
+#define H_PULSE 90 /* HS pulse width */
+#define H_START 204 /* first valid pixel */
+
+#define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */
+#define V_PULSE (3 * H_PERIOD) /* VS pulse width (1-5 H_PERIODs) */
+#define V_PERIOD (H_PERIOD * V_LINES) /* VS period */
+
+#define ACTIVE_VIDEO_MEM_OFFSET (U_LINE * H_ACTPIX)
+
+#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
+
+#define DRIVER_NAME "bfin-t350mcqb"
+static char driver_name[] = DRIVER_NAME;
+
+struct bfin_t350mcqbfb_info {
+ struct fb_info *fb;
+ struct device *dev;
+ unsigned char *fb_buffer; /* RGB Buffer */
+ dma_addr_t dma_handle;
+ int lq043_mmap;
+ int lq043_open_cnt;
+ int irq;
+ spinlock_t lock; /* lock */
+};
+
+static int nocursor;
+module_param(nocursor, int, 0644);
+MODULE_PARM_DESC(nocursor, "cursor enable/disable");
+
+#define PPI_TX_MODE 0x2
+#define PPI_XFER_TYPE_11 0xC
+#define PPI_PORT_CFG_01 0x10
+#define PPI_PACK_EN 0x80
+#define PPI_POLS_1 0x8000
+
+static void bfin_t350mcqb_config_ppi(struct bfin_t350mcqbfb_info *fbi)
+{
+ bfin_write_PPI_DELAY(H_START);
+ bfin_write_PPI_COUNT(H_ACTPIX-1);
+ bfin_write_PPI_FRAME(V_LINES);
+
+ bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */
+ PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */
+ PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */
+ PPI_PACK_EN | /* packing enabled PACK_EN */
+ PPI_POLS_1); /* faling edge syncs POLS */
+}
+
+static inline void bfin_t350mcqb_disable_ppi(void)
+{
+ bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
+}
+
+static inline void bfin_t350mcqb_enable_ppi(void)
+{
+ bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
+}
+
+static void bfin_t350mcqb_start_timers(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ enable_gptimers(TIMER1bit);
+ enable_gptimers(TIMER0bit);
+ local_irq_restore(flags);
+}
+
+static void bfin_t350mcqb_stop_timers(void)
+{
+ disable_gptimers(TIMER0bit | TIMER1bit);
+
+ set_gptimer_status(0, TIMER_STATUS_TRUN0 | TIMER_STATUS_TRUN1 |
+ TIMER_STATUS_TIMIL0 | TIMER_STATUS_TIMIL1 |
+ TIMER_STATUS_TOVF0 | TIMER_STATUS_TOVF1);
+
+}
+
+static void bfin_t350mcqb_init_timers(void)
+{
+
+ bfin_t350mcqb_stop_timers();
+
+ set_gptimer_period(TIMER0_id, H_PERIOD);
+ set_gptimer_pwidth(TIMER0_id, H_PULSE);
+ set_gptimer_config(TIMER0_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
+ TIMER_TIN_SEL | TIMER_CLK_SEL|
+ TIMER_EMU_RUN);
+
+ set_gptimer_period(TIMER1_id, V_PERIOD);
+ set_gptimer_pwidth(TIMER1_id, V_PULSE);
+ set_gptimer_config(TIMER1_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
+ TIMER_TIN_SEL | TIMER_CLK_SEL |
+ TIMER_EMU_RUN);
+
+}
+
+static void bfin_t350mcqb_config_dma(struct bfin_t350mcqbfb_info *fbi)
+{
+
+ set_dma_config(CH_PPI,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO,
+ INTR_DISABLE, DIMENSION_2D,
+ DATA_SIZE_16,
+ DMA_NOSYNC_KEEP_DMA_BUF));
+ set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE);
+ set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8);
+ set_dma_y_count(CH_PPI, V_LINES);
+
+ set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8);
+ set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer);
+
+}
+
+static int bfin_t350mcqb_request_ports(int action)
+{
+ u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
+ P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
+ P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
+ P_PPI0_D6, P_PPI0_D7, 0};
+
+ if (action) {
+ if (peripheral_request_list(ppi0_req_8, DRIVER_NAME)) {
+ printk(KERN_ERR "Requesting Peripherals faild\n");
+ return -EFAULT;
+ }
+ } else
+ peripheral_free_list(ppi0_req_8);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_open(struct fb_info *info, int user)
+{
+ struct bfin_t350mcqbfb_info *fbi = info->par;
+
+ spin_lock(&fbi->lock);
+ fbi->lq043_open_cnt++;
+
+ if (fbi->lq043_open_cnt <= 1) {
+
+ bfin_t350mcqb_disable_ppi();
+ SSYNC();
+
+ bfin_t350mcqb_config_dma(fbi);
+ bfin_t350mcqb_config_ppi(fbi);
+ bfin_t350mcqb_init_timers();
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+ bfin_t350mcqb_start_timers();
+ }
+
+ spin_unlock(&fbi->lock);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_release(struct fb_info *info, int user)
+{
+ struct bfin_t350mcqbfb_info *fbi = info->par;
+
+ spin_lock(&fbi->lock);
+
+ fbi->lq043_open_cnt--;
+ fbi->lq043_mmap = 0;
+
+ if (fbi->lq043_open_cnt <= 0) {
+ bfin_t350mcqb_disable_ppi();
+ SSYNC();
+ disable_dma(CH_PPI);
+ bfin_t350mcqb_stop_timers();
+ memset(fbi->fb_buffer, 0, info->fix.smem_len);
+ }
+
+ spin_unlock(&fbi->lock);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+
+ if (var->bits_per_pixel != LCD_BPP) {
+ pr_debug("%s: depth not supported: %u BPP\n", __FUNCTION__,
+ var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ if (info->var.xres != var->xres || info->var.yres != var->yres ||
+ info->var.xres_virtual != var->xres_virtual ||
+ info->var.yres_virtual != var->yres_virtual) {
+ pr_debug("%s: Resolution not supported: X%u x Y%u \n",
+ __FUNCTION__, var->xres, var->yres);
+ return -EINVAL;
+ }
+
+ /*
+ * Memory limit
+ */
+
+ if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
+ pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
+ __FUNCTION__, var->yres_virtual);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct bfin_t350mcqbfb_info *fbi = info->par;
+
+ if (fbi->lq043_mmap)
+ return -1;
+
+ spin_lock(&fbi->lock);
+ fbi->lq043_mmap = 1;
+ spin_unlock(&fbi->lock);
+
+ vma->vm_start = (unsigned long)(fbi->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET);
+
+ vma->vm_end = vma->vm_start + info->fix.smem_len;
+ /* For those who don't understand how mmap works, go read
+ * Documentation/nommu-mmap.txt.
+ * For those that do, you will know that the VM_MAYSHARE flag
+ * must be set in the vma->vm_flags structure on noMMU
+ * Other flags can be set, and are documented in
+ * include/linux/mm.h
+ */
+ vma->vm_flags |= VM_MAYSHARE;
+
+ return 0;
+}
+
+int bfin_t350mcqb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ if (nocursor)
+ return 0;
+ else
+ return -EINVAL; /* just to force soft_cursor() call */
+}
+
+static int bfin_t350mcqb_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp,
+ struct fb_info *info)
+{
+ if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
+ return -EINVAL;
+
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+
+ u32 value;
+ /* Place color in the pseudopalette */
+ if (regno > 16)
+ return -EINVAL;
+
+ red >>= (16 - info->var.red.length);
+ green >>= (16 - info->var.green.length);
+ blue >>= (16 - info->var.blue.length);
+
+ value = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ value &= 0xFFFFFF;
+
+ ((u32 *) (info->pseudo_palette))[regno] = value;
+
+ }
+
+ return 0;
+}
+
+static struct fb_ops bfin_t350mcqb_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = bfin_t350mcqb_fb_open,
+ .fb_release = bfin_t350mcqb_fb_release,
+ .fb_check_var = bfin_t350mcqb_fb_check_var,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_mmap = bfin_t350mcqb_fb_mmap,
+ .fb_cursor = bfin_t350mcqb_fb_cursor,
+ .fb_setcolreg = bfin_t350mcqb_fb_setcolreg,
+};
+
+#ifndef NO_BL_SUPPORT
+static int bl_get_brightness(struct backlight_device *bd)
+{
+ return 0;
+}
+
+static struct backlight_ops bfin_lq043fb_bl_ops = {
+ .get_brightness = bl_get_brightness,
+};
+
+static struct backlight_device *bl_dev;
+
+static int bfin_lcd_get_power(struct lcd_device *dev)
+{
+ return 0;
+}
+
+static int bfin_lcd_set_power(struct lcd_device *dev, int power)
+{
+ return 0;
+}
+
+static int bfin_lcd_get_contrast(struct lcd_device *dev)
+{
+ return 0;
+}
+
+static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
+{
+
+ return 0;
+}
+
+static int bfin_lcd_check_fb(struct fb_info *fi)
+{
+ if (!fi || (fi == &bfin_t350mcqb_fb))
+ return 1;
+ return 0;
+}
+
+static struct lcd_ops bfin_lcd_ops = {
+ .get_power = bfin_lcd_get_power,
+ .set_power = bfin_lcd_set_power,
+ .get_contrast = bfin_lcd_get_contrast,
+ .set_contrast = bfin_lcd_set_contrast,
+ .check_fb = bfin_lcd_check_fb,
+};
+
+static struct lcd_device *lcd_dev;
+#endif
+
+static irqreturn_t bfin_t350mcqb_irq_error(int irq, void *dev_id)
+{
+ /*struct bfin_t350mcqbfb_info *info = (struct bfin_t350mcqbfb_info *)dev_id;*/
+
+ u16 status = bfin_read_PPI_STATUS();
+ bfin_write_PPI_STATUS(0xFFFF);
+
+ if (status) {
+ bfin_t350mcqb_disable_ppi();
+ disable_dma(CH_PPI);
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+ bfin_write_PPI_STATUS(0xFFFF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __init bfin_t350mcqb_probe(struct platform_device *pdev)
+{
+ struct bfin_t350mcqbfb_info *info;
+ struct fb_info *fbinfo;
+ int ret;
+
+ printk(KERN_INFO DRIVER_NAME ": %dx%d %d-bit RGB FrameBuffer initializing...\n",
+ LCD_X_RES, LCD_Y_RES, LCD_BPP);
+
+ if (request_dma(CH_PPI, "CH_PPI") < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": couldn't request CH_PPI DMA\n");
+ ret = -EFAULT;
+ goto out1;
+ }
+
+ fbinfo =
+ framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev);
+ if (!fbinfo) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ info = fbinfo->par;
+ info->fb = fbinfo;
+ info->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, fbinfo);
+
+ strcpy(fbinfo->fix.id, driver_name);
+
+ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbinfo->fix.type_aux = 0;
+ fbinfo->fix.xpanstep = 0;
+ fbinfo->fix.ypanstep = 0;
+ fbinfo->fix.ywrapstep = 0;
+ fbinfo->fix.accel = FB_ACCEL_NONE;
+ fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ fbinfo->var.nonstd = 0;
+ fbinfo->var.activate = FB_ACTIVATE_NOW;
+ fbinfo->var.height = -1;
+ fbinfo->var.width = -1;
+ fbinfo->var.accel_flags = 0;
+ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+ fbinfo->var.xres = LCD_X_RES;
+ fbinfo->var.xres_virtual = LCD_X_RES;
+ fbinfo->var.yres = LCD_Y_RES;
+ fbinfo->var.yres_virtual = LCD_Y_RES;
+ fbinfo->var.bits_per_pixel = LCD_BPP;
+
+ fbinfo->var.red.offset = 0;
+ fbinfo->var.green.offset = 8;
+ fbinfo->var.blue.offset = 16;
+ fbinfo->var.transp.offset = 0;
+ fbinfo->var.red.length = 8;
+ fbinfo->var.green.length = 8;
+ fbinfo->var.blue.length = 8;
+ fbinfo->var.transp.length = 0;
+ fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8;
+
+ fbinfo->fix.line_length = fbinfo->var.xres_virtual *
+ fbinfo->var.bits_per_pixel / 8;
+
+
+ fbinfo->fbops = &bfin_t350mcqb_fb_ops;
+ fbinfo->flags = FBINFO_FLAG_DEFAULT;
+
+ info->fb_buffer =
+ dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
+ GFP_KERNEL);
+
+ if (NULL == info->fb_buffer) {
+ printk(KERN_ERR DRIVER_NAME
+ ": couldn't allocate dma buffer.\n");
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ memset(info->fb_buffer, 0, fbinfo->fix.smem_len);
+
+ fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
+ fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
+
+ fbinfo->fbops = &bfin_t350mcqb_fb_ops;
+
+ fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+ if (!fbinfo->pseudo_palette) {
+ printk(KERN_ERR DRIVER_NAME
+ "Fail to allocate pseudo_palette\n");
+
+ ret = -ENOMEM;
+ goto out4;
+ }
+
+ memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16);
+
+ if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0)
+ < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ "Fail to allocate colormap (%d entries)\n",
+ BFIN_LCD_NBR_PALETTE_ENTRIES);
+ ret = -EFAULT;
+ goto out5;
+ }
+
+ if (bfin_t350mcqb_request_ports(1)) {
+ printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
+ ret = -EFAULT;
+ goto out6;
+ }
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ ret = -EINVAL;
+ goto out7;
+ }
+
+ if (request_irq(info->irq, (void *)bfin_t350mcqb_irq_error, IRQF_DISABLED,
+ "PPI ERROR", info) < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": unable to request PPI ERROR IRQ\n");
+ ret = -EFAULT;
+ goto out7;
+ }
+
+ if (register_framebuffer(fbinfo) < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": unable to register framebuffer.\n");
+ ret = -EINVAL;
+ goto out8;
+ }
+#ifndef NO_BL_SUPPORT
+ bl_dev =
+ backlight_device_register("bf52x-bl", NULL, NULL,
+ &bfin_lq043fb_bl_ops);
+ bl_dev->props.max_brightness = 255;
+
+ lcd_dev = lcd_device_register(DRIVER_NAME, NULL, &bfin_lcd_ops);
+ lcd_dev->props.max_contrast = 255, printk(KERN_INFO "Done.\n");
+#endif
+
+ return 0;
+
+out8:
+ free_irq(info->irq, info);
+out7:
+ bfin_t350mcqb_request_ports(0);
+out6:
+ fb_dealloc_cmap(&fbinfo->cmap);
+out5:
+ kfree(fbinfo->pseudo_palette);
+out4:
+ dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
+ info->dma_handle);
+out3:
+ framebuffer_release(fbinfo);
+out2:
+ free_dma(CH_PPI);
+out1:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int bfin_t350mcqb_remove(struct platform_device *pdev)
+{
+
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *info = fbinfo->par;
+
+ free_dma(CH_PPI);
+ free_irq(info->irq, info);
+
+ if (info->fb_buffer != NULL)
+ dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
+ info->dma_handle);
+
+ kfree(fbinfo->pseudo_palette);
+ fb_dealloc_cmap(&fbinfo->cmap);
+
+#ifndef NO_BL_SUPPORT
+ lcd_device_unregister(lcd_dev);
+ backlight_device_unregister(bl_dev);
+#endif
+
+ unregister_framebuffer(fbinfo);
+
+ bfin_t350mcqb_request_ports(0);
+
+ printk(KERN_INFO DRIVER_NAME ": Unregister LCD driver.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_t350mcqb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *info = fbinfo->par;
+
+ bfin_t350mcqb_disable_ppi();
+ disable_dma(CH_PPI);
+ bfin_write_PPI_STATUS(0xFFFF);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *info = fbinfo->par;
+
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+
+ return 0;
+}
+#else
+#define bfin_t350mcqb_suspend NULL
+#define bfin_t350mcqb_resume NULL
+#endif
+
+static struct platform_driver bfin_t350mcqb_driver = {
+ .probe = bfin_t350mcqb_probe,
+ .remove = bfin_t350mcqb_remove,
+ .suspend = bfin_t350mcqb_suspend,
+ .resume = bfin_t350mcqb_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit bfin_t350mcqb_driver_init(void)
+{
+ return platform_driver_register(&bfin_t350mcqb_driver);
+}
+
+static void __exit bfin_t350mcqb_driver_cleanup(void)
+{
+ platform_driver_unregister(&bfin_t350mcqb_driver);
+}
+
+MODULE_DESCRIPTION("Blackfin TFT LCD Driver");
+MODULE_LICENSE("GPL");
+
+module_init(bfin_t350mcqb_driver_init);
+module_exit(bfin_t350mcqb_driver_cleanup);
--
1.5.3.4


2008-01-30 16:39:55

by Randy Dunlap

[permalink] [raw]
Subject: Re: [Linux-fbdev-devel] [PATCH 1/1] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

On Wed, 30 Jan 2008 17:53:20 +0800 Bryan Wu wrote:

> From: Michael Hennerich <[email protected]>
>
> Signed-off-by: Michael Hennerich <[email protected]>
> Signed-off-by: Bryan Wu <[email protected]>
> ---
> drivers/video/Kconfig | 53 ++--

All of these non-bfin changes to Kconfig shouldn't be part of
this patch.


> drivers/video/Makefile | 1 +
> drivers/video/bfin-t350mcqb-fb.c | 685 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 719 insertions(+), 20 deletions(-)
> create mode 100644 drivers/video/bfin-t350mcqb-fb.c
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 758435f..6e3bb9d 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -553,6 +553,19 @@ config FB_BF54X_LQ043
> help
> This is the framebuffer device driver for a SHARP LQ043T1DG01 TFT LCD
>
> +config FB_BFIN_T350MCQB
> + tristate "Varitronix COG-T350MCQB TFT LCD display (BF527 EZKIT)"
> + depends on FB && BLACKFIN
> + select BFIN_GPTIMERS
> + select FB_CFB_FILLRECT
> + select FB_CFB_COPYAREA
> + select FB_CFB_IMAGEBLIT
> + help
> + This is the framebuffer device driver for a Varitronix VL-PS-COG-T350MCQB-01 display TFT LCD
> + This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI
> + It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
> +
> +
> config FB_STI
> tristate "HP STI frame buffer device support"
> depends on FB && PARISC
> @@ -565,7 +578,7 @@ config FB_STI
> BIOS routines contained in a ROM chip in HP PA-RISC based machines.
> Enabling this option will implement the linux framebuffer device
> using calls to the STI BIOS routines for initialisation.
> -
> +
> If you enable this option, you will get a planar framebuffer device
> /dev/fb which will work on the most common HP graphic cards of the
> NGLE family, including the artist chips (in the 7xx and Bxxx series),
> @@ -1019,36 +1032,36 @@ config FB_I810
> select FB_CFB_IMAGEBLIT
> select VGASTATE
> help
> - This driver supports the on-board graphics built in to the Intel 810
> + This driver supports the on-board graphics built in to the Intel 810
> and 815 chipsets. Say Y if you have and plan to use such a board.
>
> To compile this driver as a module, choose M here: the
> module will be called i810fb.
>
> - For more information, please read
> + For more information, please read
> <file:Documentation/fb/intel810.txt>
>
> config FB_I810_GTF
> bool "use VESA Generalized Timing Formula"
> depends on FB_I810
> help
> - If you say Y, then the VESA standard, Generalized Timing Formula
> + If you say Y, then the VESA standard, Generalized Timing Formula
> or GTF, will be used to calculate the required video timing values
> - per video mode. Since the GTF allows nondiscrete timings
> + per video mode. Since the GTF allows nondiscrete timings
> (nondiscrete being a range of values as opposed to discrete being a
> - set of values), you'll be able to use any combination of horizontal
> + set of values), you'll be able to use any combination of horizontal
> and vertical resolutions, and vertical refresh rates without having
> to specify your own timing parameters. This is especially useful
> - to maximize the performance of an aging display, or if you just
> - have a display with nonstandard dimensions. A VESA compliant
> + to maximize the performance of an aging display, or if you just
> + have a display with nonstandard dimensions. A VESA compliant
> monitor is recommended, but can still work with non-compliant ones.
> - If you need or want this, then select this option. The timings may
> - not be compliant with Intel's recommended values. Use at your own
> + If you need or want this, then select this option. The timings may
> + not be compliant with Intel's recommended values. Use at your own
> risk.
>
> - If you say N, the driver will revert to discrete video timings
> + If you say N, the driver will revert to discrete video timings
> using a set recommended by Intel in their documentation.
> -
> +
> If unsure, say N.
>
> config FB_I810_I2C
> @@ -1172,10 +1185,10 @@ config FB_MATROX_G
> framebuffer section. G450/G550 secondary head and digital output
> are supported without additional modules.
>
> - The driver starts in monitor mode. You must use the matroxset tool
> - (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
> - swap primary and secondary head outputs, or to change output mode.
> - Secondary head driver always start in 640x480 resolution and you
> + The driver starts in monitor mode. You must use the matroxset tool
> + (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
> + swap primary and secondary head outputs, or to change output mode.
> + Secondary head driver always start in 640x480 resolution and you
> must use fbset to change it.
>
> Do not forget that second head supports only 16 and 32 bpp
> @@ -1273,7 +1286,7 @@ config FB_RADEON
> "I2C support" and "I2C bit-banging support" in the character devices
> section.
>
> - If you say M here then "I2C support" and "I2C bit-banging support"
> + If you say M here then "I2C support" and "I2C bit-banging support"
> can be build either as modules or built-in.
>
> There is a product page at
> @@ -1285,7 +1298,7 @@ config FB_RADEON_I2C
> select FB_DDC
> default y
> help
> - Say Y here if you want DDC/I2C support for your Radeon board.
> + Say Y here if you want DDC/I2C support for your Radeon board.
>
> config FB_RADEON_BACKLIGHT
> bool "Support for backlight control"
> @@ -1466,7 +1479,7 @@ config FB_NEOMAGIC
> select VGASTATE
> help
> This driver supports notebooks with NeoMagic PCI chips.
> - Say Y if you have such a graphics card.
> + Say Y if you have such a graphics card.
>
> To compile this driver as a module, choose M here: the
> module will be called neofb.
> @@ -1512,7 +1525,7 @@ config FB_VOODOO1
> select FB_CFB_COPYAREA
> select FB_CFB_IMAGEBLIT
> ---help---
> - Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
> + Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
> Voodoo2 (cvg) based graphics card.
>
> To compile this driver as a module, choose M here: the


---
~Randy

2008-01-30 16:42:25

by Bryan Wu

[permalink] [raw]
Subject: Re: [Linux-fbdev-devel] [PATCH 1/1] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver


On Wed, 2008-01-30 at 08:38 -0800, Randy Dunlap wrote:
> On Wed, 30 Jan 2008 17:53:20 +0800 Bryan Wu wrote:
>
> > From: Michael Hennerich <[email protected]>
> >
> > Signed-off-by: Michael Hennerich <[email protected]>
> > Signed-off-by: Bryan Wu <[email protected]>
> > ---
> > drivers/video/Kconfig | 53 ++--
>
> All of these non-bfin changes to Kconfig shouldn't be part of
> this patch.
>

Oops, that is my fault. I will fix this soon. thanks

-Bryan

>
> > drivers/video/Makefile | 1 +
> > drivers/video/bfin-t350mcqb-fb.c | 685 ++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 719 insertions(+), 20 deletions(-)
> > create mode 100644 drivers/video/bfin-t350mcqb-fb.c
> >
> > diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> > index 758435f..6e3bb9d 100644
> > --- a/drivers/video/Kconfig
> > +++ b/drivers/video/Kconfig
> > @@ -553,6 +553,19 @@ config FB_BF54X_LQ043
> > help
> > This is the framebuffer device driver for a SHARP LQ043T1DG01 TFT LCD
> >
> > +config FB_BFIN_T350MCQB
> > + tristate "Varitronix COG-T350MCQB TFT LCD display (BF527 EZKIT)"
> > + depends on FB && BLACKFIN
> > + select BFIN_GPTIMERS
> > + select FB_CFB_FILLRECT
> > + select FB_CFB_COPYAREA
> > + select FB_CFB_IMAGEBLIT
> > + help
> > + This is the framebuffer device driver for a Varitronix VL-PS-COG-T350MCQB-01 display TFT LCD
> > + This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI
> > + It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
> > +
> > +
> > config FB_STI
> > tristate "HP STI frame buffer device support"
> > depends on FB && PARISC
> > @@ -565,7 +578,7 @@ config FB_STI
> > BIOS routines contained in a ROM chip in HP PA-RISC based machines.
> > Enabling this option will implement the linux framebuffer device
> > using calls to the STI BIOS routines for initialisation.
> > -
> > +
> > If you enable this option, you will get a planar framebuffer device
> > /dev/fb which will work on the most common HP graphic cards of the
> > NGLE family, including the artist chips (in the 7xx and Bxxx series),
> > @@ -1019,36 +1032,36 @@ config FB_I810
> > select FB_CFB_IMAGEBLIT
> > select VGASTATE
> > help
> > - This driver supports the on-board graphics built in to the Intel 810
> > + This driver supports the on-board graphics built in to the Intel 810
> > and 815 chipsets. Say Y if you have and plan to use such a board.
> >
> > To compile this driver as a module, choose M here: the
> > module will be called i810fb.
> >
> > - For more information, please read
> > + For more information, please read
> > <file:Documentation/fb/intel810.txt>
> >
> > config FB_I810_GTF
> > bool "use VESA Generalized Timing Formula"
> > depends on FB_I810
> > help
> > - If you say Y, then the VESA standard, Generalized Timing Formula
> > + If you say Y, then the VESA standard, Generalized Timing Formula
> > or GTF, will be used to calculate the required video timing values
> > - per video mode. Since the GTF allows nondiscrete timings
> > + per video mode. Since the GTF allows nondiscrete timings
> > (nondiscrete being a range of values as opposed to discrete being a
> > - set of values), you'll be able to use any combination of horizontal
> > + set of values), you'll be able to use any combination of horizontal
> > and vertical resolutions, and vertical refresh rates without having
> > to specify your own timing parameters. This is especially useful
> > - to maximize the performance of an aging display, or if you just
> > - have a display with nonstandard dimensions. A VESA compliant
> > + to maximize the performance of an aging display, or if you just
> > + have a display with nonstandard dimensions. A VESA compliant
> > monitor is recommended, but can still work with non-compliant ones.
> > - If you need or want this, then select this option. The timings may
> > - not be compliant with Intel's recommended values. Use at your own
> > + If you need or want this, then select this option. The timings may
> > + not be compliant with Intel's recommended values. Use at your own
> > risk.
> >
> > - If you say N, the driver will revert to discrete video timings
> > + If you say N, the driver will revert to discrete video timings
> > using a set recommended by Intel in their documentation.
> > -
> > +
> > If unsure, say N.
> >
> > config FB_I810_I2C
> > @@ -1172,10 +1185,10 @@ config FB_MATROX_G
> > framebuffer section. G450/G550 secondary head and digital output
> > are supported without additional modules.
> >
> > - The driver starts in monitor mode. You must use the matroxset tool
> > - (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
> > - swap primary and secondary head outputs, or to change output mode.
> > - Secondary head driver always start in 640x480 resolution and you
> > + The driver starts in monitor mode. You must use the matroxset tool
> > + (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
> > + swap primary and secondary head outputs, or to change output mode.
> > + Secondary head driver always start in 640x480 resolution and you
> > must use fbset to change it.
> >
> > Do not forget that second head supports only 16 and 32 bpp
> > @@ -1273,7 +1286,7 @@ config FB_RADEON
> > "I2C support" and "I2C bit-banging support" in the character devices
> > section.
> >
> > - If you say M here then "I2C support" and "I2C bit-banging support"
> > + If you say M here then "I2C support" and "I2C bit-banging support"
> > can be build either as modules or built-in.
> >
> > There is a product page at
> > @@ -1285,7 +1298,7 @@ config FB_RADEON_I2C
> > select FB_DDC
> > default y
> > help
> > - Say Y here if you want DDC/I2C support for your Radeon board.
> > + Say Y here if you want DDC/I2C support for your Radeon board.
> >
> > config FB_RADEON_BACKLIGHT
> > bool "Support for backlight control"
> > @@ -1466,7 +1479,7 @@ config FB_NEOMAGIC
> > select VGASTATE
> > help
> > This driver supports notebooks with NeoMagic PCI chips.
> > - Say Y if you have such a graphics card.
> > + Say Y if you have such a graphics card.
> >
> > To compile this driver as a module, choose M here: the
> > module will be called neofb.
> > @@ -1512,7 +1525,7 @@ config FB_VOODOO1
> > select FB_CFB_COPYAREA
> > select FB_CFB_IMAGEBLIT
> > ---help---
> > - Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
> > + Say Y here if you have a 3Dfx Voodoo Graphics (Voodoo1/sst1) or
> > Voodoo2 (cvg) based graphics card.
> >
> > To compile this driver as a module, choose M here: the
>
>
> ---
> ~Randy

2008-01-30 16:50:16

by Bryan Wu

[permalink] [raw]
Subject: [PATCH 1/1 try#2] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

>From 156c001b0c3e1d6481b41f978badcea74ec8e765 Mon Sep 17 00:00:00 2001
From: Michael Hennerich <[email protected]>
Date: Thu, 31 Jan 2008 00:46:01 +0800
Subject: [PATCH] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

Signed-off-by: Michael Hennerich <[email protected]>
Signed-off-by: Bryan Wu <[email protected]>
---
drivers/video/Kconfig | 13 +
drivers/video/Makefile | 1 +
drivers/video/bfin-t350mcqb-fb.c | 685 ++++++++++++++++++++++++++++++++++++++
3 files changed, 699 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/bfin-t350mcqb-fb.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 758435f..75debf6 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -553,6 +553,19 @@ config FB_BF54X_LQ043
help
This is the framebuffer device driver for a SHARP LQ043T1DG01 TFT LCD

+config FB_BFIN_T350MCQB
+ tristate "Varitronix COG-T350MCQB TFT LCD display (BF527 EZKIT)"
+ depends on FB && BLACKFIN
+ select BFIN_GPTIMERS
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the framebuffer device driver for a Varitronix VL-PS-COG-T350MCQB-01 display TFT LCD
+ This display is a QVGA 320x240 24-bit RGB display interfaced by an 8-bit wide PPI
+ It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
+
+
config FB_STI
tristate "HP STI frame buffer device support"
depends on FB && PARISC
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 83e02b3..03371c7 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_FB_EFI) += efifb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o
obj-$(CONFIG_FB_OF) += offb.o
obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
+obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o

# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c
new file mode 100644
index 0000000..a2bb2de
--- /dev/null
+++ b/drivers/video/bfin-t350mcqb-fb.c
@@ -0,0 +1,685 @@
+/*
+ * File: drivers/video/bfin-t350mcqb-fb.c
+ * Based on:
+ * Author: Michael Hennerich <[email protected]>
+ *
+ * Created:
+ * Description: Blackfin LCD Framebufer driver
+ *
+ *
+ * Modified:
+ * Copyright 2004-2007 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <asm/blackfin.h>
+#include <asm/irq.h>
+#include <asm/dma-mapping.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+#include <asm/gptimers.h>
+
+#define NO_BL_SUPPORT
+
+#define LCD_X_RES 320 /* Horizontal Resolution */
+#define LCD_Y_RES 240 /* Vertical Resolution */
+#define LCD_BPP 24 /* Bit Per Pixel */
+
+#define DMA_BUS_SIZE 16
+#define LCD_CLK (12*1000*1000) /* 12MHz */
+
+#define CLOCKS_PER_PIX 3
+
+ /*
+ * HS and VS timing parameters (all in number of PPI clk ticks)
+ */
+
+#define U_LINE 1 /* Blanking Lines */
+
+#define H_ACTPIX (LCD_X_RES * CLOCKS_PER_PIX) /* active horizontal pixel */
+#define H_PERIOD (408 * CLOCKS_PER_PIX) /* HS period */
+#define H_PULSE 90 /* HS pulse width */
+#define H_START 204 /* first valid pixel */
+
+#define V_LINES (LCD_Y_RES + U_LINE) /* total vertical lines */
+#define V_PULSE (3 * H_PERIOD) /* VS pulse width (1-5 H_PERIODs) */
+#define V_PERIOD (H_PERIOD * V_LINES) /* VS period */
+
+#define ACTIVE_VIDEO_MEM_OFFSET (U_LINE * H_ACTPIX)
+
+#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
+
+#define DRIVER_NAME "bfin-t350mcqb"
+static char driver_name[] = DRIVER_NAME;
+
+struct bfin_t350mcqbfb_info {
+ struct fb_info *fb;
+ struct device *dev;
+ unsigned char *fb_buffer; /* RGB Buffer */
+ dma_addr_t dma_handle;
+ int lq043_mmap;
+ int lq043_open_cnt;
+ int irq;
+ spinlock_t lock; /* lock */
+};
+
+static int nocursor;
+module_param(nocursor, int, 0644);
+MODULE_PARM_DESC(nocursor, "cursor enable/disable");
+
+#define PPI_TX_MODE 0x2
+#define PPI_XFER_TYPE_11 0xC
+#define PPI_PORT_CFG_01 0x10
+#define PPI_PACK_EN 0x80
+#define PPI_POLS_1 0x8000
+
+static void bfin_t350mcqb_config_ppi(struct bfin_t350mcqbfb_info *fbi)
+{
+ bfin_write_PPI_DELAY(H_START);
+ bfin_write_PPI_COUNT(H_ACTPIX-1);
+ bfin_write_PPI_FRAME(V_LINES);
+
+ bfin_write_PPI_CONTROL(PPI_TX_MODE | /* output mode , PORT_DIR */
+ PPI_XFER_TYPE_11 | /* sync mode XFR_TYPE */
+ PPI_PORT_CFG_01 | /* two frame sync PORT_CFG */
+ PPI_PACK_EN | /* packing enabled PACK_EN */
+ PPI_POLS_1); /* faling edge syncs POLS */
+}
+
+static inline void bfin_t350mcqb_disable_ppi(void)
+{
+ bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
+}
+
+static inline void bfin_t350mcqb_enable_ppi(void)
+{
+ bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
+}
+
+static void bfin_t350mcqb_start_timers(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ enable_gptimers(TIMER1bit);
+ enable_gptimers(TIMER0bit);
+ local_irq_restore(flags);
+}
+
+static void bfin_t350mcqb_stop_timers(void)
+{
+ disable_gptimers(TIMER0bit | TIMER1bit);
+
+ set_gptimer_status(0, TIMER_STATUS_TRUN0 | TIMER_STATUS_TRUN1 |
+ TIMER_STATUS_TIMIL0 | TIMER_STATUS_TIMIL1 |
+ TIMER_STATUS_TOVF0 | TIMER_STATUS_TOVF1);
+
+}
+
+static void bfin_t350mcqb_init_timers(void)
+{
+
+ bfin_t350mcqb_stop_timers();
+
+ set_gptimer_period(TIMER0_id, H_PERIOD);
+ set_gptimer_pwidth(TIMER0_id, H_PULSE);
+ set_gptimer_config(TIMER0_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
+ TIMER_TIN_SEL | TIMER_CLK_SEL|
+ TIMER_EMU_RUN);
+
+ set_gptimer_period(TIMER1_id, V_PERIOD);
+ set_gptimer_pwidth(TIMER1_id, V_PULSE);
+ set_gptimer_config(TIMER1_id, TIMER_MODE_PWM | TIMER_PERIOD_CNT |
+ TIMER_TIN_SEL | TIMER_CLK_SEL |
+ TIMER_EMU_RUN);
+
+}
+
+static void bfin_t350mcqb_config_dma(struct bfin_t350mcqbfb_info *fbi)
+{
+
+ set_dma_config(CH_PPI,
+ set_bfin_dma_config(DIR_READ, DMA_FLOW_AUTO,
+ INTR_DISABLE, DIMENSION_2D,
+ DATA_SIZE_16,
+ DMA_NOSYNC_KEEP_DMA_BUF));
+ set_dma_x_count(CH_PPI, (LCD_X_RES * LCD_BPP) / DMA_BUS_SIZE);
+ set_dma_x_modify(CH_PPI, DMA_BUS_SIZE / 8);
+ set_dma_y_count(CH_PPI, V_LINES);
+
+ set_dma_y_modify(CH_PPI, DMA_BUS_SIZE / 8);
+ set_dma_start_addr(CH_PPI, (unsigned long)fbi->fb_buffer);
+
+}
+
+static int bfin_t350mcqb_request_ports(int action)
+{
+ u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
+ P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
+ P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
+ P_PPI0_D6, P_PPI0_D7, 0};
+
+ if (action) {
+ if (peripheral_request_list(ppi0_req_8, DRIVER_NAME)) {
+ printk(KERN_ERR "Requesting Peripherals faild\n");
+ return -EFAULT;
+ }
+ } else
+ peripheral_free_list(ppi0_req_8);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_open(struct fb_info *info, int user)
+{
+ struct bfin_t350mcqbfb_info *fbi = info->par;
+
+ spin_lock(&fbi->lock);
+ fbi->lq043_open_cnt++;
+
+ if (fbi->lq043_open_cnt <= 1) {
+
+ bfin_t350mcqb_disable_ppi();
+ SSYNC();
+
+ bfin_t350mcqb_config_dma(fbi);
+ bfin_t350mcqb_config_ppi(fbi);
+ bfin_t350mcqb_init_timers();
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+ bfin_t350mcqb_start_timers();
+ }
+
+ spin_unlock(&fbi->lock);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_release(struct fb_info *info, int user)
+{
+ struct bfin_t350mcqbfb_info *fbi = info->par;
+
+ spin_lock(&fbi->lock);
+
+ fbi->lq043_open_cnt--;
+ fbi->lq043_mmap = 0;
+
+ if (fbi->lq043_open_cnt <= 0) {
+ bfin_t350mcqb_disable_ppi();
+ SSYNC();
+ disable_dma(CH_PPI);
+ bfin_t350mcqb_stop_timers();
+ memset(fbi->fb_buffer, 0, info->fix.smem_len);
+ }
+
+ spin_unlock(&fbi->lock);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+
+ if (var->bits_per_pixel != LCD_BPP) {
+ pr_debug("%s: depth not supported: %u BPP\n", __FUNCTION__,
+ var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ if (info->var.xres != var->xres || info->var.yres != var->yres ||
+ info->var.xres_virtual != var->xres_virtual ||
+ info->var.yres_virtual != var->yres_virtual) {
+ pr_debug("%s: Resolution not supported: X%u x Y%u \n",
+ __FUNCTION__, var->xres, var->yres);
+ return -EINVAL;
+ }
+
+ /*
+ * Memory limit
+ */
+
+ if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
+ pr_debug("%s: Memory Limit requested yres_virtual = %u\n",
+ __FUNCTION__, var->yres_virtual);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int bfin_t350mcqb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct bfin_t350mcqbfb_info *fbi = info->par;
+
+ if (fbi->lq043_mmap)
+ return -1;
+
+ spin_lock(&fbi->lock);
+ fbi->lq043_mmap = 1;
+ spin_unlock(&fbi->lock);
+
+ vma->vm_start = (unsigned long)(fbi->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET);
+
+ vma->vm_end = vma->vm_start + info->fix.smem_len;
+ /* For those who don't understand how mmap works, go read
+ * Documentation/nommu-mmap.txt.
+ * For those that do, you will know that the VM_MAYSHARE flag
+ * must be set in the vma->vm_flags structure on noMMU
+ * Other flags can be set, and are documented in
+ * include/linux/mm.h
+ */
+ vma->vm_flags |= VM_MAYSHARE;
+
+ return 0;
+}
+
+int bfin_t350mcqb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ if (nocursor)
+ return 0;
+ else
+ return -EINVAL; /* just to force soft_cursor() call */
+}
+
+static int bfin_t350mcqb_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp,
+ struct fb_info *info)
+{
+ if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES)
+ return -EINVAL;
+
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+
+ u32 value;
+ /* Place color in the pseudopalette */
+ if (regno > 16)
+ return -EINVAL;
+
+ red >>= (16 - info->var.red.length);
+ green >>= (16 - info->var.green.length);
+ blue >>= (16 - info->var.blue.length);
+
+ value = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ value &= 0xFFFFFF;
+
+ ((u32 *) (info->pseudo_palette))[regno] = value;
+
+ }
+
+ return 0;
+}
+
+static struct fb_ops bfin_t350mcqb_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = bfin_t350mcqb_fb_open,
+ .fb_release = bfin_t350mcqb_fb_release,
+ .fb_check_var = bfin_t350mcqb_fb_check_var,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_mmap = bfin_t350mcqb_fb_mmap,
+ .fb_cursor = bfin_t350mcqb_fb_cursor,
+ .fb_setcolreg = bfin_t350mcqb_fb_setcolreg,
+};
+
+#ifndef NO_BL_SUPPORT
+static int bl_get_brightness(struct backlight_device *bd)
+{
+ return 0;
+}
+
+static struct backlight_ops bfin_lq043fb_bl_ops = {
+ .get_brightness = bl_get_brightness,
+};
+
+static struct backlight_device *bl_dev;
+
+static int bfin_lcd_get_power(struct lcd_device *dev)
+{
+ return 0;
+}
+
+static int bfin_lcd_set_power(struct lcd_device *dev, int power)
+{
+ return 0;
+}
+
+static int bfin_lcd_get_contrast(struct lcd_device *dev)
+{
+ return 0;
+}
+
+static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
+{
+
+ return 0;
+}
+
+static int bfin_lcd_check_fb(struct fb_info *fi)
+{
+ if (!fi || (fi == &bfin_t350mcqb_fb))
+ return 1;
+ return 0;
+}
+
+static struct lcd_ops bfin_lcd_ops = {
+ .get_power = bfin_lcd_get_power,
+ .set_power = bfin_lcd_set_power,
+ .get_contrast = bfin_lcd_get_contrast,
+ .set_contrast = bfin_lcd_set_contrast,
+ .check_fb = bfin_lcd_check_fb,
+};
+
+static struct lcd_device *lcd_dev;
+#endif
+
+static irqreturn_t bfin_t350mcqb_irq_error(int irq, void *dev_id)
+{
+ /*struct bfin_t350mcqbfb_info *info = (struct bfin_t350mcqbfb_info *)dev_id;*/
+
+ u16 status = bfin_read_PPI_STATUS();
+ bfin_write_PPI_STATUS(0xFFFF);
+
+ if (status) {
+ bfin_t350mcqb_disable_ppi();
+ disable_dma(CH_PPI);
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+ bfin_write_PPI_STATUS(0xFFFF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __init bfin_t350mcqb_probe(struct platform_device *pdev)
+{
+ struct bfin_t350mcqbfb_info *info;
+ struct fb_info *fbinfo;
+ int ret;
+
+ printk(KERN_INFO DRIVER_NAME ": %dx%d %d-bit RGB FrameBuffer initializing...\n",
+ LCD_X_RES, LCD_Y_RES, LCD_BPP);
+
+ if (request_dma(CH_PPI, "CH_PPI") < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": couldn't request CH_PPI DMA\n");
+ ret = -EFAULT;
+ goto out1;
+ }
+
+ fbinfo =
+ framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev);
+ if (!fbinfo) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ info = fbinfo->par;
+ info->fb = fbinfo;
+ info->dev = &pdev->dev;
+
+ platform_set_drvdata(pdev, fbinfo);
+
+ strcpy(fbinfo->fix.id, driver_name);
+
+ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbinfo->fix.type_aux = 0;
+ fbinfo->fix.xpanstep = 0;
+ fbinfo->fix.ypanstep = 0;
+ fbinfo->fix.ywrapstep = 0;
+ fbinfo->fix.accel = FB_ACCEL_NONE;
+ fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ fbinfo->var.nonstd = 0;
+ fbinfo->var.activate = FB_ACTIVATE_NOW;
+ fbinfo->var.height = -1;
+ fbinfo->var.width = -1;
+ fbinfo->var.accel_flags = 0;
+ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+ fbinfo->var.xres = LCD_X_RES;
+ fbinfo->var.xres_virtual = LCD_X_RES;
+ fbinfo->var.yres = LCD_Y_RES;
+ fbinfo->var.yres_virtual = LCD_Y_RES;
+ fbinfo->var.bits_per_pixel = LCD_BPP;
+
+ fbinfo->var.red.offset = 0;
+ fbinfo->var.green.offset = 8;
+ fbinfo->var.blue.offset = 16;
+ fbinfo->var.transp.offset = 0;
+ fbinfo->var.red.length = 8;
+ fbinfo->var.green.length = 8;
+ fbinfo->var.blue.length = 8;
+ fbinfo->var.transp.length = 0;
+ fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8;
+
+ fbinfo->fix.line_length = fbinfo->var.xres_virtual *
+ fbinfo->var.bits_per_pixel / 8;
+
+
+ fbinfo->fbops = &bfin_t350mcqb_fb_ops;
+ fbinfo->flags = FBINFO_FLAG_DEFAULT;
+
+ info->fb_buffer =
+ dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
+ GFP_KERNEL);
+
+ if (NULL == info->fb_buffer) {
+ printk(KERN_ERR DRIVER_NAME
+ ": couldn't allocate dma buffer.\n");
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ memset(info->fb_buffer, 0, fbinfo->fix.smem_len);
+
+ fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
+ fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
+
+ fbinfo->fbops = &bfin_t350mcqb_fb_ops;
+
+ fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+ if (!fbinfo->pseudo_palette) {
+ printk(KERN_ERR DRIVER_NAME
+ "Fail to allocate pseudo_palette\n");
+
+ ret = -ENOMEM;
+ goto out4;
+ }
+
+ memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16);
+
+ if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0)
+ < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ "Fail to allocate colormap (%d entries)\n",
+ BFIN_LCD_NBR_PALETTE_ENTRIES);
+ ret = -EFAULT;
+ goto out5;
+ }
+
+ if (bfin_t350mcqb_request_ports(1)) {
+ printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
+ ret = -EFAULT;
+ goto out6;
+ }
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ ret = -EINVAL;
+ goto out7;
+ }
+
+ if (request_irq(info->irq, (void *)bfin_t350mcqb_irq_error, IRQF_DISABLED,
+ "PPI ERROR", info) < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": unable to request PPI ERROR IRQ\n");
+ ret = -EFAULT;
+ goto out7;
+ }
+
+ if (register_framebuffer(fbinfo) < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": unable to register framebuffer.\n");
+ ret = -EINVAL;
+ goto out8;
+ }
+#ifndef NO_BL_SUPPORT
+ bl_dev =
+ backlight_device_register("bf52x-bl", NULL, NULL,
+ &bfin_lq043fb_bl_ops);
+ bl_dev->props.max_brightness = 255;
+
+ lcd_dev = lcd_device_register(DRIVER_NAME, NULL, &bfin_lcd_ops);
+ lcd_dev->props.max_contrast = 255, printk(KERN_INFO "Done.\n");
+#endif
+
+ return 0;
+
+out8:
+ free_irq(info->irq, info);
+out7:
+ bfin_t350mcqb_request_ports(0);
+out6:
+ fb_dealloc_cmap(&fbinfo->cmap);
+out5:
+ kfree(fbinfo->pseudo_palette);
+out4:
+ dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
+ info->dma_handle);
+out3:
+ framebuffer_release(fbinfo);
+out2:
+ free_dma(CH_PPI);
+out1:
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int bfin_t350mcqb_remove(struct platform_device *pdev)
+{
+
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *info = fbinfo->par;
+
+ free_dma(CH_PPI);
+ free_irq(info->irq, info);
+
+ if (info->fb_buffer != NULL)
+ dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
+ info->dma_handle);
+
+ kfree(fbinfo->pseudo_palette);
+ fb_dealloc_cmap(&fbinfo->cmap);
+
+#ifndef NO_BL_SUPPORT
+ lcd_device_unregister(lcd_dev);
+ backlight_device_unregister(bl_dev);
+#endif
+
+ unregister_framebuffer(fbinfo);
+
+ bfin_t350mcqb_request_ports(0);
+
+ printk(KERN_INFO DRIVER_NAME ": Unregister LCD driver.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_t350mcqb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *info = fbinfo->par;
+
+ bfin_t350mcqb_disable_ppi();
+ disable_dma(CH_PPI);
+ bfin_write_PPI_STATUS(0xFFFF);
+
+ return 0;
+}
+
+static int bfin_t350mcqb_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct bfin_t350mcqbfb_info *info = fbinfo->par;
+
+ enable_dma(CH_PPI);
+ bfin_t350mcqb_enable_ppi();
+
+ return 0;
+}
+#else
+#define bfin_t350mcqb_suspend NULL
+#define bfin_t350mcqb_resume NULL
+#endif
+
+static struct platform_driver bfin_t350mcqb_driver = {
+ .probe = bfin_t350mcqb_probe,
+ .remove = bfin_t350mcqb_remove,
+ .suspend = bfin_t350mcqb_suspend,
+ .resume = bfin_t350mcqb_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit bfin_t350mcqb_driver_init(void)
+{
+ return platform_driver_register(&bfin_t350mcqb_driver);
+}
+
+static void __exit bfin_t350mcqb_driver_cleanup(void)
+{
+ platform_driver_unregister(&bfin_t350mcqb_driver);
+}
+
+MODULE_DESCRIPTION("Blackfin TFT LCD Driver");
+MODULE_LICENSE("GPL");
+
+module_init(bfin_t350mcqb_driver_init);
+module_exit(bfin_t350mcqb_driver_cleanup);
--
1.5.3.4

2008-03-06 07:02:56

by Andrew Morton

[permalink] [raw]
Subject: Re: [Linux-fbdev-devel] [PATCH 1/1 try#2] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

On Thu, 31 Jan 2008 00:49:43 +0800 Bryan Wu <[email protected]> wrote:
>

bah, January.

I'm trolling the fbdev list only now - I guess I'll slip this one into
2.6.25.

> +static int bfin_t350mcqb_request_ports(int action)
> +{
> + u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
> + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
> + P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
> + P_PPI0_D6, P_PPI0_D7, 0};

This array will be assembled on the stack at runtime. If
peripheral_request_list() doesn't alter it then it would be better to
create it at compile-time by adding `static'.

> + if (action) {
> + if (peripheral_request_list(ppi0_req_8, DRIVER_NAME)) {
> + printk(KERN_ERR "Requesting Peripherals faild\n");
> + return -EFAULT;
> + }
> + } else
> + peripheral_free_list(ppi0_req_8);
> +
> + return 0;
> +}
> +
> +static int bfin_t350mcqb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
> +{
> + struct bfin_t350mcqbfb_info *fbi = info->par;
> +
> + if (fbi->lq043_mmap)
> + return -1;
> +
> + spin_lock(&fbi->lock);
> + fbi->lq043_mmap = 1;
> + spin_unlock(&fbi->lock);

the locking here probably doesn't do a lot.

> + vma->vm_start = (unsigned long)(fbi->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET);
> +
> + vma->vm_end = vma->vm_start + info->fix.smem_len;
> + /* For those who don't understand how mmap works, go read
> + * Documentation/nommu-mmap.txt.
> + * For those that do, you will know that the VM_MAYSHARE flag
> + * must be set in the vma->vm_flags structure on noMMU
> + * Other flags can be set, and are documented in
> + * include/linux/mm.h
> + */
> + vma->vm_flags |= VM_MAYSHARE;
> +
> + return 0;
> +}
> +
> +
> +static int __init bfin_t350mcqb_probe(struct platform_device *pdev)
> +{
> + struct bfin_t350mcqbfb_info *info;
> + struct fb_info *fbinfo;
> + int ret;
> +
> + printk(KERN_INFO DRIVER_NAME ": %dx%d %d-bit RGB FrameBuffer initializing...\n",
> + LCD_X_RES, LCD_Y_RES, LCD_BPP);
> +
> + if (request_dma(CH_PPI, "CH_PPI") < 0) {
> + printk(KERN_ERR DRIVER_NAME
> + ": couldn't request CH_PPI DMA\n");
> + ret = -EFAULT;
> + goto out1;
> + }
> +
> + fbinfo =
> + framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev);
> + if (!fbinfo) {
> + ret = -ENOMEM;
> + goto out2;
> + }
> +
> + info = fbinfo->par;
> + info->fb = fbinfo;
> + info->dev = &pdev->dev;
> +
> + platform_set_drvdata(pdev, fbinfo);
> +
> + strcpy(fbinfo->fix.id, driver_name);
> +
> + fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
> + fbinfo->fix.type_aux = 0;
> + fbinfo->fix.xpanstep = 0;
> + fbinfo->fix.ypanstep = 0;
> + fbinfo->fix.ywrapstep = 0;
> + fbinfo->fix.accel = FB_ACCEL_NONE;
> + fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
> +
> + fbinfo->var.nonstd = 0;
> + fbinfo->var.activate = FB_ACTIVATE_NOW;
> + fbinfo->var.height = -1;
> + fbinfo->var.width = -1;
> + fbinfo->var.accel_flags = 0;
> + fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
> +
> + fbinfo->var.xres = LCD_X_RES;
> + fbinfo->var.xres_virtual = LCD_X_RES;
> + fbinfo->var.yres = LCD_Y_RES;
> + fbinfo->var.yres_virtual = LCD_Y_RES;
> + fbinfo->var.bits_per_pixel = LCD_BPP;
> +
> + fbinfo->var.red.offset = 0;
> + fbinfo->var.green.offset = 8;
> + fbinfo->var.blue.offset = 16;
> + fbinfo->var.transp.offset = 0;
> + fbinfo->var.red.length = 8;
> + fbinfo->var.green.length = 8;
> + fbinfo->var.blue.length = 8;
> + fbinfo->var.transp.length = 0;
> + fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8;
> +
> + fbinfo->fix.line_length = fbinfo->var.xres_virtual *
> + fbinfo->var.bits_per_pixel / 8;
> +
> +
> + fbinfo->fbops = &bfin_t350mcqb_fb_ops;
> + fbinfo->flags = FBINFO_FLAG_DEFAULT;
> +
> + info->fb_buffer =
> + dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
> + GFP_KERNEL);
> +
> + if (NULL == info->fb_buffer) {
> + printk(KERN_ERR DRIVER_NAME
> + ": couldn't allocate dma buffer.\n");
> + ret = -ENOMEM;
> + goto out3;
> + }
> +
> + memset(info->fb_buffer, 0, fbinfo->fix.smem_len);
> +
> + fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
> + fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
> +
> + fbinfo->fbops = &bfin_t350mcqb_fb_ops;
> +
> + fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
> + if (!fbinfo->pseudo_palette) {
> + printk(KERN_ERR DRIVER_NAME
> + "Fail to allocate pseudo_palette\n");
> +
> + ret = -ENOMEM;
> + goto out4;
> + }
> +
> + memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16);

You just invented kzalloc!

> + if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0)
> + < 0) {
> + printk(KERN_ERR DRIVER_NAME
> + "Fail to allocate colormap (%d entries)\n",
> + BFIN_LCD_NBR_PALETTE_ENTRIES);
> + ret = -EFAULT;
> + goto out5;
> + }
> +
> + if (bfin_t350mcqb_request_ports(1)) {
> + printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
> + ret = -EFAULT;
> + goto out6;
> + }
> +
> + info->irq = platform_get_irq(pdev, 0);
> + if (info->irq < 0) {
> + ret = -EINVAL;
> + goto out7;
> + }
> +
> + if (request_irq(info->irq, (void *)bfin_t350mcqb_irq_error, IRQF_DISABLED,
> + "PPI ERROR", info) < 0) {
> + printk(KERN_ERR DRIVER_NAME
> + ": unable to request PPI ERROR IRQ\n");
> + ret = -EFAULT;

EFAULT?

> + goto out7;
> + }
> +
> + if (register_framebuffer(fbinfo) < 0) {
> + printk(KERN_ERR DRIVER_NAME
> + ": unable to register framebuffer.\n");
> + ret = -EINVAL;
> + goto out8;
> + }
> +#ifndef NO_BL_SUPPORT
> + bl_dev =
> + backlight_device_register("bf52x-bl", NULL, NULL,
> + &bfin_lq043fb_bl_ops);
> + bl_dev->props.max_brightness = 255;
> +
> + lcd_dev = lcd_device_register(DRIVER_NAME, NULL, &bfin_lcd_ops);
> + lcd_dev->props.max_contrast = 255, printk(KERN_INFO "Done.\n");
> +#endif
> +
> + return 0;
> +
> +out8:
> + free_irq(info->irq, info);
> +out7:
> + bfin_t350mcqb_request_ports(0);
> +out6:
> + fb_dealloc_cmap(&fbinfo->cmap);
> +out5:
> + kfree(fbinfo->pseudo_palette);
> +out4:
> + dma_free_coherent(NULL, fbinfo->fix.smem_len, info->fb_buffer,
> + info->dma_handle);
> +out3:
> + framebuffer_release(fbinfo);
> +out2:
> + free_dma(CH_PPI);
> +out1:
> + platform_set_drvdata(pdev, NULL);
> +
> + return ret;
> +}
> +

2008-03-06 16:51:18

by Bryan Wu

[permalink] [raw]
Subject: Re: [Linux-fbdev-devel] [PATCH 1/1 try#2] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

On Wed, Mar 5, 2008 at 11:02 PM, Andrew Morton
<[email protected]> wrote:
> On Thu, 31 Jan 2008 00:49:43 +0800 Bryan Wu <[email protected]> wrote:
> >
>
> bah, January.
>
> I'm trolling the fbdev list only now - I guess I'll slip this one into
> 2.6.25.
>

You finally caught this, -;)

>
> > +static int bfin_t350mcqb_request_ports(int action)
> > +{
> > + u16 ppi0_req_8[] = {P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2,
> > + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2,
> > + P_PPI0_D3, P_PPI0_D4, P_PPI0_D5,
> > + P_PPI0_D6, P_PPI0_D7, 0};
>
> This array will be assembled on the stack at runtime. If
> peripheral_request_list() doesn't alter it then it would be better to
> create it at compile-time by adding `static'.
>

Exactly, I will do it.

>
> > + if (action) {
> > + if (peripheral_request_list(ppi0_req_8, DRIVER_NAME)) {
> > + printk(KERN_ERR "Requesting Peripherals faild\n");
> > + return -EFAULT;
> > + }
> > + } else
> > + peripheral_free_list(ppi0_req_8);
> > +
> > + return 0;
> > +}
> > +
>
> > +static int bfin_t350mcqb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
> > +{
> > + struct bfin_t350mcqbfb_info *fbi = info->par;
> > +
> > + if (fbi->lq043_mmap)
> > + return -1;
> > +
> > + spin_lock(&fbi->lock);
> > + fbi->lq043_mmap = 1;
> > + spin_unlock(&fbi->lock);
>
> the locking here probably doesn't do a lot.
>

Actually, this locking is necessary here for trying to mmap by two
application, IMO.

>
> > + vma->vm_start = (unsigned long)(fbi->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET);
> > +
> > + vma->vm_end = vma->vm_start + info->fix.smem_len;
> > + /* For those who don't understand how mmap works, go read
> > + * Documentation/nommu-mmap.txt.
> > + * For those that do, you will know that the VM_MAYSHARE flag
> > + * must be set in the vma->vm_flags structure on noMMU
> > + * Other flags can be set, and are documented in
> > + * include/linux/mm.h
> > + */
> > + vma->vm_flags |= VM_MAYSHARE;
> > +
> > + return 0;
> > +}
> > +
> > +
>
>
> > +static int __init bfin_t350mcqb_probe(struct platform_device *pdev)
> > +{
> > + struct bfin_t350mcqbfb_info *info;
> > + struct fb_info *fbinfo;
> > + int ret;
> > +
> > + printk(KERN_INFO DRIVER_NAME ": %dx%d %d-bit RGB FrameBuffer initializing...\n",
> > + LCD_X_RES, LCD_Y_RES, LCD_BPP);
> > +
> > + if (request_dma(CH_PPI, "CH_PPI") < 0) {
> > + printk(KERN_ERR DRIVER_NAME
> > + ": couldn't request CH_PPI DMA\n");
> > + ret = -EFAULT;
> > + goto out1;
> > + }
> > +
> > + fbinfo =
> > + framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev);
> > + if (!fbinfo) {
> > + ret = -ENOMEM;
> > + goto out2;
> > + }
> > +
> > + info = fbinfo->par;
> > + info->fb = fbinfo;
> > + info->dev = &pdev->dev;
> > +
> > + platform_set_drvdata(pdev, fbinfo);
> > +
> > + strcpy(fbinfo->fix.id, driver_name);
> > +
> > + fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
> > + fbinfo->fix.type_aux = 0;
> > + fbinfo->fix.xpanstep = 0;
> > + fbinfo->fix.ypanstep = 0;
> > + fbinfo->fix.ywrapstep = 0;
> > + fbinfo->fix.accel = FB_ACCEL_NONE;
> > + fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
> > +
> > + fbinfo->var.nonstd = 0;
> > + fbinfo->var.activate = FB_ACTIVATE_NOW;
> > + fbinfo->var.height = -1;
> > + fbinfo->var.width = -1;
> > + fbinfo->var.accel_flags = 0;
> > + fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
> > +
> > + fbinfo->var.xres = LCD_X_RES;
> > + fbinfo->var.xres_virtual = LCD_X_RES;
> > + fbinfo->var.yres = LCD_Y_RES;
> > + fbinfo->var.yres_virtual = LCD_Y_RES;
> > + fbinfo->var.bits_per_pixel = LCD_BPP;
> > +
> > + fbinfo->var.red.offset = 0;
> > + fbinfo->var.green.offset = 8;
> > + fbinfo->var.blue.offset = 16;
> > + fbinfo->var.transp.offset = 0;
> > + fbinfo->var.red.length = 8;
> > + fbinfo->var.green.length = 8;
> > + fbinfo->var.blue.length = 8;
> > + fbinfo->var.transp.length = 0;
> > + fbinfo->fix.smem_len = LCD_X_RES * LCD_Y_RES * LCD_BPP / 8;
> > +
> > + fbinfo->fix.line_length = fbinfo->var.xres_virtual *
> > + fbinfo->var.bits_per_pixel / 8;
> > +
> > +
> > + fbinfo->fbops = &bfin_t350mcqb_fb_ops;
> > + fbinfo->flags = FBINFO_FLAG_DEFAULT;
> > +
> > + info->fb_buffer =
> > + dma_alloc_coherent(NULL, fbinfo->fix.smem_len, &info->dma_handle,
> > + GFP_KERNEL);
> > +
> > + if (NULL == info->fb_buffer) {
> > + printk(KERN_ERR DRIVER_NAME
> > + ": couldn't allocate dma buffer.\n");
> > + ret = -ENOMEM;
> > + goto out3;
> > + }
> > +
> > + memset(info->fb_buffer, 0, fbinfo->fix.smem_len);
> > +
> > + fbinfo->screen_base = (void *)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
> > + fbinfo->fix.smem_start = (int)info->fb_buffer + ACTIVE_VIDEO_MEM_OFFSET;
> > +
> > + fbinfo->fbops = &bfin_t350mcqb_fb_ops;
> > +
> > + fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
> > + if (!fbinfo->pseudo_palette) {
> > + printk(KERN_ERR DRIVER_NAME
> > + "Fail to allocate pseudo_palette\n");
> > +
> > + ret = -ENOMEM;
> > + goto out4;
> > + }
> > +
> > + memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16);
>
> You just invented kzalloc!
>

Right

>
>
> > + if (fb_alloc_cmap(&fbinfo->cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0)
> > + < 0) {
> > + printk(KERN_ERR DRIVER_NAME
> > + "Fail to allocate colormap (%d entries)\n",
> > + BFIN_LCD_NBR_PALETTE_ENTRIES);
> > + ret = -EFAULT;
> > + goto out5;
> > + }
> > +
> > + if (bfin_t350mcqb_request_ports(1)) {
> > + printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
> > + ret = -EFAULT;
> > + goto out6;
> > + }
> > +
> > + info->irq = platform_get_irq(pdev, 0);
> > + if (info->irq < 0) {
> > + ret = -EINVAL;
> > + goto out7;
> > + }
> > +
> > + if (request_irq(info->irq, (void *)bfin_t350mcqb_irq_error, IRQF_DISABLED,
> > + "PPI ERROR", info) < 0) {
> > + printk(KERN_ERR DRIVER_NAME
> > + ": unable to request PPI ERROR IRQ\n");
> > + ret = -EFAULT;
>
> EFAULT?
>

It should return the request_irq return value and other similar
function calls should be checked like this,
right? Michael.

-Bryan

2008-03-06 18:08:17

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [Linux-fbdev-devel] [PATCH 1/1 try#2] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver

On Thu, 6 Mar 2008, Bryan Wu wrote:
> On Wed, Mar 5, 2008 at 11:02 PM, Andrew Morton
> <[email protected]> wrote:
> > On Thu, 31 Jan 2008 00:49:43 +0800 Bryan Wu <[email protected]> wrote:
> > > + fbinfo =
> > > + framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info), &pdev->dev);
> > > + if (!fbinfo) {
> > > + ret = -ENOMEM;
> > > + goto out2;
> > > + }

[...]

> > > + fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
> > > + if (!fbinfo->pseudo_palette) {
> > > + printk(KERN_ERR DRIVER_NAME
> > > + "Fail to allocate pseudo_palette\n");
> > > +
> > > + ret = -ENOMEM;
> > > + goto out4;
> > > + }
> > > +
> > > + memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16);
> >
> > You just invented kzalloc!
> >
>
> Right

Alternatively, you could embed the pseudo palette inside struct
bfin_t350mcqbfb_info, so it's allocated by the framebuffer_alloc() call
above.

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2008-03-10 14:36:32

by Hennerich, Michael

[permalink] [raw]
Subject: RE: [Linux-fbdev-devel] [PATCH 1/1 try#2] [VIDEO/FRAMEBUFFER]: add BF52x EZkit Display driver



>From: [email protected] [mailto:[email protected]]
>Subject: Re: [Linux-fbdev-devel] [PATCH 1/1 try#2] [VIDEO/FRAMEBUFFER]:
add
>BF52x EZkit Display driver
>
>On Thu, 6 Mar 2008, Bryan Wu wrote:
>> On Wed, Mar 5, 2008 at 11:02 PM, Andrew Morton
>> <[email protected]> wrote:
>> > On Thu, 31 Jan 2008 00:49:43 +0800 Bryan Wu <[email protected]>
>wrote:
>> > > + fbinfo =
>> > > + framebuffer_alloc(sizeof(struct bfin_t350mcqbfb_info),
>&pdev->dev);
>> > > + if (!fbinfo) {
>> > > + ret = -ENOMEM;
>> > > + goto out2;
>> > > + }
>
> [...]
>
>> > > + fbinfo->pseudo_palette = kmalloc(sizeof(u32) * 16,
>GFP_KERNEL);
>> > > + if (!fbinfo->pseudo_palette) {
>> > > + printk(KERN_ERR DRIVER_NAME
>> > > + "Fail to allocate pseudo_palette\n");
>> > > +
>> > > + ret = -ENOMEM;
>> > > + goto out4;
>> > > + }
>> > > +
>> > > + memset(fbinfo->pseudo_palette, 0, sizeof(u32) * 16);
>> >
>> > You just invented kzalloc!
>> >
>>
>> Right
>
>Alternatively, you could embed the pseudo palette inside struct
>bfin_t350mcqbfb_info, so it's allocated by the framebuffer_alloc() call
>above.

This makes the most sense.
Bryan is going to send an updated patch, including your and Andrew's
recommendations.

Thanks and best regards,
Michael


>
>Gr{oetje,eeting}s,
>
> Geert
>
>--
>Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-
>m68k.org
>
>In personal conversations with technical people, I call myself a
hacker.
>But
>when I'm talking to journalists I just say "programmer" or something
like
>that.
> -- Linus
Torvalds