merrifield: framebuffer support for tangier simulator
authorMark F. Brown <mark.f.brown@intel.com>
Fri, 2 Mar 2012 01:30:03 +0000 (17:30 -0800)
committerbuildbot <buildbot@intel.com>
Sat, 10 Mar 2012 02:23:00 +0000 (18:23 -0800)
BZ: 25997

Change-Id: Id4d2c590c5ea799dbf0db9e278d2c7061ab8ed16
Signed-off-by: Mark F. Brown <mark.f.brown@intel.com>
Reviewed-on: http://android.intel.com:8080/37365
Reviewed-by: Ng, Cheon-woei <cheon-woei.ng@intel.com>
Tested-by: Ng, Cheon-woei <cheon-woei.ng@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/mrflvp-fb.c [new file with mode: 0644]
drivers/video/mrflvp-fb.h [new file with mode: 0644]

index 4c85a4b..240f1ef 100644 (file)
@@ -2385,6 +2385,16 @@ config FB_PUV3_UNIGFX
          Choose this option if you want to use the Unigfx device as a
          framebuffer device. Without the support of PCI & AGP.
 
+config FB_MRFLD_VP
+       tristate "Intel Merrifield Virtual Platform Framebuffer Driver"
+       depends on PCI && X86_MRFLD && FB
+       default n
+       select FB_CFB_COPYAREA
+       select FB_CFB_FILLRECT
+       select FB_CFB_IMAGEBLIT
+       help
+          Basic support for Generic Framebuffer Driver for Merrifield.
+
 source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 
index 8b83129..760922f 100644 (file)
@@ -141,6 +141,7 @@ obj-$(CONFIG_FB_MSM)              += msm/
 obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
 obj-$(CONFIG_FB_JZ4740)                  += jz4740_fb.o
 obj-$(CONFIG_FB_PUV3_UNIGFX)      += fb-puv3.o
