* [PATCH 1/1] [Video/Framebuffer]: Hitachi TX09D70VM1CDA TFT LCD framebuffer driver
@ 2008-05-12 10:49 Bryan Wu
2008-05-12 11:18 ` Paul Mundt
0 siblings, 1 reply; 2+ messages in thread
From: Bryan Wu @ 2008-05-12 10:49 UTC (permalink / raw)
To: adaplas, linux-fbdev-devel
Cc: Bryan Wu, Harald Krapfenbauer, linux-kernel, Michael Hennerich
From: Harald Krapfenbauer <harald.krapfenbauer@bluetechnix.at>
Signed-off-by: Harald Krapfenbauer <harald.krapfenbauer@bluetechnix.at>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
---
drivers/video/Kconfig | 36 ++
drivers/video/Makefile | 1 +
drivers/video/hitachi-tx09.c | 794 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 831 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/hitachi-tx09.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index bb1dada..4a56985 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -600,6 +600,42 @@ config FB_BFIN_T350MCQB
It uses PPI[0..7] PPI_FS1, PPI_FS2 and PPI_CLK.
+config FB_HITACHI_TX09
+ tristate "Hitachi TX09D70VM1CDA TFT LCD"
+ depends on FB && (BF537 || BF561)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the framebuffer device for a HITACHI TX09D70VM1CDA
+ TFT LCD attached to a CM-BF537E or CM-BF561 from Bluetechnix.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hitachi-tx09.
+
+config FB_HITACHI_TX09_LANDSCAPE
+ bool "Use Landscape 320x240 instead of Portrait 240x320"
+ depends on FB_HITACHI_TX09
+ default n
+ help
+ Use 320x240 instead of native 240x320 Resolution
+
+config FB_HITACHI_TX09_REFRESHRATE
+ int "Refresh rate"
+ depends on FB_HITACHI_TX09
+ range 25 70
+ default 52
+ help
+ Refresh rate of the LCD in Hz. According to data sheet, must
+ be in the interval [52...68].
+
+config CONFIG_FB_HITACHI_TX09_CURSOR
+ bool "Enable/Disable cursor"
+ depends on FB_HITACHI_TX09
+ default n
+ help
+ Enable the cursor
+
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 04bca35..51fe9ab 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -117,6 +117,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
+obj-$(CONFIG_FB_HITACHI_TX09) += hitachi-tx09.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/hitachi-tx09.c b/drivers/video/hitachi-tx09.c
new file mode 100644
index 0000000..e53de0a
--- /dev/null
+++ b/drivers/video/hitachi-tx09.c
@@ -0,0 +1,794 @@
+/*
+ * drivers/video/hitachi-tx09.c
+ * Date: 2007-11-22
+ *
+ * Framebuffer driver for the Hitachi TX09D70VM1CDA TFT LCD written
+ * by Harald Krapfenbauer <harald.krapfenbauer@bluetechnix.at>
+ *
+ * Thanks to Michael Hennerich from Analog Devices Inc. for his support
+ * and the bf537-lq035.c TFT driver!
+ *
+ * For more information, please read the data sheet:
+ * http://www.hitachi-displays-eu.com/doc/TX09D70VM1CDA.PDF
+ *
+ * This program is free software; you can distribute it and/or modify it
+ * under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/backlight.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#define DRIVER_NAME "hitachi-tx09"
+
+#define MAX_BRIGHTNESS 100
+#define BFIN_LCD_NBR_PALETTE_ENTRIES 256
+
+#undef BITREVERSED /* colors bitreversed? (only needed for older EXT-CAM boards) */
+
+#define PPI0_16 {P_PPI0_CLK, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, \
+ P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, \
+ P_PPI0_D11, P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, 0}
+
+#ifdef CONFIG_BFIN537_BLUETECHNIX_CM
+
+#define TIMER_DCLK 3
+#define TIMER_HSYNC 0
+#define TIMER_DTMG 1
+#define TIMER_VSYNC 6
+#define TIMER_BACKLIGHT 4
+#define PCI_PIN GPIO_PF14
+#define TIMERS {P_TMR0, P_TMR1, P_TMR3, P_TMR4, P_TMR6, 0}
+#define WRITE_PPI_CONTROL(x) bfin_write_PPI_CONTROL(x)
+#define READ_PPI_CONTROL bfin_read_PPI_CONTROL
+#define WRITE_PPI_DELAY(x) bfin_write_PPI_DELAY(x)
+#define WRITE_PPI_COUNT(x) bfin_write_PPI_COUNT(x)
+
+#else
+
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+
+#define TIMER_DCLK 6
+#define TIMER_HSYNC 8
+#define TIMER_DTMG 9
+#define TIMER_VSYNC 1
+#define TIMER_BACKLIGHT 5
+#define PCI_PIN GPIO_PF0
+#define TIMERS {P_TMR1, P_TMR5, P_TMR6, P_TMR8, P_TMR9, 0}
+#define WRITE_PPI_CONTROL(x) bfin_write_PPI0_CONTROL(x)
+#define READ_PPI_CONTROL bfin_read_PPI0_CONTROL
+#define WRITE_PPI_DELAY(x) bfin_write_PPI0_DELAY(x)
+#define WRITE_PPI_COUNT(x) bfin_write_PPI0_COUNT(x)
+
+#else
+#error The Hitachi TX-09 frame buffer driver only supports Bluetechnix CM-BF537E and CM-BF561
+#endif
+
+#endif
+
+#define BFIN_WRITE(a, b) CONCAT(bfin_write_TIMER, a, _, b)
+#define BFIN_READ(a, b) CONCAT(bfin_read_TIMER, a, _, b)
+#define CONCAT(a, b, c, d) a ## b ## c ## d
+
+static unsigned char *fb_buffer; /* RGB Buffer */
+static dma_addr_t dma_handle;
+static unsigned long *dma_desc_table;
+static unsigned long current_brightness; /* backlight */
+static int tx09_open_cnt;
+static int tx09_mmap;
+static struct backlight_device *bl_dev;
+static int t_conf_done;
+static DEFINE_SPINLOCK(tx09_lock);
+
+static void set_backlight(int val)
+{
+ unsigned long timer_period;
+
+ pr_debug("%s to %d\n", __FUNCTION__, val);
+
+ if (val < 0)
+ val = 0;
+ if (val > MAX_BRIGHTNESS)
+ val = MAX_BRIGHTNESS;
+
+ current_brightness = val;
+
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS8_DISABLE(1 << TIMER_BACKLIGHT);
+#else
+ bfin_write_TIMER_DISABLE(1 << TIMER_BACKLIGHT);
+#endif
+ SSYNC();
+
+ timer_period = get_sclk() / 100000;
+ BFIN_WRITE(TIMER_BACKLIGHT, WIDTH) ((timer_period * val) / 100);
+
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS8_ENABLE(1 << TIMER_BACKLIGHT);
+#else
+ bfin_write_TIMER_ENABLE(1 << TIMER_BACKLIGHT);
+#endif
+
+ SSYNC();
+}
+
+static void start_timers(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS4_ENABLE((1 << (TIMER_HSYNC - 8)) |
+ (1 << (TIMER_DTMG - 8)));
+ bfin_write_TMRS8_ENABLE(1 << TIMER_VSYNC);
+ SSYNC();
+ bfin_write_TMRS8_ENABLE(1 << TIMER_DCLK);
+#else
+ bfin_write_TIMER_ENABLE((1 << TIMER_HSYNC) | (1 << TIMER_DTMG) |
+ (1 << TIMER_VSYNC));
+ SSYNC();
+ bfin_write_TIMER_ENABLE(1 << TIMER_DCLK);
+#endif
+ SSYNC();
+ mdelay(50);
+ gpio_set_value(PCI_PIN, 1);
+ SSYNC();
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS8_ENABLE(1 << TIMER_BACKLIGHT);
+#else
+ bfin_write_TIMER_ENABLE(1 << TIMER_BACKLIGHT);
+#endif
+ SSYNC();
+
+ local_irq_restore(flags);
+}
+
+static void stop_timers(void)
+{
+ unsigned long flags;
+ long old_value = 0, new_value = 0;
+
+ local_irq_save(flags);
+
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS8_DISABLE(1 << TIMER_BACKLIGHT);
+#else
+ bfin_write_TIMER_DISABLE(1 << TIMER_BACKLIGHT);
+#endif
+ gpio_set_value(PCI_PIN, 0);
+
+ while (1) {
+ old_value = new_value;
+ new_value = BFIN_READ(TIMER_VSYNC, COUNTER) ();
+ if ((old_value - new_value) > 45000) {
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS8_DISABLE((1 << TIMER_DCLK) |
+ (1 << TIMER_VSYNC));
+ bfin_write_TMRS4_DISABLE((1 << (TIMER_HSYNC - 8)) |
+ (1 << (TIMER_DTMG - 8)));
+#else
+ bfin_write_TIMER_DISABLE((1 << TIMER_VSYNC) |
+ (1 << TIMER_HSYNC) |
+ (1 << TIMER_DTMG) |
+ (1 << TIMER_DCLK));
+#endif
+ break;
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+static void config_timers(void)
+{
+ unsigned long timer_period = 0;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ /* stop timers */
+#ifdef CONFIG_BFIN561_BLUETECHNIX_CM
+ bfin_write_TMRS8_DISABLE((1 << TIMER_DCLK) | (1 << TIMER_VSYNC) |
+ (1 << TIMER_BACKLIGHT));
+ bfin_write_TMRS4_DISABLE((1 << (TIMER_HSYNC - 8)) |
+ (1 << (TIMER_DTMG - 8)));
+#else
+ bfin_write_TIMER_DISABLE((1 << TIMER_VSYNC) | (1 << TIMER_HSYNC) |
+ (1 << TIMER_DTMG) | (1 << TIMER_DCLK) |
+ (1 << TIMER_BACKLIGHT));
+#endif
+ SSYNC();
+
+ /* dclk clock output */
+ BFIN_WRITE(TIMER_DCLK, CONFIG) (PERIOD_CNT | PULSE_HI | PWM_OUT);
+ timer_period =
+ get_sclk() / (CONFIG_FB_HITACHI_TX09_REFRESHRATE * 89271);
+ BFIN_WRITE(TIMER_DCLK, PERIOD) (timer_period);
+ BFIN_WRITE(TIMER_DCLK, WIDTH) (timer_period / 2);
+ SSYNC();
+
+ /* brightness timer */
+ BFIN_WRITE(TIMER_BACKLIGHT, CONFIG) (PERIOD_CNT | PULSE_HI | PWM_OUT);
+ timer_period = get_sclk() / 100000;
+ BFIN_WRITE(TIMER_BACKLIGHT, PERIOD) (timer_period);
+ BFIN_WRITE(TIMER_BACKLIGHT, WIDTH) (timer_period - 1); /* 100% duty cycle */
+ SSYNC();
+
+ /* hsync timer */
+ BFIN_WRITE(TIMER_HSYNC, CONFIG) (CLK_SEL | TIN_SEL | PERIOD_CNT | PWM_OUT); /* clocked by PPI_clk */
+ BFIN_WRITE(TIMER_HSYNC, PERIOD) (273); /* 240 + 33 blanking */
+ BFIN_WRITE(TIMER_HSYNC, WIDTH) (5);
+ SSYNC();
+
+ /* dtmg timer */
+ BFIN_WRITE(TIMER_DTMG, CONFIG) (CLK_SEL | TIN_SEL | PERIOD_CNT | PWM_OUT); /* clocked by PPI_clk */
+ BFIN_WRITE(TIMER_DTMG, PERIOD) (273);
+ BFIN_WRITE(TIMER_DTMG, WIDTH) (33);
+ SSYNC();
+
+ /* vsync timer */
+ BFIN_WRITE(TIMER_VSYNC, CONFIG) (CLK_SEL | TIN_SEL | PERIOD_CNT | PWM_OUT); /* clocked by PPI_clk */
+ BFIN_WRITE(TIMER_VSYNC, PERIOD) (89271);
+ BFIN_WRITE(TIMER_VSYNC, WIDTH) (1911);
+ SSYNC();
+
+ t_conf_done = 1;
+}
+
+static void config_ppi(void)
+{
+ /* configure PPI registers */
+ WRITE_PPI_DELAY(27);
+ WRITE_PPI_COUNT(240 - 1);
+ WRITE_PPI_CONTROL(0x380e);
+}
+
+static int config_dma(void)
+{
+ u32 i = 0;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ /* fill descriptor table */
+ for (i = 0; i < 326; i++) {
+ /* point to next desc table */
+ dma_desc_table[2 * i] =
+ (unsigned long)&dma_desc_table[2 * i + 2];
+ }
+ /* last descriptor points to first */
+ dma_desc_table[2 * 326] = (unsigned long)&dma_desc_table[0];
+
+#ifdef CONFIG_FB_HITACHI_TX09_LANDSCAPE
+
+ dma_desc_table[0 + 1] = (unsigned long)fb_buffer;
+ for (i = 0; i < 7; i++) {
+ /* blanking lines point to first line of fb_buffer */
+ dma_desc_table[2 * i + 1] = (unsigned long)fb_buffer + 319 * 2;
+ }
+ for (i = 7; i < 327; i++) {
+ /* visible lines */
+ dma_desc_table[2 * i + 1] =
+ (unsigned long)fb_buffer + (319 - (i - 7)) * 2;
+ }
+
+#else /* portrait mode */
+
+ for (i = 0; i < 7; i++) {
+ /* blanking lines point to first line of fb_buffer */
+ dma_desc_table[2 * i + 1] = (unsigned long)fb_buffer;
+ }
+ for (i = 7; i < 327; i++) {
+ /* visible lines */
+ dma_desc_table[2 * i + 1] =
+ (unsigned long)fb_buffer + 2 * 240 * (i - 7);
+ }
+
+#endif
+
+#ifdef CONFIG_FB_HITACHI_TX09_LANDSCAPE
+ set_dma_x_count(CH_PPI, 240);
+ set_dma_x_modify(CH_PPI, 2 * 320);
+ set_dma_y_count(CH_PPI, 0);
+ set_dma_y_modify(CH_PPI, 0);
+ set_dma_next_desc_addr(CH_PPI, (unsigned long)dma_desc_table[2 * 326]);
+#else
+ set_dma_x_count(CH_PPI, 240);
+ set_dma_x_modify(CH_PPI, 2);
+ set_dma_y_count(CH_PPI, 0);
+ set_dma_y_modify(CH_PPI, 0);
+ set_dma_next_desc_addr(CH_PPI, (unsigned long)dma_desc_table[2 * 326]);
+#endif
+
+ set_dma_config(CH_PPI, 0x7404);
+
+ return 0;
+}
+
+static int request_ports(int action)
+{
+ u16 ppi_req[] = PPI0_16;
+ u16 tmr_req[] = TIMERS;
+
+ if (action) {
+ if (peripheral_request_list(ppi_req, DRIVER_NAME)) {
+ printk(KERN_ERR DRIVER_NAME
+ ": Requesting Peripherals PPI faild\n");
+ return -EFAULT;
+ }
+
+ if (peripheral_request_list(tmr_req, DRIVER_NAME)) {
+ peripheral_free_list(ppi_req);
+ printk(KERN_ERR DRIVER_NAME
+ ": Requesting Peripherals TMR faild\n");
+ return -EFAULT;
+ }
+
+ if (gpio_request(PCI_PIN, DRIVER_NAME)) {
+ peripheral_free_list(ppi_req);
+ peripheral_free_list(tmr_req);
+ printk(KERN_ERR ": Requesting GPIO %d faild\n", PCI_PIN);
+ return -EFAULT;
+ }
+
+ gpio_direction_output(PCI_PIN, 0);
+ SSYNC();
+ } else {
+ peripheral_free_list(ppi_req);
+ peripheral_free_list(tmr_req);
+
+ gpio_free(PCI_PIN);
+ }
+ return 0;
+}
+
+static struct fb_info tx09_fb;
+
+static struct fb_var_screeninfo tx09_fb_defined = {
+#ifdef CONFIG_FB_HITACHI_TX09_LANDSCAPE
+ .xres = 320,
+ .yres = 240,
+ .xres_virtual = 320,
+ .yres_virtual = 240,
+#else
+ .xres = 240,
+ .yres = 320,
+ .xres_virtual = 240,
+ .yres_virtual = 320,
+#endif
+ .bits_per_pixel = 16,
+ .activate = FB_ACTIVATE_TEST,
+ .height = -1,
+ .width = -1,
+#ifdef BITREVERSED
+ .red = {0, 5, 1}, /* offset, length, msb right */
+ .green = {5, 6, 1},
+ .blue = {11, 5, 1},
+#else
+ .red = {0, 5, 0},
+ .green = {5, 6, 0},
+ .blue = {11, 5, 0},
+#endif
+ .transp = {0, 0, 0},
+ .left_margin = 0,
+ .right_margin = 0,
+ .upper_margin = 0,
+ .lower_margin = 0,
+};
+
+static struct fb_fix_screeninfo tx09_fb_fix = {
+ .id = DRIVER_NAME,
+ .smem_len = 320 * 240 * 2,
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 0,
+ .ypanstep = 0,
+#ifdef CONFIG_FB_HITACHI_TX09_LANDSCAPE
+ .line_length = 320 * 2,
+#else
+ .line_length = 240 * 2,
+#endif
+ .accel = FB_ACCEL_NONE,
+};
+
+static int tx09_fb_open(struct fb_info *info, int user)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ spin_lock_irqsave(&tx09_lock, flags);
+ tx09_open_cnt++; /* increase counter */
+ spin_unlock_irqrestore(&tx09_lock, flags);
+
+ if (tx09_open_cnt <= 1) { /* opened the first time */
+
+ /* stop PPI */
+ WRITE_PPI_CONTROL(0);
+ SSYNC();
+
+ /* configure dma stuff */
+ config_dma();
+
+ config_ppi();
+
+ /* start dma */
+ enable_dma(CH_PPI);
+ SSYNC();
+
+ /* start PPI */
+ WRITE_PPI_CONTROL(READ_PPI_CONTROL() | PORT_EN);
+ SSYNC();
+
+ if (!t_conf_done)
+ config_timers();
+
+ start_timers();
+ }
+
+ return 0;
+}
+
+static int tx09_fb_release(struct fb_info *info, int user)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ spin_lock_irqsave(&tx09_lock, flags);
+ tx09_open_cnt--;
+ tx09_mmap = 0;
+ spin_unlock_irqrestore(&tx09_lock, flags);
+
+ if (tx09_open_cnt <= 0) {
+ stop_timers();
+
+ WRITE_PPI_CONTROL(0);
+ SSYNC();
+
+ disable_dma(CH_PPI);
+ }
+
+ return 0;
+}
+
+static int tx09_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (var->bits_per_pixel != 16) {
+ 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;
+ }
+
+ 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 tx09_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (tx09_mmap)
+ return -1; /* already mmap'ed */
+
+ spin_lock_irqsave(&tx09_lock, flags);
+ tx09_mmap = 1;
+ spin_unlock_irqrestore(&tx09_lock, flags);
+
+ vma->vm_start = (unsigned long)fb_buffer;
+ vma->vm_end = vma->vm_start + 320 * 240 * 2;
+ vma->vm_flags |= VM_MAYSHARE | VM_SHARED;
+
+ return 0;
+}
+
+int tx09_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+
+#ifdef CONFIG_FB_HITACHI_TX09_CURSOR
+ return -EINVAL; /* just to force soft_cursor() call */
+#else
+ return 0;
+#endif
+
+}
+
+static int tx09_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 &= 0xFFFF;
+ ((u32 *) (info->pseudo_palette))[regno] = value;
+ }
+ return 0;
+}
+
+static struct fb_ops tx09_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = tx09_fb_open,
+ .fb_release = tx09_fb_release,
+ .fb_check_var = tx09_fb_check_var,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_mmap = tx09_fb_mmap,
+ .fb_cursor = tx09_fb_cursor,
+ .fb_setcolreg = tx09_fb_setcolreg,
+};
+
+static int bl_update_properties(struct backlight_device *bd)
+{
+ pr_debug("%s\n", __FUNCTION__);
+ set_backlight(bd->props.brightness);
+ return 0;
+}
+
+static int bl_get_brightness(struct backlight_device *bd)
+{
+ pr_debug("%s\n", __FUNCTION__);
+ return current_brightness;
+}
+
+static struct backlight_ops tx09fb_bl_ops = {
+ .get_brightness = bl_get_brightness,
+ .update_status = bl_update_properties,
+};
+
+static int __init tx09_probe(struct platform_device *pdev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ printk(KERN_INFO DRIVER_NAME ": FrameBuffer initializing...\n");
+
+ /* dma channel */
+ if (request_dma(CH_PPI, "BF533_PPI_DMA") < 0) {
+ printk(KERN_ERR DRIVER_NAME ": couldn't request PPI dma.\n");
+ return -EFAULT;
+ }
+
+ /* gpio ports */
+ if (request_ports(1)) {
+ printk(KERN_ERR DRIVER_NAME ": couldn't request gpio port.\n");
+ free_dma(CH_PPI);
+ return -EFAULT;
+ }
+
+ /* frame buffer */
+ fb_buffer = dma_alloc_coherent(NULL, 240 * 320 * 2, &dma_handle, GFP_KERNEL); /* 7 blanking lines, 2 bytes/pixel */
+ if (fb_buffer == NULL) {
+ printk(KERN_ERR DRIVER_NAME
+ ": couldn't allocate dma buffer.\n");
+ free_dma(CH_PPI);
+ request_ports(0);
+ return -ENOMEM;
+ }
+
+ /* dma descriptor list */
+#if L1_DATA_A_LENGTH != 0
+ dma_desc_table =
+ (unsigned long *)l1_data_sram_alloc(sizeof(unsigned long) * 2 *
+ (320 + 7));
+#else
+ dma_desc_table =
+ dma_alloc_coherent(NULL, sizeof(unsigned long) * 2 * (320 + 7),
+ &dma_handle, 0);
+#endif
+
+ if (dma_desc_table == NULL) {
+ printk(KERN_ERR DRIVER_NAME
+ ": couldn't allocate dma descriptor.\n");
+ free_dma(CH_PPI);
+ request_ports(0);
+ dma_free_coherent(NULL, 240 * 320 * 2, fb_buffer, dma_handle);
+ return -ENOMEM;
+ }
+
+ memset(fb_buffer, 0, 240 * 320 * 2);
+
+ tx09_fb.screen_base = (void *)fb_buffer;
+ tx09_fb_fix.smem_start = (int)fb_buffer;
+
+ tx09_fb.fbops = &tx09_fb_ops;
+ tx09_fb.var = tx09_fb_defined;
+
+ tx09_fb.fix = tx09_fb_fix;
+ tx09_fb.flags = FBINFO_DEFAULT;
+
+ /* pseudo palette */
+
+ tx09_fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+
+ if (!tx09_fb.pseudo_palette) {
+ printk(KERN_ERR DRIVER_NAME
+ "Failed to allocate pseudo palette\n");
+ free_dma(CH_PPI);
+ request_ports(0);
+ dma_free_coherent(NULL, 240 * 320 * 2, fb_buffer, dma_handle);
+ return -ENOMEM;
+ }
+ memset(tx09_fb.pseudo_palette, 0, sizeof(u32) * 16);
+
+ /* color map */
+ if (fb_alloc_cmap(&tx09_fb.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ "Failed to allocate colormap (%d entries)\n",
+ BFIN_LCD_NBR_PALETTE_ENTRIES);
+ free_dma(CH_PPI);
+ request_ports(0);
+ dma_free_coherent(NULL, 240 * 320 * 2, fb_buffer, dma_handle);
+ kfree(tx09_fb.pseudo_palette);
+ return -EFAULT;
+ }
+
+ /* register framebuffer */
+ if (register_framebuffer(&tx09_fb) < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": unable to register framebuffer.\n");
+ free_dma(CH_PPI);
+ request_ports(0);
+ dma_free_coherent(NULL, 240 * 320 * 2, fb_buffer, dma_handle);
+ fb_buffer = NULL;
+ return -EINVAL;
+ }
+
+ /* backlight device */
+ bl_dev =
+ backlight_device_register("hitachi-bl", NULL, NULL, &tx09fb_bl_ops);
+ bl_dev->props.max_brightness = MAX_BRIGHTNESS;
+
+ printk(KERN_INFO "Done.\n");
+
+ return 0;
+}
+
+static int tx09_remove(struct platform_device *pdev)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (fb_buffer != NULL)
+ dma_free_coherent(NULL, 240 * 320 * 2, fb_buffer, dma_handle);
+
+#if L1_DATA_A_LENGTH != 0
+ if (dma_desc_table)
+ l1_data_sram_free(dma_desc_table);
+#else
+ if (dma_desc_table)
+ dma_free_coherent(NULL, sizeof(unsigned long) * 2 * (320 + 7),
+ &dma_handle, 0);
+#endif
+
+ stop_timers();
+
+ free_dma(CH_PPI);
+
+ kfree(tx09_fb.pseudo_palette);
+ fb_dealloc_cmap(&tx09_fb.cmap);
+
+ backlight_device_unregister(bl_dev);
+
+ unregister_framebuffer(&tx09_fb);
+
+ request_ports(0);
+
+ printk(KERN_INFO DRIVER_NAME ": Unregistered LCD driver.\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int tx09_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (tx09_open_cnt > 0) {
+ WRITE_PPI_CONTROL(0);
+ SSYNC();
+ disable_dma(CH_PPI);
+ }
+ return 0;
+}
+
+static int tx09_resume(struct platform_device *pdev)
+{
+ if (tx09_open_cnt > 0) {
+ WRITE_PPI_CONTROL(0);
+ SSYNC();
+
+ config_dma();
+ config_ppi();
+
+ enable_dma(CH_PPI);
+ SSYNC();
+ WRITE_PPI_CONTROL(READ_PPI_CONTROL() | PORT_EN);
+ SSYNC();
+ }
+ return 0;
+}
+#else
+#define tx09_suspend NULL
+#define tx09_resume NULL
+#endif
+
+static struct platform_driver tx09_driver = {
+ .probe = tx09_probe,
+ .remove = tx09_remove,
+ .suspend = tx09_suspend,
+ .resume = tx09_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit tx09_driver_init(void)
+{
+ return platform_driver_register(&tx09_driver);
+}
+
+static void __exit tx09_driver_cleanup(void)
+{
+ platform_driver_unregister(&tx09_driver);
+}
+
+MODULE_DESCRIPTION("Hitachi TX09D70VM1CDA TFT LCD Driver");
+MODULE_AUTHOR("Harald Krapfenbauer");
+MODULE_LICENSE("GPL");
+
+module_init(tx09_driver_init);
+module_exit(tx09_driver_cleanup);
--
1.5.5
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH 1/1] [Video/Framebuffer]: Hitachi TX09D70VM1CDA TFT LCD framebuffer driver
2008-05-12 10:49 [PATCH 1/1] [Video/Framebuffer]: Hitachi TX09D70VM1CDA TFT LCD framebuffer driver Bryan Wu
@ 2008-05-12 11:18 ` Paul Mundt
0 siblings, 0 replies; 2+ messages in thread
From: Paul Mundt @ 2008-05-12 11:18 UTC (permalink / raw)
To: Bryan Wu
Cc: Michael Hennerich, Harald Krapfenbauer, linux-fbdev-devel,
linux-kernel, adaplas
On Mon, May 12, 2008 at 06:49:07PM +0800, Bryan Wu wrote:
> +config FB_HITACHI_TX09_REFRESHRATE
> + int "Refresh rate"
> + depends on FB_HITACHI_TX09
> + range 25 70
> + default 52
> + help
> + Refresh rate of the LCD in Hz. According to data sheet, must
> + be in the interval [52...68].
> +
25..70 != 52..68, pick one and stick with it. If there's a reason for the
discrepency, then document that, too. ie, "data sheet has no relation to
hardware".
> +static void stop_timers(void)
> +{
> + unsigned long flags;
> + long old_value = 0, new_value = 0;
> +
> + local_irq_save(flags);
> +
[snip]
> + while (1) {
> + old_value = new_value;
> + new_value = BFIN_READ(TIMER_VSYNC, COUNTER) ();
> + if ((old_value - new_value) > 45000) {
The only way to break out of here is dependent on the timer. You have no
timeout in the case of the timer getting stuck.
> +static int tx09_fb_open(struct fb_info *info, int user)
> +{
> + unsigned long flags;
> +
> + pr_debug("%s\n", __FUNCTION__);
> +
> + spin_lock_irqsave(&tx09_lock, flags);
> + tx09_open_cnt++; /* increase counter */
> + spin_unlock_irqrestore(&tx09_lock, flags);
> +
> + if (tx09_open_cnt <= 1) { /* opened the first time */
> +
> + /* stop PPI */
> + WRITE_PPI_CONTROL(0);
> + SSYNC();
> +
> + /* configure dma stuff */
> + config_dma();
> +
> + config_ppi();
> +
> + /* start dma */
> + enable_dma(CH_PPI);
> + SSYNC();
> +
> + /* start PPI */
> + WRITE_PPI_CONTROL(READ_PPI_CONTROL() | PORT_EN);
> + SSYNC();
> +
> + if (!t_conf_done)
> + config_timers();
> +
> + start_timers();
> + }
> +
Presumably this should be done in the probe path, rather than lazily.
Though you don't bother doing any error checking, so I suppose it doesn't
really matter.
> +static int tx09_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
> +{
> + unsigned long flags;
> +
> + pr_debug("%s\n", __FUNCTION__);
> +
> + if (tx09_mmap)
> + return -1; /* already mmap'ed */
> +
> + spin_lock_irqsave(&tx09_lock, flags);
> + tx09_mmap = 1;
> + spin_unlock_irqrestore(&tx09_lock, flags);
> +
> + vma->vm_start = (unsigned long)fb_buffer;
> + vma->vm_end = vma->vm_start + 320 * 240 * 2;
> + vma->vm_flags |= VM_MAYSHARE | VM_SHARED;
> +
> + return 0;
> +}
> +
This is equally bizarre. If you can never support more than one opener of
this device, then have your open() reflect that, rather than trying to do
these lame hacks in every file op.
Beyond that, the size of the VMA is reflected in info->fix.mmio_len,
which the generic fb_mmap() takes care of for you. Other than the
questionable VMA flags, it's not obvious why you rolled your own mmap.
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2008-05-12 11:19 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-12 10:49 [PATCH 1/1] [Video/Framebuffer]: Hitachi TX09D70VM1CDA TFT LCD framebuffer driver Bryan Wu
2008-05-12 11:18 ` Paul Mundt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).