From caf003fa1d4386755d34f12bb23f264da1496d3b Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Wed, 18 Jan 2012 16:52:04 +0000 Subject: ARM HDLCD: Add support for ARM High Definition LCD. The ARM HDLCD device is now found in various new Versatile Express coretiles. Signed-off-by: Liviu Dudau --- drivers/video/Kconfig | 15 + drivers/video/Makefile | 1 + drivers/video/arm-hdlcd.c | 739 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 755 insertions(+) create mode 100644 drivers/video/arm-hdlcd.c (limited to 'drivers/video') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2e937bdace6..89f8c68c40c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -326,6 +326,21 @@ config FB_ARMCLCD here and read . The module will be called amba-clcd. +config FB_ARMHDLCD + tristate "ARM High Definition LCD support" + depends on FB && ARM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This framebuffer device driver is for the ARM High Definition + Colour LCD controller. + + If you want to compile this as a module (=code which can be + inserted into and removed from the running kernel), say M + here and read . The module + will be called arm-hdlcd. + config FB_ACORN bool "Acorn VIDC support" depends on (FB = y) && ARM && ARCH_ACORN diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e8bae8dd480..82f09484b0d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o +obj-$(CONFIG_FB_ARMHDLCD) += arm-hdlcd.o obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c new file mode 100644 index 00000000000..3e103a5c9b5 --- /dev/null +++ b/drivers/video/arm-hdlcd.c @@ -0,0 +1,739 @@ +/* + * drivers/video/arm-hdlcd.c + * + * Copyright (C) 2011 ARM Limited + * + * 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. + * + * ARM HDLCD Controller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edid.h" + +#ifdef CONFIG_SERIAL_AMBA_PCU_UART +/* set the DVI output mode using the firmware */ +int set_dvi_mode(u8 *msgbuf); +int get_edid(u8 *msgbuf); +#endif + +#define to_hdlcd_device(info) container_of(info, struct hdlcd_device, fb) + +static struct of_device_id hdlcd_of_matches[] = { + { .compatible = "arm,hdlcd" }, + {}, +}; + +/* Framebuffer size. */ +static unsigned long framebuffer_size; + +static char *fb_mode = "1680x1050-32@60\0\0\0\0\0"; + +static struct fb_var_screeninfo cached_var_screeninfo; + +static struct fb_videomode hdlcd_default_mode = { + .refresh = 60, + .xres = 1680, + .yres = 1050, + .pixclock = 8403, + .left_margin = 80, + .right_margin = 48, + .upper_margin = 21, + .lower_margin = 3, + .hsync_len = 32, + .vsync_len = 6, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED +}; + + +static inline void hdlcd_enable(struct hdlcd_device *hdlcd) +{ + dev_dbg(hdlcd->dev, "HDLCD: output enabled\n"); + writel(1, hdlcd->base + HDLCD_REG_COMMAND); +} + +static inline void hdlcd_disable(struct hdlcd_device *hdlcd) +{ + dev_dbg(hdlcd->dev, "HDLCD: output disabled\n"); + writel(0, hdlcd->base + HDLCD_REG_COMMAND); +} + +static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd, + struct fb_var_screeninfo *var) +{ + int ret = 0; + + memset(&var->transp, 0, sizeof(var->transp)); + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->blue.offset = 0; + + switch (var->bits_per_pixel) { + case 8: + /* pseudocolor */ + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + break; + case 16: + /* 565 format */ + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + break; + case 32: + var->transp.length = 8; + case 24: + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + var->green.offset = var->blue.length; + var->red.offset = var->green.offset + var->green.length; + if (var->bits_per_pixel == 32) + var->transp.offset = var->red.offset + var->red.length; + } + + return ret; +} + +static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct hdlcd_device *hdlcd = to_hdlcd_device(info); + int bytes_per_pixel = var->bits_per_pixel / 8; + + var->yres_virtual = 2 * var->yres; + + if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len) + return -ENOMEM; + + if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES) + return -EINVAL; + + /* make sure the bitfields are set appropriately */ + return hdlcd_set_bitfields(hdlcd, var); +} + +#ifdef CONFIG_SERIAL_AMBA_PCU_UART +static int hdlcd_set_output_mode(int xres, int yres) +{ + /* firmware uses some stupid protocol: 5 bytes (only byte 1 used) + to send 3 bits of information (value between 0 - 5) */ + u8 msgbuffer[5]; + + memset(msgbuffer, 0, sizeof(msgbuffer)); + /* default resolution: 640 x 480 */ + if (xres == 800 && yres <= 600) + msgbuffer[0] = 1; /* SVGA: 800 * 600 */ + else if (xres == 1024 && yres <= 768) + msgbuffer[0] = 2; /* XGA: 1024 * 768 */ + else if (xres == 1280 && yres <= 1024) + msgbuffer[0] = 3; /* SXGA: 1280 * 1024 */ + else if (xres == 1600 && yres <= 1200) + msgbuffer[0] = 4; /* UXGA: 1600 * 1200 */ + else if (xres == 1920 && yres <= 1200) + msgbuffer[0] = 5; /* WUXGA: 1920 * 1200 */ + + return set_dvi_mode(msgbuffer); +} +#else +inline int hdlcd_set_output_mode(int xres, int yres) +{ + return 0; +} +#endif + +#define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg)) +#define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg)) + +static int hdlcd_set_par(struct fb_info *info) +{ + struct hdlcd_device *hdlcd = to_hdlcd_device(info); + int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8; + int polarities; + + if (!memcmp(&info->var, &cached_var_screeninfo, sizeof(struct fb_var_screeninfo))) + return 0; + + hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel; + + if (hdlcd->fb.var.bits_per_pixel >= 16) + hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR; + else + hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + + memcpy(&cached_var_screeninfo, &info->var, sizeof(struct fb_var_screeninfo)); + + polarities = HDLCD_POLARITY_DATAEN | +#ifndef CONFIG_ARCH_TUSCAN + HDLCD_POLARITY_PIXELCLK | +#endif + HDLCD_POLARITY_DATA; + polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0; + polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0; + + hdlcd_disable(hdlcd); + + WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel); + WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel); + WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1); + WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1); + WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1); + WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1); + WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1); + WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1); + WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1); + WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1); + WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1); + WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities); + WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3); + WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset); + WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset); + WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset); + + hdlcd_set_output_mode(hdlcd->fb.var.xres, hdlcd->fb.var.yres); + + clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000); + clk_enable(hdlcd->clk); + + hdlcd_enable(hdlcd); + + return 0; +} + +static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + pal[regno] = ((red >> 8) << info->var.red.offset) | + ((green >> 8) << info->var.green.offset) | + ((blue >> 8) << info->var.blue.offset); + } + + return 0; +} + +static irqreturn_t hdlcd_irq(int irq, void *data) +{ + struct hdlcd_device *hdlcd = data; + unsigned long irq_mask, irq_status; + + irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK); + irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS); + + /* acknowledge interrupt(s) */ + WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status); + + if (irq_status & HDLCD_INTERRUPT_VSYNC) { + /* disable future VSYNC interrupts */ + WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC); + + complete(&hdlcd->vsync_completion); + } + + return IRQ_HANDLED; +} + +static int hdlcd_wait_for_vsync(struct fb_info *info) +{ + struct hdlcd_device *hdlcd = to_hdlcd_device(info); + unsigned long irq_mask; + int err; + + /* enable VSYNC interrupt */ + irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK); + WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC); + + err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion, + msecs_to_jiffies(100)); + + if (!err) + return -ETIMEDOUT; + + return 0; +} + +static int hdlcd_blank(int blank_mode, struct fb_info *info) +{ + struct hdlcd_device *hdlcd = to_hdlcd_device(info); + + switch (blank_mode) { + case FB_BLANK_POWERDOWN: + clk_disable(hdlcd->clk); + case FB_BLANK_NORMAL: + hdlcd_disable(hdlcd); + break; + case FB_BLANK_UNBLANK: + clk_enable(hdlcd->clk); + hdlcd_enable(hdlcd); + break; + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + } + + return 0; +} + +static void hdlcd_mmap_open(struct vm_area_struct *vma) +{ +} + +static void hdlcd_mmap_close(struct vm_area_struct *vma) +{ +} + +static struct vm_operations_struct hdlcd_mmap_ops = { + .open = hdlcd_mmap_open, + .close = hdlcd_mmap_close, +}; + +static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct hdlcd_device *hdlcd = to_hdlcd_device(info); + unsigned long off; + unsigned long start; + unsigned long len = hdlcd->fb.fix.smem_len; + + if (vma->vm_end - vma->vm_start == 0) + return 0; + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + + off = vma->vm_pgoff << PAGE_SHIFT; + if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + + start = hdlcd->fb.fix.smem_start; + off += start; + + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &hdlcd_mmap_ops; + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct hdlcd_device *hdlcd = to_hdlcd_device(info); + + hdlcd->fb.var.yoffset = var->yoffset; + WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start + + (var->yoffset * hdlcd->fb.fix.line_length)); + + hdlcd_wait_for_vsync(info); + + return 0; +} + +static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + int err; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + err = hdlcd_wait_for_vsync(info); + break; + default: + err = -ENOIOCTLCMD; + break; + } + + return err; +} + +static struct fb_ops hdlcd_ops = { + .owner = THIS_MODULE, + .fb_check_var = hdlcd_check_var, + .fb_set_par = hdlcd_set_par, + .fb_setcolreg = hdlcd_setcolreg, + .fb_blank = hdlcd_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_mmap = hdlcd_mmap, + .fb_pan_display = hdlcd_pan_display, + .fb_ioctl = hdlcd_ioctl, + .fb_compat_ioctl = hdlcd_ioctl +}; + +static int hdlcd_setup(struct hdlcd_device *hdlcd) +{ + u32 version; + int err = -EFAULT; + + hdlcd->clk = clk_get(hdlcd->dev, NULL); + if (IS_ERR(hdlcd->clk)) { + dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n"); + return PTR_ERR(hdlcd->clk); + } + + hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len); + if (!hdlcd->base) { + dev_err(hdlcd->dev, "HDLCD: unable to map registers\n"); + goto remap_err; + } + + hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); + if (!hdlcd->fb.pseudo_palette) { + dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n"); + err = -ENOMEM; + goto kmalloc_err; + } + + version = readl(hdlcd->base + HDLCD_REG_VERSION); + if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) { + dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version); + err = -EINVAL; + goto kmalloc_err; + } + dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n", + (version & HDLCD_VERSION_MAJOR_MASK) >> 8, + version & HDLCD_VERSION_MINOR_MASK); + + strcpy(hdlcd->fb.fix.id, "hdlcd"); + hdlcd->fb.fbops = &hdlcd_ops; + hdlcd->fb.flags = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/; + + hdlcd->fb.fix.type = FB_TYPE_PACKED_PIXELS; + hdlcd->fb.fix.type_aux = 0; + hdlcd->fb.fix.xpanstep = 0; + hdlcd->fb.fix.ypanstep = 1; + hdlcd->fb.fix.ywrapstep = 0; + hdlcd->fb.fix.accel = FB_ACCEL_NONE; + + hdlcd->fb.var.nonstd = 0; + hdlcd->fb.var.activate = FB_ACTIVATE_NOW; + hdlcd->fb.var.height = -1; + hdlcd->fb.var.width = -1; + hdlcd->fb.var.accel_flags = 0; + + init_completion(&hdlcd->vsync_completion); + + if (hdlcd->edid) { + /* build modedb from EDID */ + fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs); + fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb, + hdlcd->fb.monspecs.modedb_len, + &hdlcd->fb.modelist); + fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, + hdlcd->fb.monspecs.modedb, + hdlcd->fb.monspecs.modedb_len, + &hdlcd_default_mode, 32); + } else { + hdlcd->fb.monspecs.hfmin = 0; + hdlcd->fb.monspecs.hfmax = 100000; + hdlcd->fb.monspecs.vfmin = 0; + hdlcd->fb.monspecs.vfmax = 400; + hdlcd->fb.monspecs.dclkmin = 1000000; + hdlcd->fb.monspecs.dclkmax = 100000000; + fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32); + } + + dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres, + hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel, + hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60); + hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres; + hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2; + + /* initialise and set the palette */ + if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) { + dev_err(hdlcd->dev, "failed to allocate cmap memory\n"); + err = -ENOMEM; + goto setup_err; + } + fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb); + + /* Allow max number of outstanding requests with the largest beat burst */ + WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16); + /* Set the framebuffer base to start of allocated memory */ + WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start); + /* Ensure interrupts are disabled */ + WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0); + + if (!register_framebuffer(&hdlcd->fb)) { + fb_set_var(&hdlcd->fb, &hdlcd->fb.var); + clk_enable(hdlcd->clk); + return 0; + } + + dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n"); + + fb_dealloc_cmap(&hdlcd->fb.cmap); +setup_err: + iounmap(hdlcd->base); +kmalloc_err: + kfree(hdlcd->fb.pseudo_palette); +remap_err: + clk_put(hdlcd->clk); + return err; +} + +static inline unsigned char atohex(u8 data) +{ + if (!isxdigit(data)) + return 0; + /* truncate the upper nibble and add 9 to non-digit values */ + return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf); +} + +/* EDID data is passed from devicetree in a literal string that can contain spaces and + the hexadecimal dump of the data */ +static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len) +{ + int i, j; + + if (!edid_data) + return -EINVAL; + + hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL); + if (!hdlcd->edid) + return -ENOMEM; + + for (i = 0, j = 0; i < data_len; i++) { + if (isspace(edid_data[i])) + continue; + hdlcd->edid[j++] = atohex(edid_data[i]); + if (j >= EDID_LENGTH) + break; + } + + if (j < EDID_LENGTH) { + kfree(hdlcd->edid); + hdlcd->edid = NULL; + return -EINVAL; + } + + return 0; +} + +static int hdlcd_probe(struct platform_device *pdev) +{ + int err = 0, i; + struct hdlcd_device *hdlcd; + struct resource *mem; +#ifdef CONFIG_OF + struct device_node *of_node; +#endif + + memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo)); + + dev_dbg(&pdev->dev, "HDLCD: probing\n"); + + hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL); + if (!hdlcd) + return -ENOMEM; + +#ifdef CONFIG_OF + of_node = pdev->dev.of_node; + if (of_node) { + int len; + const u8 *edid; + const void *prop = of_get_property(of_node, "mode", &len); + if (prop) + strncpy(fb_mode, prop, len); + prop = of_get_property(of_node, "framebuffer", &len); + if (prop) { + hdlcd->fb.fix.smem_start = of_read_ulong((const __be32 *) prop, 1); + framebuffer_size = of_read_ulong((const __be32 *)prop, 1); + if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE) + framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE; + dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n", + hdlcd->fb.fix.smem_start, framebuffer_size); + } + edid = of_get_property(of_node, "edid", &len); + if (edid) { + err = parse_edid_data(hdlcd, edid, len); +#ifdef CONFIG_SERIAL_AMBA_PCU_UART + } else { + /* ask the firmware to fetch the EDID */ + dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n"); + hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL); + if (!hdlcd->edid) + return -ENOMEM; + err = get_edid(hdlcd->edid); +#endif /* CONFIG_SERIAL_AMBA_PCU_UART */ + } + if (err) + dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n"); + } +#endif /* CONFIG_OF */ + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n"); + err = -EINVAL; + goto resource_err; + } + + i = platform_get_irq(pdev, 0); + if (i < 0) { + dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n"); + err = -ENOENT; + goto resource_err; + } else { + err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd); + if (err) { + dev_err(&pdev->dev, "HDLCD: unable to request irq\n"); + goto resource_err; + } + hdlcd->irq = i; + } + + if (!request_mem_region(mem->start, resource_size(mem), dev_name(&pdev->dev))) { + err = -ENXIO; + goto request_err; + } + + if (!hdlcd->fb.fix.smem_start) { + dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n"); + err = -ENOMEM; + goto memalloc_err; + } + hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size); + if (!hdlcd->fb.screen_base) { + dev_err(&pdev->dev, "unable to ioremap framebuffer\n"); + err = -ENOMEM; + goto probe_err; + } + + hdlcd->fb.screen_size = framebuffer_size; + hdlcd->fb.fix.smem_len = framebuffer_size; + hdlcd->fb.fix.mmio_start = mem->start; + hdlcd->fb.fix.mmio_len = resource_size(mem); + + /* Clear the framebuffer */ + memset(hdlcd->fb.screen_base, 0, framebuffer_size); + + hdlcd->dev = &pdev->dev; + + dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n", + hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start); + + err = hdlcd_setup(hdlcd); + + if (err) + goto probe_err; + + platform_set_drvdata(pdev, hdlcd); + return 0; + +probe_err: + iounmap(hdlcd->fb.screen_base); + memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start); + +memalloc_err: + release_mem_region(mem->start, resource_size(mem)); + +request_err: + free_irq(hdlcd->irq, hdlcd); + +resource_err: + kfree(hdlcd); + + return err; +} + +static int hdlcd_remove(struct platform_device *pdev) +{ + struct hdlcd_device *hdlcd = platform_get_drvdata(pdev); + + clk_disable(hdlcd->clk); + clk_put(hdlcd->clk); + + /* unmap memory */ + iounmap(hdlcd->fb.screen_base); + iounmap(hdlcd->base); + + /* deallocate fb memory */ + fb_dealloc_cmap(&hdlcd->fb.cmap); + kfree(hdlcd->fb.pseudo_palette); + memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start); + release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len); + + free_irq(hdlcd->irq, NULL); + kfree(hdlcd); + + return 0; +} + +#ifdef CONFIG_PM +static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* not implemented yet */ + return 0; +} + +static int hdlcd_resume(struct platform_device *pdev) +{ + /* not implemented yet */ + return 0; +} +#else +#define hdlcd_suspend NULL +#define hdlcd_resume NULL +#endif + +static struct platform_driver hdlcd_driver = { + .probe = hdlcd_probe, + .remove = hdlcd_remove, + .suspend = hdlcd_suspend, + .resume = hdlcd_resume, + .driver = { + .name = "hdlcd", + .owner = THIS_MODULE, + .of_match_table = hdlcd_of_matches, + }, +}; + +static int __init hdlcd_init(void) +{ + return platform_driver_register(&hdlcd_driver); +} + +void __exit hdlcd_exit(void) +{ + platform_driver_unregister(&hdlcd_driver); +} + +module_init(hdlcd_init); +module_exit(hdlcd_exit); + +MODULE_AUTHOR("Liviu Dudau"); +MODULE_DESCRIPTION("ARM HDLCD core driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 7c7d3b31d5e22bed91202c022932e4bde0cf1822 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 13 Jun 2012 09:45:43 +0100 Subject: ARM HDLCD: Add missing clk_{un}prepare calls Signed-off-by: Jon Medhurst --- drivers/video/arm-hdlcd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 3e103a5c9b5..221004379c7 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -219,6 +219,7 @@ static int hdlcd_set_par(struct fb_info *info) hdlcd_set_output_mode(hdlcd->fb.var.xres, hdlcd->fb.var.yres); + clk_prepare(hdlcd->clk); clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000); clk_enable(hdlcd->clk); @@ -674,6 +675,7 @@ static int hdlcd_remove(struct platform_device *pdev) struct hdlcd_device *hdlcd = platform_get_drvdata(pdev); clk_disable(hdlcd->clk); + clk_unprepare(hdlcd->clk); clk_put(hdlcd->clk); /* unmap memory */ -- cgit v1.2.3 From 51f1911828c30b0ce27825452cef4db5a00edb07 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 13 Jul 2012 13:32:48 +0100 Subject: ARM HDLCD: Fixup driver to resemble ARM's latest codebase. Signed-off-by: Jon Medhurst --- drivers/video/arm-hdlcd.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 221004379c7..948d41c1413 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -29,10 +29,12 @@ #include "edid.h" -#ifdef CONFIG_SERIAL_AMBA_PCU_UART /* set the DVI output mode using the firmware */ -int set_dvi_mode(u8 *msgbuf); +extern int set_dvi_mode(int mode); + +#ifdef CONFIG_SERIAL_AMBA_PCU_UART int get_edid(u8 *msgbuf); +#else #endif #define to_hdlcd_device(info) container_of(info, struct hdlcd_device, fb) @@ -140,34 +142,29 @@ static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return hdlcd_set_bitfields(hdlcd, var); } -#ifdef CONFIG_SERIAL_AMBA_PCU_UART static int hdlcd_set_output_mode(int xres, int yres) { - /* firmware uses some stupid protocol: 5 bytes (only byte 1 used) + /* some firmware uses some stupid protocol: 5 bytes (only byte 1 used) to send 3 bits of information (value between 0 - 5) */ u8 msgbuffer[5]; memset(msgbuffer, 0, sizeof(msgbuffer)); - /* default resolution: 640 x 480 */ - if (xres == 800 && yres <= 600) + + if (xres < 800 && yres < 600) + msgbuffer[0] = 0; /* default resolution: 640 x 480 */ + else if (xres < 1024 && yres < 768) msgbuffer[0] = 1; /* SVGA: 800 * 600 */ - else if (xres == 1024 && yres <= 768) + else if (xres < 1280 && yres < 1024) msgbuffer[0] = 2; /* XGA: 1024 * 768 */ - else if (xres == 1280 && yres <= 1024) + else if (xres < 1600 && yres < 1200) msgbuffer[0] = 3; /* SXGA: 1280 * 1024 */ - else if (xres == 1600 && yres <= 1200) + else if (xres < 1920 && yres <= 1200) msgbuffer[0] = 4; /* UXGA: 1600 * 1200 */ - else if (xres == 1920 && yres <= 1200) + else msgbuffer[0] = 5; /* WUXGA: 1920 * 1200 */ - return set_dvi_mode(msgbuffer); -} -#else -inline int hdlcd_set_output_mode(int xres, int yres) -{ - return 0; + return set_dvi_mode(msgbuffer[0]); } -#endif #define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg)) #define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg)) @@ -565,13 +562,16 @@ static int hdlcd_probe(struct platform_device *pdev) if (of_node) { int len; const u8 *edid; - const void *prop = of_get_property(of_node, "mode", &len); + const __be32 *prop = of_get_property(of_node, "mode", &len); if (prop) - strncpy(fb_mode, prop, len); + strncpy(fb_mode, (char *)prop, len); prop = of_get_property(of_node, "framebuffer", &len); if (prop) { - hdlcd->fb.fix.smem_start = of_read_ulong((const __be32 *) prop, 1); - framebuffer_size = of_read_ulong((const __be32 *)prop, 1); + hdlcd->fb.fix.smem_start = of_read_ulong(prop, + of_n_addr_cells(of_node)); + prop += of_n_addr_cells(of_node); + framebuffer_size = of_read_ulong(prop, + of_n_size_cells(of_node)); if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE) framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE; dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n", -- cgit v1.2.3 From f03c663576a50642f1e9f142627a86de61a6574e Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 21 Jun 2012 17:16:33 +0100 Subject: ARM HDLCD: Get it working under Android. Add a shortcut when set_par is called to only change the yoffset. Android is doing that instead of calling pan_display to flip the framebuffer. Signed-off-by: Chris Redpath --- drivers/video/arm-hdlcd.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 948d41c1413..526d771db99 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -166,6 +166,11 @@ static int hdlcd_set_output_mode(int xres, int yres) return set_dvi_mode(msgbuffer[0]); } + +/* prototype */ +static int hdlcd_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); + #define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg)) #define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg)) @@ -174,9 +179,22 @@ static int hdlcd_set_par(struct fb_info *info) struct hdlcd_device *hdlcd = to_hdlcd_device(info); int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8; int polarities; - - if (!memcmp(&info->var, &cached_var_screeninfo, sizeof(struct fb_var_screeninfo))) + int old_yoffset; + + /* check for shortcuts */ + old_yoffset = cached_var_screeninfo.yoffset; + cached_var_screeninfo.yoffset = info->var.yoffset; + if (!memcmp(&info->var, &cached_var_screeninfo, + sizeof(struct fb_var_screeninfo))) { + if(old_yoffset != info->var.yoffset) { + /* we only changed yoffset */ + hdlcd_pan_display(&info->var, info); + memcpy(&cached_var_screeninfo, &info->var, + sizeof(struct fb_var_screeninfo)); + } + /* or no change */ return 0; + } hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel; -- cgit v1.2.3 From 585617333b9f883182dd285b50710deab04d4e00 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 29 Jun 2012 13:10:35 +0100 Subject: ARM HDLCD: Change default byte ordering to match Android assumption Change color byte location in 32-bit word from argb to abgr to match the assumption made in Android when 32-bit color displays are used. Signed-off-by: Chris Redpath --- drivers/video/arm-hdlcd.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 526d771db99..9b5b21ab136 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -116,10 +116,25 @@ static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd, } if (!ret) { - var->green.offset = var->blue.length; - var->red.offset = var->green.offset + var->green.length; - if (var->bits_per_pixel == 32) - var->transp.offset = var->red.offset + var->red.length; + if(var->bits_per_pixel != 32) + { + var->green.offset = var->blue.length; + var->red.offset = var->green.offset + var->green.length; + } + else + { + /* Previously, the byte ordering for 32-bit color was + * (msb)(lsb) + * but this does not match what android expects and + * the colors are odd. Instead, use + * + * Since we tell fb what we are doing, console + * , X and directfb access should work fine. + */ + var->green.offset = var->red.length; + var->blue.offset = var->green.offset + var->green.length; + var->transp.offset = var->blue.offset + var->blue.length; + } } return ret; -- cgit v1.2.3 From 7195cb14440127f4bcda912dd51cfc2760f9f25e Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 29 Jun 2012 16:07:08 +0100 Subject: ARM HDLCD: Add useful functions to HDLCD driver for system integration During TC2 integration a bad config option resulted in HDLCD memory reads not being serviced often enough. This lead to unsightly screen blanking. These options allow the developer to count the number of underruns and to control the color used by HDLCD when an underrun prevents accessing pixel data. The combination of these two options allow easy diagnosis of HDLCD underrun conditions. Signed-off-by: Chris Redpath --- drivers/video/arm-hdlcd.c | 90 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 9b5b21ab136..8d5409db6b1 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -26,6 +26,10 @@ #include #include #include +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS +#include +#include +#endif #include "edid.h" @@ -47,6 +51,63 @@ static struct of_device_id hdlcd_of_matches[] = { /* Framebuffer size. */ static unsigned long framebuffer_size; +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS +static unsigned long buffer_underrun_events; +static DEFINE_SPINLOCK(hdlcd_underrun_lock); + +static void hdlcd_underrun_set(unsigned long val) +{ + spin_lock(&hdlcd_underrun_lock); + buffer_underrun_events = val; + spin_unlock(&hdlcd_underrun_lock); +} + +static unsigned long hdlcd_underrun_get(void) +{ + unsigned long val; + spin_lock(&hdlcd_underrun_lock); + val = buffer_underrun_events; + spin_unlock(&hdlcd_underrun_lock); + return val; +} + +#ifdef CONFIG_PROC_FS +static int hdlcd_underrun_show(struct seq_file *m, void *v) +{ + unsigned char underrun_string[32]; + snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get()); + seq_puts(m, underrun_string); + return 0; +} + +static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file) +{ + return single_open(file, hdlcd_underrun_show, NULL); +} + +static const struct file_operations proc_hdlcd_underrun_operations = { + .open = proc_hdlcd_underrun_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int hdlcd_underrun_init(void) +{ + hdlcd_underrun_set(0); + proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations); + return 0; +} +static void hdlcd_underrun_close(void) +{ + remove_proc_entry("hdlcd_underrun", NULL); +} +#else +static int hdlcd_underrun_init(void) { return 0; } +static void hdlcd_underrun_close(void) { } +#endif +#endif + static char *fb_mode = "1680x1050-32@60\0\0\0\0\0"; static struct fb_var_screeninfo cached_var_screeninfo; @@ -66,7 +127,6 @@ static struct fb_videomode hdlcd_default_mode = { .vmode = FB_VMODE_NONINTERLACED }; - static inline void hdlcd_enable(struct hdlcd_device *hdlcd) { dev_dbg(hdlcd->dev, "HDLCD: output enabled\n"); @@ -243,7 +303,12 @@ static int hdlcd_set_par(struct fb_info *info) WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1); WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities); WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3); +#ifdef HDLCD_RED_DEFAULT_COLOUR + WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \ + | hdlcd->fb.var.red.offset); +#else WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset); +#endif WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset); WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset); @@ -282,7 +347,12 @@ static irqreturn_t hdlcd_irq(int irq, void *data) /* acknowledge interrupt(s) */ WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status); - +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS + if (irq_status & HDLCD_INTERRUPT_UNDERRUN) { + /* increment the count */ + hdlcd_underrun_set(hdlcd_underrun_get() + 1); + } +#endif if (irq_status & HDLCD_INTERRUPT_VSYNC) { /* disable future VSYNC interrupts */ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC); @@ -514,9 +584,13 @@ static int hdlcd_setup(struct hdlcd_device *hdlcd) WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16); /* Set the framebuffer base to start of allocated memory */ WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start); +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS + /* turn on underrun interrupt for counting */ + WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN); +#else /* Ensure interrupts are disabled */ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0); - +#endif if (!register_framebuffer(&hdlcd->fb)) { fb_set_var(&hdlcd->fb, &hdlcd->fb.var); clk_enable(hdlcd->clk); @@ -758,11 +832,21 @@ static struct platform_driver hdlcd_driver = { static int __init hdlcd_init(void) { +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS + int err = platform_driver_register(&hdlcd_driver); + if (!err) + hdlcd_underrun_init(); + return err; +#else return platform_driver_register(&hdlcd_driver); +#endif } void __exit hdlcd_exit(void) { +#ifdef HDLCD_COUNT_BUFFERUNDERRUNS + hdlcd_underrun_close(); +#endif platform_driver_unregister(&hdlcd_driver); } -- cgit v1.2.3 From 5705f5f9c6d47d59af73145b5febd1b1d5a56775 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 29 Jun 2012 16:07:46 +0100 Subject: ARM HDLCD: Review comments from Liviu - extraneous memcpy Signed-off-by: Chris Redpath Reviewed-By: Liviu Dudau --- drivers/video/arm-hdlcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 8d5409db6b1..f16bb882e0c 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -262,10 +262,10 @@ static int hdlcd_set_par(struct fb_info *info) if (!memcmp(&info->var, &cached_var_screeninfo, sizeof(struct fb_var_screeninfo))) { if(old_yoffset != info->var.yoffset) { - /* we only changed yoffset */ + /* we only changed yoffset, and we already + * already recorded it a couple lines up + */ hdlcd_pan_display(&info->var, info); - memcpy(&cached_var_screeninfo, &info->var, - sizeof(struct fb_var_screeninfo)); } /* or no change */ return 0; -- cgit v1.2.3 From 9f14266f6f6108f0ff67be65da8866f3b757beec Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Thu, 12 Jul 2012 12:33:13 +0100 Subject: ARM HDLCD: Add developer option to remove double-height framebuffers This option can be used with Android to push the graphics subsystem into a different composition strategy which is more effective when used on hardware where the framebuffer memory is not cacheable. Signed-off-by: Chris Redpath --- drivers/video/arm-hdlcd.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index f16bb882e0c..9600c70209b 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -205,7 +205,11 @@ static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info) struct hdlcd_device *hdlcd = to_hdlcd_device(info); int bytes_per_pixel = var->bits_per_pixel / 8; +#ifdef HDLCD_NO_VIRTUAL_SCREEN + var->yres_virtual = var->yres; +#else var->yres_virtual = 2 * var->yres; +#endif if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len) return -ENOMEM; @@ -570,7 +574,11 @@ static int hdlcd_setup(struct hdlcd_device *hdlcd) hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel, hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60); hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres; +#ifdef HDLCD_NO_VIRTUAL_SCREEN + hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres; +#else hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2; +#endif /* initialise and set the palette */ if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) { -- cgit v1.2.3 From ac1b17efc7bae092fd46aeb144ddbf28add50877 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 4 Oct 2012 12:38:06 +0100 Subject: ARM HDLCD: Changes to get HDLCD working with 've-updates' Signed-off-by: Jon Medhurst --- drivers/video/arm-hdlcd.c | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index 9600c70209b..bd5fe68684a 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -33,9 +33,6 @@ #include "edid.h" -/* set the DVI output mode using the firmware */ -extern int set_dvi_mode(int mode); - #ifdef CONFIG_SERIAL_AMBA_PCU_UART int get_edid(u8 *msgbuf); #else @@ -221,31 +218,6 @@ static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return hdlcd_set_bitfields(hdlcd, var); } -static int hdlcd_set_output_mode(int xres, int yres) -{ - /* some firmware uses some stupid protocol: 5 bytes (only byte 1 used) - to send 3 bits of information (value between 0 - 5) */ - u8 msgbuffer[5]; - - memset(msgbuffer, 0, sizeof(msgbuffer)); - - if (xres < 800 && yres < 600) - msgbuffer[0] = 0; /* default resolution: 640 x 480 */ - else if (xres < 1024 && yres < 768) - msgbuffer[0] = 1; /* SVGA: 800 * 600 */ - else if (xres < 1280 && yres < 1024) - msgbuffer[0] = 2; /* XGA: 1024 * 768 */ - else if (xres < 1600 && yres < 1200) - msgbuffer[0] = 3; /* SXGA: 1280 * 1024 */ - else if (xres < 1920 && yres <= 1200) - msgbuffer[0] = 4; /* UXGA: 1600 * 1200 */ - else - msgbuffer[0] = 5; /* WUXGA: 1920 * 1200 */ - - return set_dvi_mode(msgbuffer[0]); -} - - /* prototype */ static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); @@ -316,8 +288,6 @@ static int hdlcd_set_par(struct fb_info *info) WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset); WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset); - hdlcd_set_output_mode(hdlcd->fb.var.xres, hdlcd->fb.var.yres); - clk_prepare(hdlcd->clk); clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000); clk_enable(hdlcd->clk); @@ -502,6 +472,8 @@ static int hdlcd_setup(struct hdlcd_device *hdlcd) u32 version; int err = -EFAULT; + hdlcd->fb.device = hdlcd->dev; + hdlcd->clk = clk_get(hdlcd->dev, NULL); if (IS_ERR(hdlcd->clk)) { dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n"); -- cgit v1.2.3 From e9a113ad8f8d113afd80028561f07426b229a31f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 5 Mar 2013 09:24:57 +0800 Subject: ARM HDLCD: Fix compilation on Linux 3.9 Signed-off-by: Jon Medhurst --- drivers/video/arm-hdlcd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index bd5fe68684a..f9c4e7490c0 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 83bce1182d983be01f3247f974654cdac5690436 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 14 Jun 2013 18:30:08 +0100 Subject: ARM HDLCD: Fix clock initialisation sequence This reworks HDLCD initialisation to mirror how CLCD does this, in particular to prepare the clock immediately after it has been got which ensures that we don't try and enable clocks before they were prepared, e.g. in the former clk_enable after register_framebuffer(). The reason this issue wasn't noticed before is that we have been setting CONFIG_FRAMEBUFFER_CONSOLE and this caused register_framebuffer() to trigger the creation of a console which calls hdlcd_set_par(), which in turn was preparing and enabling the clock. Signed-off-by: Jon Medhurst --- drivers/video/arm-hdlcd.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c index f9c4e7490c0..cfd631e3dc5 100644 --- a/drivers/video/arm-hdlcd.c +++ b/drivers/video/arm-hdlcd.c @@ -289,7 +289,6 @@ static int hdlcd_set_par(struct fb_info *info) WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset); WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset); - clk_prepare(hdlcd->clk); clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000); clk_enable(hdlcd->clk); @@ -481,6 +480,10 @@ static int hdlcd_setup(struct hdlcd_device *hdlcd) return PTR_ERR(hdlcd->clk); } + err = clk_prepare(hdlcd->clk); + if (err) + goto clk_prepare_err; + hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len); if (!hdlcd->base) { dev_err(hdlcd->dev, "HDLCD: unable to map registers\n"); @@ -572,9 +575,9 @@ static int hdlcd_setup(struct hdlcd_device *hdlcd) /* Ensure interrupts are disabled */ WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0); #endif + fb_set_var(&hdlcd->fb, &hdlcd->fb.var); + if (!register_framebuffer(&hdlcd->fb)) { - fb_set_var(&hdlcd->fb, &hdlcd->fb.var); - clk_enable(hdlcd->clk); return 0; } @@ -586,6 +589,8 @@ setup_err: kmalloc_err: kfree(hdlcd->fb.pseudo_palette); remap_err: + clk_unprepare(hdlcd->clk); +clk_prepare_err: clk_put(hdlcd->clk); return err; } -- cgit v1.2.3