+obj-$(CONFIG_FB_MRFLD_VP)        += mrflvp-fb.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff --git a/drivers/video/mrflvp-fb.c b/drivers/video/mrflvp-fb.c
new file mode 100644 (file)
index 0000000..bac73c9
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Merrifield Virtual Platform Display Controller Driver
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Author: Mark F. Brown <mark.f.brown@intel.com>
+ * Author: Joel Rosenzweig <joel.b.rosenzweig@intel.com>
+ * This code is based from goldfish_fb.c from Google Android
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include "mrflvp-fb.h"
+
+#define DRV_NAME "mrflvp-fb"
+
+static struct fb_fix_screeninfo merrifield_fb_fix = {
+       .id = "mrflvp-fb",
+       .type = FB_TYPE_PACKED_PIXELS,
+       .ypanstep = 1,
+       .visual = FB_VISUAL_PSEUDOCOLOR,
+       .accel = FB_ACCEL_NONE,
+};
+
+/* TODO remove global */
+static void __iomem *io_virt;
+
+static void merrifield_fb_remove(struct pci_dev *pdev);
+
+static int merrifield_fb_check_var(struct fb_var_screeninfo *var,
+                                  struct fb_info *info)
+{
+       if ((var->rotate & 1) != (info->var.rotate & 1)) {
+               if ((var->xres != info->var.yres) ||
+                   (var->yres != info->var.xres) ||
+                   (var->xres_virtual != info->var.yres) ||
+                   (var->yres_virtual > info->var.xres * 2) ||
+                   (var->yres_virtual < info->var.xres)) {
+                       return -EINVAL;
+               }
+       } else {
+               if ((var->xres != info->var.xres) ||
+                   (var->yres != info->var.yres) ||
+                   (var->xres_virtual != info->var.xres) ||
+                   (var->yres_virtual > info->var.yres * 2) ||
+                   (var->yres_virtual < info->var.yres)) {
+                       return -EINVAL;
+               }
+       }
+
+       if ((var->xoffset != info->var.xoffset) ||
+           (var->bits_per_pixel != info->var.bits_per_pixel) ||
+           (var->grayscale != info->var.grayscale)) {
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int merrifield_fb_pan_display(struct fb_var_screeninfo *var,
+                                    struct fb_info *info)
+{
+       /* TODO convert magic numbers to macro definitions */
+       if (var->yoffset == 0)
+               /* surface address register */
+               iowrite32(0x0, io_virt + 0x7019C);
+       else
+               /* surface address register */
+               iowrite32(var->xres * (var->bits_per_pixel / 8) * var->yres,
+                         io_virt + 0x7019C);
+
+       return 0;
+}
+
+static struct fb_ops merrifield_fb_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var = merrifield_fb_check_var,
+       .fb_pan_display = merrifield_fb_pan_display,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
+};
+
+static void init_display_controller_registers(void)
+{
+
+       int width = H_ACTIVE;
+       int height = V_ACTIVE;
+       int viewport_width = H_ACTIVE;
+       int viewport_height = V_ACTIVE;
+
+       BUG_ON(io_virt == NULL);
+
+       /* TODO convert magic numbers to macro definitions */
+       /* Programming LNC CRC Registers */
+       iowrite32(0x80135937, io_virt + 0x60050);
+       iowrite32(0x0048efae, io_virt + 0x60054);
+       iowrite32(0x004ad4cb, io_virt + 0x60058);
+       iowrite32(0x0, io_virt + 0x6005c);
+       /* Program clocks and check for clock lock */
+       /* Program DSI PLL */
+       iowrite32(0x0, io_virt + 0x0F014);
+       iowrite32(0x000000c1, io_virt + 0xF040);
+       iowrite32(0x00800000, io_virt + 0xF014);
+       iowrite32(0x80800000, io_virt + 0xF014);
+       iowrite32(0x0, io_virt + 0x62190);
+       /* Enable MIPI port */
+       iowrite32(0x80810000, io_virt + 0x61190);
+       iowrite32(0x270f04, io_virt + 0x61210);
+       /* MIPI DPHY PARAM REG X */
+       iowrite32(0xb14540c, io_virt + 0xB080);
+       /* Data lanes - 2 RGB 888 X */
+       iowrite32((RGB888 << 7) | 0x12, io_virt + 0xB00c);
+       /* Video mode - Burst mode X */
+       iowrite32(0x5, io_virt + 0xB058);
+       /* MIPI control register X */
+       iowrite32(0x18, io_virt + 0xB104);
+       /* Interrupt enable X */
+       iowrite32(0xffffffff, io_virt + 0xB008);
+       /* MIPI HS-TX timeout reg X */
+       iowrite32(0x3fffff, io_virt + 0xB010);
+       /* LP reception timeout reg X */
+       iowrite32(0xffff, io_virt + 0xB014);
+       /* Turnaround timeout X */
+       iowrite32(0x1f, io_virt + 0xB018);
+       /* Reset timeout X */
+       iowrite32(0xff, io_virt + 0xB01c);
+       /* HS to LP timeout reg X */
+       iowrite32(0x46, io_virt + 0xB044);
+       /* MIPI Clock lane switching time count */
+       iowrite32(0xa0014, io_virt + 0xB088);
+       /* DBI bandwidth control register */
+       iowrite32(0x400, io_virt + 0xB084);
+       /* Master_init_timer X */
+       iowrite32(0x7d0, io_virt + 0xB050);
+       /* Disable clock stopping X */
+       iowrite32(0x0, io_virt + 0xB05C);
+       /* LP Byte clock X */
+       iowrite32(0x4, io_virt + 0xB060);
+       /* DPI resolution X */
+       iowrite32((height << 16) | width, io_virt + 0xB020);
+       /* Horizontal sync padding X */
+       iowrite32(0x4, io_virt + 0xB028);
+       /* Horizontal back porch X */
+       iowrite32(0xe, io_virt + 0xB02c);
+       /* Horizontal Front porch X */
+       iowrite32(0x8, io_virt + 0xB030);
+       /* Horizontal active area count X */
+       iowrite32(0x2d0, io_virt + 0xB034);
+       /* Vertical Sync padding X */
+       iowrite32(0x4, io_virt + 0xB038);
+       /* Vertical back porch X */
+       iowrite32(0x8, io_virt + 0xB03c);
+       /* Vertical front porch X */
+       iowrite32(0x7, io_virt + 0xB040);
+       /* Turn on DPI */
+       iowrite32(0x1, io_virt + 0xB000);
+       /* Turn on DPI Control register */
+       iowrite32(0x2, io_virt + 0xB048);
+       /* Programming Pipe A */
+       iowrite32((((H_ACTIVE + 80 - 1) << 16) | (H_ACTIVE - 1)),
+                 io_virt + 0x60000);
+       iowrite32((((H_ACTIVE + 80 - 1) << 16) | (H_ACTIVE - 1)),
+                 io_virt + 0x60004);
+       iowrite32(((H_ACTIVE + 48 - 1) << 16) | (H_ACTIVE + 8 - 1),
+                 io_virt + 0x60008);
+       iowrite32((((V_ACTIVE + 5) << 16) | (V_ACTIVE - 1)), io_virt + 0x6000C);
+       iowrite32((((V_ACTIVE + 5) << 16) | (V_ACTIVE - 1)), io_virt + 0x60010);
+       iowrite32((((V_ACTIVE + 2) << 16) | (V_ACTIVE + 1)), io_virt + 0x60014);
+       iowrite32((((H_ACTIVE - 1) << 16) | (V_ACTIVE - 1)), io_virt + 0x6001C);
+       iowrite32(0x7b1dffff, io_virt + 0x70500);
+       iowrite32(0x6c000000, io_virt + 0x70504);
+       iowrite32(0x0, io_virt + 0x701D0);
+       iowrite32(0x0, io_virt + 0x701D4);
+       /* 0x5 == RGB565 */
+       /* 0xa == RGB888 24-bit RGB no Alpha */
+       /* 0xf == RGBA8888 32-bit RGB */
+       /* Enable Display Sprite A */
+       iowrite32(0x80000000 | (0x5 << 26), io_virt + 0x70180);
+       /* Stride */
+       iowrite32(0x00000780, io_virt + 0x70188);
+       /* Linear offset register */
+       iowrite32(0x0, io_virt + 0x70184);
+       /* Position */
+       iowrite32(0x0, io_virt + 0x7018C);
+       /* Width and height X */
+       iowrite32(((viewport_height - 1) << 16) | (viewport_width - 1),
+                 io_virt + 0x70190);
+       /* Surface address register */
+       iowrite32(0x0, io_virt + 0x7019C);
+       /* Disable VGA plane */
+       iowrite32(0x80000000, io_virt + 0x71400);
+       /* Pipe A Enable */
+       iowrite32(0x80000000, io_virt + 0x70008);
+       /* Pipe A Status register */
+       iowrite32(0xb000ffff, io_virt + 0x70024);
+
+}
+
+static int merrifield_fb_probe(struct pci_dev *pdev,
+                              const struct pci_device_id *id)
+{
+       int ret;
+       struct fb_info *info = NULL;
+       int fb_base = 0;
+       int width = H_ACTIVE;
+       int height = V_ACTIVE;
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable device!\n");
+               goto failure;
+       }
+
+       info = framebuffer_alloc(0, &pdev->dev);
+       if (!info) {
+               dev_err(&pdev->dev, "framebuffer allocation failure.\n");
+               goto failure;
+       }
+
+       merrifield_fb_fix.mmio_start = pci_resource_start(pdev, 0);
+       merrifield_fb_fix.mmio_len = pci_resource_len(pdev, 0);
+
+       if (!request_mem_region(merrifield_fb_fix.mmio_start,
+                               merrifield_fb_fix.mmio_len, DRV_NAME)) {
+               dev_err(&pdev->dev, "mmio request_mem_region failure!\n");
+               goto failure;
+       }
+
+       io_virt = ioremap_nocache(merrifield_fb_fix.mmio_start,
+                           merrifield_fb_fix.mmio_len);
+
+       pci_read_config_dword(pdev, 0x5C, &fb_base);
+
+       /* Allocate enough for up 2 x 16-bit frame buffers at
+        * our given resolution which is used for double buffering */
+       merrifield_fb_fix.smem_start = fb_base;
+       merrifield_fb_fix.smem_len = H_ACTIVE * V_ACTIVE * 4;
+
+       info->screen_base = ioremap_nocache(merrifield_fb_fix.smem_start,
+                                           merrifield_fb_fix.smem_len);
+       info->fix = merrifield_fb_fix;
+       info->fbops = &merrifield_fb_ops;
+       info->flags =
+           FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED | FBINFO_FLAG_DEFAULT;
+       strcat(info->fix.id, DRV_NAME);
+       info->var.activate = FB_ACTIVATE_NOW;
+       info->device = &pdev->dev;
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "cmap allocation failure.\n");
+               goto failure;
+       }
+
+       /* RGB 5:6:5 */
+       info->pseudo_palette = &info->cmap;
+       info->cmap.len = 16;
+       info->fix.type = FB_TYPE_PACKED_PIXELS;
+       info->fix.visual = FB_VISUAL_TRUECOLOR;
+       info->fix.line_length = width * 2;
+       info->fix.accel = FB_ACCEL_NONE;
+       info->fix.ypanstep = 1;
+       info->fix.smem_start = merrifield_fb_fix.smem_start;
+       info->fix.smem_len = merrifield_fb_fix.smem_len;
+       info->var.xres = width;
+       info->var.yres = height;
+       info->var.xres_virtual = width;
+       info->var.yres_virtual = height * 2;
+       info->var.bits_per_pixel = 16;
+       info->var.height = height;
+       info->var.width = width;
+       info->var.red.offset = 11;
+       info->var.red.length = 5;
+       info->var.green.offset = 5;
+       info->var.green.length = 6;
+       info->var.blue.offset = 0;
+       info->var.blue.length = 5;
+
+       ret = fb_set_var(info, &info->var);
+       if (ret) {
+               dev_err(&pdev->dev, "error setting var info\n");
+               goto failure;
+       }
+
+       info->pixmap.addr = kmalloc(4096, GFP_KERNEL);
+       if (!info->pixmap.addr) {
+               dev_err(&pdev->dev, "pixmap allocation failure\n");
+               goto failure;
+       }
+
+       info->pixmap.size = 4096;
+       info->pixmap.buf_align = 4;
+       info->pixmap.scan_align = 1;
+       info->pixmap.access_align = 32;
+       info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+       pci_set_drvdata(pdev, info);
+       if (register_framebuffer(info) < 0) {
+               dev_err(&pdev->dev, "could not register framebuffer\n");
+               goto failure;
+       }
+
+       init_display_controller_registers();
+
+       return 0;
+
+failure:
+       merrifield_fb_remove(pdev);
+
+       return ret;
+}
+
+static void merrifield_fb_remove(struct pci_dev *pdev)
+{
+       struct fb_info *info = pci_get_drvdata(pdev);
+
+       if (info) {
+               if (info->screen_base)
+                       iounmap(info->screen_base);
+               kfree(info->pixmap.addr);
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+               framebuffer_release(info);
+       }
+
+       if (io_virt)
+               iounmap(io_virt);
+
+       pci_disable_device(pdev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(merrifield_fb_devices) = {
+       {PCI_VENDOR_ID_INTEL, 0x1180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, merrifield_fb_devices);
+
+static struct pci_driver merrifield_fb_driver = {
+       .name = DRV_NAME,
+       .id_table = merrifield_fb_devices,
+       .probe = merrifield_fb_probe,
+       .remove = merrifield_fb_remove
+};
+
+static int __init merrifield_fb_init(void)
+{
+       return pci_register_driver(&merrifield_fb_driver);
+}
+
+static void __exit merrifield_fb_exit(void)
+{
+       pci_unregister_driver(&merrifield_fb_driver);
+}
+
+module_init(merrifield_fb_init);
+module_exit(merrifield_fb_exit);
diff --git a/drivers/video/mrflvp-fb.h b/drivers/video/mrflvp-fb.h
new file mode 100644 (file)
index 0000000..3b387eb
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __MRFL_DISPLAY_CONTROLLER_H
+#define __MRFL_DISPLAY_CONTROLLER_H
+
+#define H_ACTIVE 480
+#define V_ACTIVE 800
+#define RGB888 0x4
+#define RGB565 0x1
+
+#endif