From b5c681445d26ec48dd94ce2a67a49b182062695e Mon Sep 17 00:00:00 2001 From: "Mark F. Brown" Date: Thu, 1 Mar 2012 17:30:03 -0800 Subject: [PATCH] merrifield: framebuffer support for tangier simulator BZ: 25997 Change-Id: Id4d2c590c5ea799dbf0db9e278d2c7061ab8ed16 Signed-off-by: Mark F. Brown Reviewed-on: http://android.intel.com:8080/37365 Reviewed-by: Ng, Cheon-woei Tested-by: Ng, Cheon-woei Reviewed-by: buildbot Tested-by: buildbot --- drivers/video/Kconfig | 10 ++ drivers/video/Makefile | 1 + drivers/video/mrflvp-fb.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/video/mrflvp-fb.h | 9 ++ 4 files changed, 401 insertions(+) create mode 100644 drivers/video/mrflvp-fb.c create mode 100644 drivers/video/mrflvp-fb.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4c85a4b..240f1ef 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -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" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8b83129..760922f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -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 index 0000000..bac73c9 --- /dev/null +++ b/drivers/video/mrflvp-fb.c @@ -0,0 +1,381 @@ +/* + * Merrifield Virtual Platform Display Controller Driver + * + * Copyright (C) 2011 Intel Corporation + * Author: Mark F. Brown + * Author: Joel Rosenzweig + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..3b387eb --- /dev/null +++ b/drivers/video/mrflvp-fb.h @@ -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 -- 2.7.4