summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2009-07-31 15:07:31 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-07-31 15:08:21 +0200
commit79baaa503f732c421f1168b57e8241b863e8af94 (patch)
tree6de0af9b28330e6fb975a50afabc494638c9e46a /drivers
parent915aa03c77e7478d59b229df894685c304b9aaab (diff)
downloadbarebox-79baaa503f732c421f1168b57e8241b863e8af94.tar.gz
barebox-79baaa503f732c421f1168b57e8241b863e8af94.tar.xz
Add framebuffer support
This patch adds framebuffer support and a driver for i.MX[12] framebuffer devices. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/video/Kconfig11
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/fb.c86
-rw-r--r--drivers/video/imx.c415
6 files changed, 517 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 72e25cf236..eb3a587ec9 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -6,5 +6,6 @@ source "drivers/spi/Kconfig"
source "drivers/nor/Kconfig"
source "drivers/nand/Kconfig"
source "drivers/usb/Kconfig"
+source "drivers/video/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 6ff0b4de87..518060a941 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -4,3 +4,4 @@ obj-y += nand/
obj-y += nor/
obj-y += usb/
obj-$(CONFIG_SPI) += spi/
+obj-$(CONFIG_VIDEO) += video/
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
new file mode 100644
index 0000000000..409b5f5787
--- /dev/null
+++ b/drivers/video/Kconfig
@@ -0,0 +1,11 @@
+menu "Video drivers "
+
+config VIDEO
+ bool "Enable Framebuffersupport"
+
+config DRIVER_VIDEO_IMX
+ bool "i.MX framebuffer driver"
+ depends on ARCH_IMX
+ depends on VIDEO
+
+endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
new file mode 100644
index 0000000000..c7971af932
--- /dev/null
+++ b/drivers/video/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO) += fb.o
+obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o
+
diff --git a/drivers/video/fb.c b/drivers/video/fb.c
new file mode 100644
index 0000000000..00a0f6a177
--- /dev/null
+++ b/drivers/video/fb.c
@@ -0,0 +1,86 @@
+#include <common.h>
+#include <fb.h>
+#include <errno.h>
+#include <command.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <fs.h>
+
+static int fb_ioctl(struct cdev* cdev, int req, void *data)
+{
+ struct fb_info *info = cdev->priv;
+
+ switch (req) {
+ case FBIOGET_SCREENINFO:
+ memcpy(data, info, sizeof(*info));
+ break;
+ case FBIO_ENABLE:
+ info->fbops->fb_enable(info);
+ break;
+ case FBIO_DISABLE:
+ info->fbops->fb_disable(info);
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static int fb_enable_set(struct device_d *dev, struct param_d *param,
+ const char *val)
+{
+ struct fb_info *info = dev->priv;
+ int enable;
+
+ enable = simple_strtoul(val, NULL, 0);
+
+ if (enable)
+ info->fbops->fb_enable(info);
+ else
+ info->fbops->fb_disable(info);
+
+ sprintf(info->enable_string, "%d", !!enable);
+
+ return 0;
+}
+
+static struct file_operations fb_ops = {
+ .read = mem_read,
+ .write = mem_write,
+ .memmap = generic_memmap_rw,
+ .lseek = dev_lseek_default,
+ .ioctl = fb_ioctl,
+};
+
+int register_framebuffer(struct fb_info *info)
+{
+ int id = get_free_deviceid("fb");
+ struct device_d *dev;
+
+ info->cdev.ops = &fb_ops;
+ info->cdev.name = asprintf("fb%d", id);
+ info->cdev.size = info->xres * info->yres * (info->bits_per_pixel >> 3);
+ info->cdev.dev = &info->dev;
+ info->cdev.priv = info;
+ info->cdev.dev->map_base = (unsigned long)info->screen_base;
+ info->cdev.dev->size = info->cdev.size;
+
+ dev = &info->dev;
+ dev->priv = info;
+
+ sprintf(dev->name, "fb");
+
+ info->param_enable.set = fb_enable_set;
+ info->param_enable.name = "enable";
+ sprintf(info->enable_string, "%d", 0);
+ info->param_enable.value = info->enable_string;
+ dev_add_param(dev, &info->param_enable);
+
+ register_device(&info->dev);
+
+ devfs_create(&info->cdev);
+
+ return 0;
+}
+
diff --git a/drivers/video/imx.c b/drivers/video/imx.c
new file mode 100644
index 0000000000..547a8774d8
--- /dev/null
+++ b/drivers/video/imx.c
@@ -0,0 +1,415 @@
+/*
+ * Freescale i.MX Frame Buffer device driver
+ *
+ * Copyright (C) 2004 Sascha Hauer, Pengutronix
+ * Based on acornfb.c Copyright (C) Russell King.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Please direct your questions and comments on this driver to the following
+ * email address:
+ *
+ * linux-arm-kernel@lists.arm.linux.org.uk
+ */
+
+#include <common.h>
+#include <fb.h>
+#include <asm/io.h>
+#include <asm/arch/imxfb.h>
+#include <driver.h>
+#include <malloc.h>
+#include <errno.h>
+#include <init.h>
+#include <asm/arch/imx-regs.h>
+#include <asm-generic/div64.h>
+#include <asm/arch/clock.h>
+
+#define LCDC_SSA 0x00
+
+#define LCDC_SIZE 0x04
+#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20)
+
+#ifdef CONFIG_ARCH_IMX1
+#define SIZE_YMAX(y) ((y) & 0x1ff)
+#else
+#define SIZE_YMAX(y) ((y) & 0x3ff)
+#endif
+
+#define LCDC_VPW 0x08
+#define VPW_VPW(x) ((x) & 0x3ff)
+
+#define LCDC_CPOS 0x0C
+#define CPOS_CC1 (1<<31)
+#define CPOS_CC0 (1<<30)
+#define CPOS_OP (1<<28)
+#define CPOS_CXP(x) (((x) & 3ff) << 16)
+
+#ifdef CONFIG_ARCH_IMX1
+#define CPOS_CYP(y) ((y) & 0x1ff)
+#else
+#define CPOS_CYP(y) ((y) & 0x3ff)
+#endif
+
+#define LCDC_LCWHB 0x10
+#define LCWHB_BK_EN (1<<31)
+#define LCWHB_CW(w) (((w) & 0x1f) << 24)
+#define LCWHB_CH(h) (((h) & 0x1f) << 16)
+#define LCWHB_BD(x) ((x) & 0xff)
+
+#define LCDC_LCHCC 0x14
+
+#ifdef CONFIG_ARCH_IMX1
+#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
+#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
+#define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
+#else
+#define LCHCC_CUR_COL_R(r) (((r) & 0x3f) << 12)
+#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 6)
+#define LCHCC_CUR_COL_B(b) ((b) & 0x3f)
+#endif
+
+#define LCDC_PCR 0x18
+
+#define LCDC_HCR 0x1C
+#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26)
+#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8)
+#define HCR_H_WAIT_2(x) ((x) & 0xff)
+
+#define LCDC_VCR 0x20
+#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26)
+#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8)
+#define VCR_V_WAIT_2(x) ((x) & 0xff)
+
+#define LCDC_POS 0x24
+#define POS_POS(x) ((x) & 1f)
+
+#define LCDC_LSCR1 0x28
+/* bit fields in imxfb.h */
+
+#define LCDC_PWMR 0x2C
+/* bit fields in imxfb.h */
+
+#define LCDC_DMACR 0x30
+/* bit fields in imxfb.h */
+
+#define LCDC_RMCR 0x34
+
+#ifdef CONFIG_ARCH_IMX1
+#define RMCR_LCDC_EN (1<<1)
+#else
+#define RMCR_LCDC_EN 0
+#endif
+
+#define RMCR_SELF_REF (1<<0)
+
+#define LCDC_LCDICR 0x38
+#define LCDICR_INT_SYN (1<<2)
+#define LCDICR_INT_CON (1)
+
+#define LCDC_LCDISR 0x40
+#define LCDISR_UDR_ERR (1<<3)
+#define LCDISR_ERR_RES (1<<2)
+#define LCDISR_EOF (1<<1)
+#define LCDISR_BOF (1<<0)
+
+/*
+ * These are the bitfields for each
+ * display depth that we support.
+ */
+struct imxfb_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+struct imxfb_info {
+ void __iomem *regs;
+
+ u_int pcr;
+ u_int pwmr;
+ u_int lscr1;
+ u_int dmacr;
+ u_int cmap_inverse:1,
+ cmap_static:1,
+ unused:30;
+
+ struct imx_fb_videomode *mode;
+
+ struct fb_info info;
+};
+
+#define IMX_NAME "IMX"
+
+/*
+ * Minimum X and Y resolutions
+ */
+#define MIN_XRES 64
+#define MIN_YRES 64
+
+/* Actually this really is 18bit support, the lowest 2 bits of each colour
+ * are unused in hardware. We claim to have 24bit support to make software
+ * like X work, which does not support 18bit.
+ */
+static struct imxfb_rgb def_rgb_18 = {
+ .red = {.offset = 16, .length = 8,},
+ .green = {.offset = 8, .length = 8,},
+ .blue = {.offset = 0, .length = 8,},
+ .transp = {.offset = 0, .length = 0,},
+};
+
+static struct imxfb_rgb def_rgb_16_tft = {
+ .red = {.offset = 11, .length = 5,},
+ .green = {.offset = 5, .length = 6,},
+ .blue = {.offset = 0, .length = 5,},
+ .transp = {.offset = 0, .length = 0,},
+};
+
+static struct imxfb_rgb def_rgb_16_stn = {
+ .red = {.offset = 8, .length = 4,},
+ .green = {.offset = 4, .length = 4,},
+ .blue = {.offset = 0, .length = 4,},
+ .transp = {.offset = 0, .length = 0,},
+};
+
+static struct imxfb_rgb def_rgb_8 = {
+ .red = {.offset = 0, .length = 8,},
+ .green = {.offset = 0, .length = 8,},
+ .blue = {.offset = 0, .length = 8,},
+ .transp = {.offset = 0, .length = 0,},
+};
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+
+static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *info)
+{
+ struct imxfb_info *fbi = info->priv;
+ int ret = 1;
+ u32 val;
+
+ /*
+ * If inverse mode was selected, invert all the colours
+ * rather than the register number. The register number
+ * is what you poke into the framebuffer to produce the
+ * colour you requested.
+ */
+ if (fbi->cmap_inverse) {
+ red = 0xffff - red;
+ green = 0xffff - green;
+ blue = 0xffff - blue;
+ }
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (info->grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+
+#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ if (regno < 256) {
+ val = (CNVT_TOHW(red, 4) << 8) |
+ (CNVT_TOHW(green,4) << 4) |
+ CNVT_TOHW(blue, 4);
+
+ writel(val, fbi->regs + 0x800 + (regno << 2));
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void imxfb_enable_controller(struct fb_info *info)
+{
+ struct imxfb_info *fbi = info->priv;
+
+ writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR);
+#ifdef CONFIG_ARCH_IMX27
+ PCCR0 |= PCCR0_LCDC_EN;
+ PCCR1 |= PCCR1_HCLK_LCDC;
+#endif
+
+}
+
+static void imxfb_disable_controller(struct fb_info *info)
+{
+ struct imxfb_info *fbi = info->priv;
+
+ writel(0, fbi->regs + LCDC_RMCR);
+#ifdef CONFIG_ARCH_IMX27
+ PCCR0 &= ~PCCR0_LCDC_EN;
+ PCCR1 &= ~PCCR1_HCLK_LCDC;
+#endif
+}
+
+/*
+ * imxfb_activate_var():
+ * Configures LCD Controller based on entries in var parameter. Settings are
+ * only written to the controller if changes were made.
+ */
+static int imxfb_activate_var(struct fb_info *info)
+{
+ struct fb_videomode *mode = info->mode;
+ struct imxfb_rgb *rgb;
+ unsigned long lcd_clk;
+ unsigned long long tmp;
+ struct imxfb_info *fbi = info->priv;
+ u32 pcr;
+
+ /* physical screen start address */
+ writel(VPW_VPW(mode->xres * info->bits_per_pixel / 8 / 4),
+ fbi->regs + LCDC_VPW);
+
+ writel(HCR_H_WIDTH(mode->hsync_len - 1) |
+ HCR_H_WAIT_1(mode->right_margin - 1) |
+ HCR_H_WAIT_2(mode->left_margin - 3),
+ fbi->regs + LCDC_HCR);
+
+ writel(VCR_V_WIDTH(mode->vsync_len) |
+ VCR_V_WAIT_1(mode->lower_margin) |
+ VCR_V_WAIT_2(mode->upper_margin),
+ fbi->regs + LCDC_VCR);
+
+ writel(SIZE_XMAX(info->xres) | SIZE_YMAX(info->yres),
+ fbi->regs + LCDC_SIZE);
+
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+ writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
+ writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
+ writel((unsigned long)fbi->info.screen_base, fbi->regs + LCDC_SSA);
+
+ /* panning offset 0 (0 pixel offset) */
+ writel(0x0, fbi->regs + LCDC_POS);
+
+ /* disable hardware cursor */
+ writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
+ fbi->regs + LCDC_CPOS);
+
+ lcd_clk = imx_get_lcdclk();
+
+ tmp = mode->pixclock * (unsigned long long)lcd_clk;
+
+ do_div(tmp, 1000000);
+
+ if (do_div(tmp, 1000000) > 500000)
+ tmp++;
+
+ pcr = (unsigned int)tmp;
+ if (--pcr > 0x3f) {
+ pcr = 0x3f;
+ printk(KERN_WARNING "Must limit pixel clock to %luHz\n",
+ lcd_clk / pcr);
+ }
+
+ switch (info->bits_per_pixel) {
+ case 32:
+ pcr |= PCR_BPIX_18;
+ rgb = &def_rgb_18;
+ break;
+ case 16:
+ default:
+#ifdef CONFIG_ARCH_IMX1
+ pcr |= PCR_BPIX_12;
+#else
+ pcr |= PCR_BPIX_16;
+#endif
+ if (fbi->pcr & PCR_TFT)
+ rgb = &def_rgb_16_tft;
+ else
+ rgb = &def_rgb_16_stn;
+ break;
+ case 8:
+ pcr |= PCR_BPIX_8;
+ rgb = &def_rgb_8;
+ break;
+ }
+
+ writel(fbi->pcr | pcr, fbi->regs + LCDC_PCR);
+
+ /*
+ * Copy the RGB parameters for this display
+ * from the machine specific parameters.
+ */
+ info->red = rgb->red;
+ info->green = rgb->green;
+ info->blue = rgb->blue;
+ info->transp = rgb->transp;
+
+ return 0;
+}
+
+static struct fb_ops imxfb_ops = {
+ .fb_setcolreg = imxfb_setcolreg,
+ .fb_enable = imxfb_enable_controller,
+ .fb_disable = imxfb_disable_controller,
+};
+
+static int imxfb_probe(struct device_d *dev)
+{
+ struct imxfb_info *fbi;
+ struct fb_info *info;
+ struct imx_fb_platform_data *pdata = dev->platform_data;
+ int ret;
+
+ if (!pdata)
+ return -ENODEV;
+
+ fbi = xzalloc(sizeof(*fbi));
+ info = &fbi->info;
+
+ fbi->mode = pdata->mode;
+ fbi->regs = (void *)dev->map_base;
+ fbi->pcr = pdata->mode->pcr;
+ fbi->pwmr = pdata->pwmr;
+ fbi->lscr1 = pdata->lscr1;
+ fbi->dmacr = pdata->dmacr;
+ info->priv = fbi;
+ info->mode = &pdata->mode->mode;
+ info->xres = pdata->mode->mode.xres;
+ info->yres = pdata->mode->mode.yres;
+ info->bits_per_pixel = pdata->mode->bpp;
+ info->fbops = &imxfb_ops;
+
+ dev_info(dev, "i.MX Framebuffer driver\n");
+
+ fbi->info.screen_base = xzalloc(info->xres * info->yres *
+ (info->bits_per_pixel >> 3));
+
+ imxfb_activate_var(&fbi->info);
+
+ ret = register_framebuffer(&fbi->info);
+ if (ret < 0) {
+ dev_err(dev, "failed to register framebuffer\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void imxfb_remove(struct device_d *dev)
+{
+}
+
+static struct driver_d imxfb_driver = {
+ .name = "imxfb",
+ .probe = imxfb_probe,
+ .remove = imxfb_remove,
+};
+
+static int imxfb_init(void)
+{
+ return register_driver(&imxfb_driver);
+}
+
+device_initcall(imxfb_init);
+