162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/offb.c -- Open Firmware based frame buffer device 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This driver is partly based on the PowerMac console driver: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1162306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 1262306a36Sopenharmony_ci * more details. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/aperture.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/mm.h> 2162306a36Sopenharmony_ci#include <linux/vmalloc.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_address.h> 2562306a36Sopenharmony_ci#include <linux/interrupt.h> 2662306a36Sopenharmony_ci#include <linux/fb.h> 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/ioport.h> 2962306a36Sopenharmony_ci#include <linux/pci.h> 3062306a36Sopenharmony_ci#include <linux/platform_device.h> 3162306a36Sopenharmony_ci#include <asm/io.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#ifdef CONFIG_PPC32 3462306a36Sopenharmony_ci#include <asm/bootx.h> 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "macmodes.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* Supported palette hacks */ 4062306a36Sopenharmony_cienum { 4162306a36Sopenharmony_ci cmap_unknown, 4262306a36Sopenharmony_ci cmap_simple, /* ATI Mach64 */ 4362306a36Sopenharmony_ci cmap_r128, /* ATI Rage128 */ 4462306a36Sopenharmony_ci cmap_M3A, /* ATI Rage Mobility M3 Head A */ 4562306a36Sopenharmony_ci cmap_M3B, /* ATI Rage Mobility M3 Head B */ 4662306a36Sopenharmony_ci cmap_radeon, /* ATI Radeon */ 4762306a36Sopenharmony_ci cmap_gxt2000, /* IBM GXT2000 */ 4862306a36Sopenharmony_ci cmap_avivo, /* ATI R5xx */ 4962306a36Sopenharmony_ci cmap_qemu, /* qemu vga */ 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct offb_par { 5362306a36Sopenharmony_ci volatile void __iomem *cmap_adr; 5462306a36Sopenharmony_ci volatile void __iomem *cmap_data; 5562306a36Sopenharmony_ci int cmap_type; 5662306a36Sopenharmony_ci int blanked; 5762306a36Sopenharmony_ci u32 pseudo_palette[16]; 5862306a36Sopenharmony_ci resource_size_t base; 5962306a36Sopenharmony_ci resource_size_t size; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#ifdef CONFIG_PPC32 6362306a36Sopenharmony_ciextern boot_infos_t *boot_infos; 6462306a36Sopenharmony_ci#endif 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Definitions used by the Avivo palette hack */ 6762306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_SELECT 0x6480 6862306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_MODE 0x6484 6962306a36Sopenharmony_ci#define AVIVO_DC_LUT_RW_INDEX 0x6488 7062306a36Sopenharmony_ci#define AVIVO_DC_LUT_SEQ_COLOR 0x648c 7162306a36Sopenharmony_ci#define AVIVO_DC_LUT_PWL_DATA 0x6490 7262306a36Sopenharmony_ci#define AVIVO_DC_LUT_30_COLOR 0x6494 7362306a36Sopenharmony_ci#define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 7462306a36Sopenharmony_ci#define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c 7562306a36Sopenharmony_ci#define AVIVO_DC_LUT_AUTOFILL 0x64a0 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define AVIVO_DC_LUTA_CONTROL 0x64c0 7862306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 7962306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 8062306a36Sopenharmony_ci#define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc 8162306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 8262306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 8362306a36Sopenharmony_ci#define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define AVIVO_DC_LUTB_CONTROL 0x6cc0 8662306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 8762306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 8862306a36Sopenharmony_ci#define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc 8962306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 9062306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 9162306a36Sopenharmony_ci#define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * Set a single color register. The values supplied are already 9562306a36Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 9662306a36Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 10062306a36Sopenharmony_ci u_int transp, struct fb_info *info) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct offb_par *par = (struct offb_par *) info->par; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR) { 10562306a36Sopenharmony_ci u32 *pal = info->pseudo_palette; 10662306a36Sopenharmony_ci u32 cr = red >> (16 - info->var.red.length); 10762306a36Sopenharmony_ci u32 cg = green >> (16 - info->var.green.length); 10862306a36Sopenharmony_ci u32 cb = blue >> (16 - info->var.blue.length); 10962306a36Sopenharmony_ci u32 value; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (regno >= 16) 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci value = (cr << info->var.red.offset) | 11562306a36Sopenharmony_ci (cg << info->var.green.offset) | 11662306a36Sopenharmony_ci (cb << info->var.blue.offset); 11762306a36Sopenharmony_ci if (info->var.transp.length > 0) { 11862306a36Sopenharmony_ci u32 mask = (1 << info->var.transp.length) - 1; 11962306a36Sopenharmony_ci mask <<= info->var.transp.offset; 12062306a36Sopenharmony_ci value |= mask; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci pal[regno] = value; 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (regno > 255) 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci red >>= 8; 13062306a36Sopenharmony_ci green >>= 8; 13162306a36Sopenharmony_ci blue >>= 8; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!par->cmap_adr) 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci switch (par->cmap_type) { 13762306a36Sopenharmony_ci case cmap_simple: 13862306a36Sopenharmony_ci writeb(regno, par->cmap_adr); 13962306a36Sopenharmony_ci writeb(red, par->cmap_data); 14062306a36Sopenharmony_ci writeb(green, par->cmap_data); 14162306a36Sopenharmony_ci writeb(blue, par->cmap_data); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case cmap_M3A: 14462306a36Sopenharmony_ci /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ 14562306a36Sopenharmony_ci out_le32(par->cmap_adr + 0x58, 14662306a36Sopenharmony_ci in_le32(par->cmap_adr + 0x58) & ~0x20); 14762306a36Sopenharmony_ci fallthrough; 14862306a36Sopenharmony_ci case cmap_r128: 14962306a36Sopenharmony_ci /* Set palette index & data */ 15062306a36Sopenharmony_ci out_8(par->cmap_adr + 0xb0, regno); 15162306a36Sopenharmony_ci out_le32(par->cmap_adr + 0xb4, 15262306a36Sopenharmony_ci (red << 16 | green << 8 | blue)); 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case cmap_M3B: 15562306a36Sopenharmony_ci /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ 15662306a36Sopenharmony_ci out_le32(par->cmap_adr + 0x58, 15762306a36Sopenharmony_ci in_le32(par->cmap_adr + 0x58) | 0x20); 15862306a36Sopenharmony_ci /* Set palette index & data */ 15962306a36Sopenharmony_ci out_8(par->cmap_adr + 0xb0, regno); 16062306a36Sopenharmony_ci out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue)); 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case cmap_radeon: 16362306a36Sopenharmony_ci /* Set palette index & data (could be smarter) */ 16462306a36Sopenharmony_ci out_8(par->cmap_adr + 0xb0, regno); 16562306a36Sopenharmony_ci out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue)); 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case cmap_gxt2000: 16862306a36Sopenharmony_ci out_le32(((unsigned __iomem *) par->cmap_adr) + regno, 16962306a36Sopenharmony_ci (red << 16 | green << 8 | blue)); 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci case cmap_avivo: 17262306a36Sopenharmony_ci /* Write to both LUTs for now */ 17362306a36Sopenharmony_ci writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); 17462306a36Sopenharmony_ci writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); 17562306a36Sopenharmony_ci writel(((red) << 22) | ((green) << 12) | ((blue) << 2), 17662306a36Sopenharmony_ci par->cmap_adr + AVIVO_DC_LUT_30_COLOR); 17762306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); 17862306a36Sopenharmony_ci writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); 17962306a36Sopenharmony_ci writel(((red) << 22) | ((green) << 12) | ((blue) << 2), 18062306a36Sopenharmony_ci par->cmap_adr + AVIVO_DC_LUT_30_COLOR); 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* 18862306a36Sopenharmony_ci * Blank the display. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int offb_blank(int blank, struct fb_info *info) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct offb_par *par = (struct offb_par *) info->par; 19462306a36Sopenharmony_ci int i, j; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!par->cmap_adr) 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!par->blanked) 20062306a36Sopenharmony_ci if (!blank) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci par->blanked = blank; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (blank) 20662306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 20762306a36Sopenharmony_ci switch (par->cmap_type) { 20862306a36Sopenharmony_ci case cmap_simple: 20962306a36Sopenharmony_ci writeb(i, par->cmap_adr); 21062306a36Sopenharmony_ci for (j = 0; j < 3; j++) 21162306a36Sopenharmony_ci writeb(0, par->cmap_data); 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci case cmap_M3A: 21462306a36Sopenharmony_ci /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ 21562306a36Sopenharmony_ci out_le32(par->cmap_adr + 0x58, 21662306a36Sopenharmony_ci in_le32(par->cmap_adr + 0x58) & ~0x20); 21762306a36Sopenharmony_ci fallthrough; 21862306a36Sopenharmony_ci case cmap_r128: 21962306a36Sopenharmony_ci /* Set palette index & data */ 22062306a36Sopenharmony_ci out_8(par->cmap_adr + 0xb0, i); 22162306a36Sopenharmony_ci out_le32(par->cmap_adr + 0xb4, 0); 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case cmap_M3B: 22462306a36Sopenharmony_ci /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ 22562306a36Sopenharmony_ci out_le32(par->cmap_adr + 0x58, 22662306a36Sopenharmony_ci in_le32(par->cmap_adr + 0x58) | 0x20); 22762306a36Sopenharmony_ci /* Set palette index & data */ 22862306a36Sopenharmony_ci out_8(par->cmap_adr + 0xb0, i); 22962306a36Sopenharmony_ci out_le32(par->cmap_adr + 0xb4, 0); 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case cmap_radeon: 23262306a36Sopenharmony_ci out_8(par->cmap_adr + 0xb0, i); 23362306a36Sopenharmony_ci out_le32(par->cmap_adr + 0xb4, 0); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case cmap_gxt2000: 23662306a36Sopenharmony_ci out_le32(((unsigned __iomem *) par->cmap_adr) + i, 23762306a36Sopenharmony_ci 0); 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case cmap_avivo: 24062306a36Sopenharmony_ci writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); 24162306a36Sopenharmony_ci writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); 24262306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); 24362306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); 24462306a36Sopenharmony_ci writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); 24562306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } else 24962306a36Sopenharmony_ci fb_set_cmap(&info->cmap, info); 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int offb_set_par(struct fb_info *info) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct offb_par *par = (struct offb_par *) info->par; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* On avivo, initialize palette control */ 25862306a36Sopenharmony_ci if (par->cmap_type == cmap_avivo) { 25962306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL); 26062306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE); 26162306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN); 26262306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED); 26362306a36Sopenharmony_ci writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE); 26462306a36Sopenharmony_ci writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN); 26562306a36Sopenharmony_ci writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED); 26662306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL); 26762306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE); 26862306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN); 26962306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED); 27062306a36Sopenharmony_ci writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE); 27162306a36Sopenharmony_ci writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN); 27262306a36Sopenharmony_ci writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED); 27362306a36Sopenharmony_ci writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); 27462306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); 27562306a36Sopenharmony_ci writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); 27662306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); 27762306a36Sopenharmony_ci writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); 27862306a36Sopenharmony_ci writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void offb_destroy(struct fb_info *info) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct offb_par *par = info->par; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (info->screen_base) 28862306a36Sopenharmony_ci iounmap(info->screen_base); 28962306a36Sopenharmony_ci release_mem_region(par->base, par->size); 29062306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 29162306a36Sopenharmony_ci framebuffer_release(info); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic const struct fb_ops offb_ops = { 29562306a36Sopenharmony_ci .owner = THIS_MODULE, 29662306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 29762306a36Sopenharmony_ci .fb_destroy = offb_destroy, 29862306a36Sopenharmony_ci .fb_setcolreg = offb_setcolreg, 29962306a36Sopenharmony_ci .fb_set_par = offb_set_par, 30062306a36Sopenharmony_ci .fb_blank = offb_blank, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void __iomem *offb_map_reg(struct device_node *np, int index, 30462306a36Sopenharmony_ci unsigned long offset, unsigned long size) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci const __be32 *addrp; 30762306a36Sopenharmony_ci u64 asize, taddr; 30862306a36Sopenharmony_ci unsigned int flags; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci addrp = of_get_pci_address(np, index, &asize, &flags); 31162306a36Sopenharmony_ci if (addrp == NULL) 31262306a36Sopenharmony_ci addrp = of_get_address(np, index, &asize, &flags); 31362306a36Sopenharmony_ci if (addrp == NULL) 31462306a36Sopenharmony_ci return NULL; 31562306a36Sopenharmony_ci if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) 31662306a36Sopenharmony_ci return NULL; 31762306a36Sopenharmony_ci if ((offset + size) > asize) 31862306a36Sopenharmony_ci return NULL; 31962306a36Sopenharmony_ci taddr = of_translate_address(np, addrp); 32062306a36Sopenharmony_ci if (taddr == OF_BAD_ADDR) 32162306a36Sopenharmony_ci return NULL; 32262306a36Sopenharmony_ci return ioremap(taddr + offset, size); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp, 32662306a36Sopenharmony_ci unsigned long address) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct offb_par *par = (struct offb_par *) info->par; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (of_node_name_prefix(dp, "ATY,Rage128")) { 33162306a36Sopenharmony_ci par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); 33262306a36Sopenharmony_ci if (par->cmap_adr) 33362306a36Sopenharmony_ci par->cmap_type = cmap_r128; 33462306a36Sopenharmony_ci } else if (of_node_name_prefix(dp, "ATY,RageM3pA") || 33562306a36Sopenharmony_ci of_node_name_prefix(dp, "ATY,RageM3p12A")) { 33662306a36Sopenharmony_ci par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); 33762306a36Sopenharmony_ci if (par->cmap_adr) 33862306a36Sopenharmony_ci par->cmap_type = cmap_M3A; 33962306a36Sopenharmony_ci } else if (of_node_name_prefix(dp, "ATY,RageM3pB")) { 34062306a36Sopenharmony_ci par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); 34162306a36Sopenharmony_ci if (par->cmap_adr) 34262306a36Sopenharmony_ci par->cmap_type = cmap_M3B; 34362306a36Sopenharmony_ci } else if (of_node_name_prefix(dp, "ATY,Rage6")) { 34462306a36Sopenharmony_ci par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); 34562306a36Sopenharmony_ci if (par->cmap_adr) 34662306a36Sopenharmony_ci par->cmap_type = cmap_radeon; 34762306a36Sopenharmony_ci } else if (of_node_name_prefix(dp, "ATY,")) { 34862306a36Sopenharmony_ci unsigned long base = address & 0xff000000UL; 34962306a36Sopenharmony_ci par->cmap_adr = 35062306a36Sopenharmony_ci ioremap(base + 0x7ff000, 0x1000) + 0xcc0; 35162306a36Sopenharmony_ci par->cmap_data = par->cmap_adr + 1; 35262306a36Sopenharmony_ci par->cmap_type = cmap_simple; 35362306a36Sopenharmony_ci } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") || 35462306a36Sopenharmony_ci of_device_is_compatible(dp, "pci1014,21c"))) { 35562306a36Sopenharmony_ci par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); 35662306a36Sopenharmony_ci if (par->cmap_adr) 35762306a36Sopenharmony_ci par->cmap_type = cmap_gxt2000; 35862306a36Sopenharmony_ci } else if (of_node_name_prefix(dp, "vga,Display-")) { 35962306a36Sopenharmony_ci /* Look for AVIVO initialized by SLOF */ 36062306a36Sopenharmony_ci struct device_node *pciparent = of_get_parent(dp); 36162306a36Sopenharmony_ci const u32 *vid, *did; 36262306a36Sopenharmony_ci vid = of_get_property(pciparent, "vendor-id", NULL); 36362306a36Sopenharmony_ci did = of_get_property(pciparent, "device-id", NULL); 36462306a36Sopenharmony_ci /* This will match most R5xx */ 36562306a36Sopenharmony_ci if (vid && did && *vid == 0x1002 && 36662306a36Sopenharmony_ci ((*did >= 0x7100 && *did < 0x7800) || 36762306a36Sopenharmony_ci (*did >= 0x9400))) { 36862306a36Sopenharmony_ci par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000); 36962306a36Sopenharmony_ci if (par->cmap_adr) 37062306a36Sopenharmony_ci par->cmap_type = cmap_avivo; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci of_node_put(pciparent); 37362306a36Sopenharmony_ci } else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) { 37462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 37562306a36Sopenharmony_ci const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 }; 37662306a36Sopenharmony_ci#else 37762306a36Sopenharmony_ci const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 }; 37862306a36Sopenharmony_ci#endif 37962306a36Sopenharmony_ci u64 io_addr = of_translate_address(dp, io_of_addr); 38062306a36Sopenharmony_ci if (io_addr != OF_BAD_ADDR) { 38162306a36Sopenharmony_ci par->cmap_adr = ioremap(io_addr + 0x3c8, 2); 38262306a36Sopenharmony_ci if (par->cmap_adr) { 38362306a36Sopenharmony_ci par->cmap_type = cmap_simple; 38462306a36Sopenharmony_ci par->cmap_data = par->cmap_adr + 1; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci info->fix.visual = (par->cmap_type != cmap_unknown) ? 38962306a36Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void offb_init_fb(struct platform_device *parent, const char *name, 39362306a36Sopenharmony_ci int width, int height, int depth, 39462306a36Sopenharmony_ci int pitch, unsigned long address, 39562306a36Sopenharmony_ci int foreign_endian, struct device_node *dp) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci unsigned long res_size = pitch * height; 39862306a36Sopenharmony_ci unsigned long res_start = address; 39962306a36Sopenharmony_ci struct fb_fix_screeninfo *fix; 40062306a36Sopenharmony_ci struct fb_var_screeninfo *var; 40162306a36Sopenharmony_ci struct fb_info *info; 40262306a36Sopenharmony_ci struct offb_par *par; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!request_mem_region(res_start, res_size, "offb")) 40562306a36Sopenharmony_ci return; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci printk(KERN_INFO 40862306a36Sopenharmony_ci "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n", 40962306a36Sopenharmony_ci width, height, name, address, depth, pitch); 41062306a36Sopenharmony_ci if (depth != 8 && depth != 15 && depth != 16 && depth != 32) { 41162306a36Sopenharmony_ci printk(KERN_ERR "%pOF: can't use depth = %d\n", dp, depth); 41262306a36Sopenharmony_ci release_mem_region(res_start, res_size); 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(*par), &parent->dev); 41762306a36Sopenharmony_ci if (!info) { 41862306a36Sopenharmony_ci release_mem_region(res_start, res_size); 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci platform_set_drvdata(parent, info); 42262306a36Sopenharmony_ci par = info->par; 42362306a36Sopenharmony_ci fix = &info->fix; 42462306a36Sopenharmony_ci var = &info->var; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (name) { 42762306a36Sopenharmony_ci strcpy(fix->id, "OFfb "); 42862306a36Sopenharmony_ci strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb ")); 42962306a36Sopenharmony_ci fix->id[sizeof(fix->id) - 1] = '\0'; 43062306a36Sopenharmony_ci } else 43162306a36Sopenharmony_ci snprintf(fix->id, sizeof(fix->id), "OFfb %pOFn", dp); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci var->xres = var->xres_virtual = width; 43562306a36Sopenharmony_ci var->yres = var->yres_virtual = height; 43662306a36Sopenharmony_ci fix->line_length = pitch; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci fix->smem_start = address; 43962306a36Sopenharmony_ci fix->smem_len = pitch * height; 44062306a36Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 44162306a36Sopenharmony_ci fix->type_aux = 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci par->cmap_type = cmap_unknown; 44462306a36Sopenharmony_ci if (depth == 8) 44562306a36Sopenharmony_ci offb_init_palette_hacks(info, dp, address); 44662306a36Sopenharmony_ci else 44762306a36Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci var->xoffset = var->yoffset = 0; 45062306a36Sopenharmony_ci switch (depth) { 45162306a36Sopenharmony_ci case 8: 45262306a36Sopenharmony_ci var->bits_per_pixel = 8; 45362306a36Sopenharmony_ci var->red.offset = 0; 45462306a36Sopenharmony_ci var->red.length = 8; 45562306a36Sopenharmony_ci var->green.offset = 0; 45662306a36Sopenharmony_ci var->green.length = 8; 45762306a36Sopenharmony_ci var->blue.offset = 0; 45862306a36Sopenharmony_ci var->blue.length = 8; 45962306a36Sopenharmony_ci var->transp.offset = 0; 46062306a36Sopenharmony_ci var->transp.length = 0; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case 15: /* RGB 555 */ 46362306a36Sopenharmony_ci var->bits_per_pixel = 16; 46462306a36Sopenharmony_ci var->red.offset = 10; 46562306a36Sopenharmony_ci var->red.length = 5; 46662306a36Sopenharmony_ci var->green.offset = 5; 46762306a36Sopenharmony_ci var->green.length = 5; 46862306a36Sopenharmony_ci var->blue.offset = 0; 46962306a36Sopenharmony_ci var->blue.length = 5; 47062306a36Sopenharmony_ci var->transp.offset = 0; 47162306a36Sopenharmony_ci var->transp.length = 0; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case 16: /* RGB 565 */ 47462306a36Sopenharmony_ci var->bits_per_pixel = 16; 47562306a36Sopenharmony_ci var->red.offset = 11; 47662306a36Sopenharmony_ci var->red.length = 5; 47762306a36Sopenharmony_ci var->green.offset = 5; 47862306a36Sopenharmony_ci var->green.length = 6; 47962306a36Sopenharmony_ci var->blue.offset = 0; 48062306a36Sopenharmony_ci var->blue.length = 5; 48162306a36Sopenharmony_ci var->transp.offset = 0; 48262306a36Sopenharmony_ci var->transp.length = 0; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case 32: /* RGB 888 */ 48562306a36Sopenharmony_ci var->bits_per_pixel = 32; 48662306a36Sopenharmony_ci var->red.offset = 16; 48762306a36Sopenharmony_ci var->red.length = 8; 48862306a36Sopenharmony_ci var->green.offset = 8; 48962306a36Sopenharmony_ci var->green.length = 8; 49062306a36Sopenharmony_ci var->blue.offset = 0; 49162306a36Sopenharmony_ci var->blue.length = 8; 49262306a36Sopenharmony_ci var->transp.offset = 24; 49362306a36Sopenharmony_ci var->transp.length = 8; 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci var->red.msb_right = var->green.msb_right = var->blue.msb_right = 49762306a36Sopenharmony_ci var->transp.msb_right = 0; 49862306a36Sopenharmony_ci var->grayscale = 0; 49962306a36Sopenharmony_ci var->nonstd = 0; 50062306a36Sopenharmony_ci var->activate = 0; 50162306a36Sopenharmony_ci var->height = var->width = -1; 50262306a36Sopenharmony_ci var->pixclock = 10000; 50362306a36Sopenharmony_ci var->left_margin = var->right_margin = 16; 50462306a36Sopenharmony_ci var->upper_margin = var->lower_margin = 16; 50562306a36Sopenharmony_ci var->hsync_len = var->vsync_len = 8; 50662306a36Sopenharmony_ci var->sync = 0; 50762306a36Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci par->base = address; 51062306a36Sopenharmony_ci par->size = fix->smem_len; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci info->fbops = &offb_ops; 51362306a36Sopenharmony_ci info->screen_base = ioremap(address, fix->smem_len); 51462306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 51562306a36Sopenharmony_ci info->flags = foreign_endian; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci fb_alloc_cmap(&info->cmap, 256, 0); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (devm_aperture_acquire_for_platform_device(parent, par->base, par->size) < 0) 52062306a36Sopenharmony_ci goto out_err; 52162306a36Sopenharmony_ci if (register_framebuffer(info) < 0) 52262306a36Sopenharmony_ci goto out_err; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci fb_info(info, "Open Firmware frame buffer device on %pOF\n", dp); 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciout_err: 52862306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 52962306a36Sopenharmony_ci iounmap(info->screen_base); 53062306a36Sopenharmony_ci iounmap(par->cmap_adr); 53162306a36Sopenharmony_ci par->cmap_adr = NULL; 53262306a36Sopenharmony_ci framebuffer_release(info); 53362306a36Sopenharmony_ci release_mem_region(res_start, res_size); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic void offb_init_nodriver(struct platform_device *parent, struct device_node *dp, 53862306a36Sopenharmony_ci int no_real_node) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci unsigned int len; 54162306a36Sopenharmony_ci int i, width = 640, height = 480, depth = 8, pitch = 640; 54262306a36Sopenharmony_ci unsigned int flags, rsize, addr_prop = 0; 54362306a36Sopenharmony_ci unsigned long max_size = 0; 54462306a36Sopenharmony_ci u64 rstart, address = OF_BAD_ADDR; 54562306a36Sopenharmony_ci const __be32 *pp, *addrp, *up; 54662306a36Sopenharmony_ci u64 asize; 54762306a36Sopenharmony_ci int foreign_endian = 0; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 55062306a36Sopenharmony_ci if (of_property_read_bool(dp, "little-endian")) 55162306a36Sopenharmony_ci foreign_endian = FBINFO_FOREIGN_ENDIAN; 55262306a36Sopenharmony_ci#else 55362306a36Sopenharmony_ci if (of_property_read_bool(dp, "big-endian")) 55462306a36Sopenharmony_ci foreign_endian = FBINFO_FOREIGN_ENDIAN; 55562306a36Sopenharmony_ci#endif 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci pp = of_get_property(dp, "linux,bootx-depth", &len); 55862306a36Sopenharmony_ci if (pp == NULL) 55962306a36Sopenharmony_ci pp = of_get_property(dp, "depth", &len); 56062306a36Sopenharmony_ci if (pp && len == sizeof(u32)) 56162306a36Sopenharmony_ci depth = be32_to_cpup(pp); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci pp = of_get_property(dp, "linux,bootx-width", &len); 56462306a36Sopenharmony_ci if (pp == NULL) 56562306a36Sopenharmony_ci pp = of_get_property(dp, "width", &len); 56662306a36Sopenharmony_ci if (pp && len == sizeof(u32)) 56762306a36Sopenharmony_ci width = be32_to_cpup(pp); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci pp = of_get_property(dp, "linux,bootx-height", &len); 57062306a36Sopenharmony_ci if (pp == NULL) 57162306a36Sopenharmony_ci pp = of_get_property(dp, "height", &len); 57262306a36Sopenharmony_ci if (pp && len == sizeof(u32)) 57362306a36Sopenharmony_ci height = be32_to_cpup(pp); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci pp = of_get_property(dp, "linux,bootx-linebytes", &len); 57662306a36Sopenharmony_ci if (pp == NULL) 57762306a36Sopenharmony_ci pp = of_get_property(dp, "linebytes", &len); 57862306a36Sopenharmony_ci if (pp && len == sizeof(u32) && (*pp != 0xffffffffu)) 57962306a36Sopenharmony_ci pitch = be32_to_cpup(pp); 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci pitch = width * ((depth + 7) / 8); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci rsize = (unsigned long)pitch * (unsigned long)height; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Ok, now we try to figure out the address of the framebuffer. 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * Unfortunately, Open Firmware doesn't provide a standard way to do 58862306a36Sopenharmony_ci * so. All we can do is a dodgy heuristic that happens to work in 58962306a36Sopenharmony_ci * practice. On most machines, the "address" property contains what 59062306a36Sopenharmony_ci * we need, though not on Matrox cards found in IBM machines. What I've 59162306a36Sopenharmony_ci * found that appears to give good results is to go through the PCI 59262306a36Sopenharmony_ci * ranges and pick one that is both big enough and if possible encloses 59362306a36Sopenharmony_ci * the "address" property. If none match, we pick the biggest 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci up = of_get_property(dp, "linux,bootx-addr", &len); 59662306a36Sopenharmony_ci if (up == NULL) 59762306a36Sopenharmony_ci up = of_get_property(dp, "address", &len); 59862306a36Sopenharmony_ci if (up && len == sizeof(u32)) 59962306a36Sopenharmony_ci addr_prop = *up; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Hack for when BootX is passing us */ 60262306a36Sopenharmony_ci if (no_real_node) 60362306a36Sopenharmony_ci goto skip_addr; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) 60662306a36Sopenharmony_ci != NULL; i++) { 60762306a36Sopenharmony_ci int match_addrp = 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!(flags & IORESOURCE_MEM)) 61062306a36Sopenharmony_ci continue; 61162306a36Sopenharmony_ci if (asize < rsize) 61262306a36Sopenharmony_ci continue; 61362306a36Sopenharmony_ci rstart = of_translate_address(dp, addrp); 61462306a36Sopenharmony_ci if (rstart == OF_BAD_ADDR) 61562306a36Sopenharmony_ci continue; 61662306a36Sopenharmony_ci if (addr_prop && (rstart <= addr_prop) && 61762306a36Sopenharmony_ci ((rstart + asize) >= (addr_prop + rsize))) 61862306a36Sopenharmony_ci match_addrp = 1; 61962306a36Sopenharmony_ci if (match_addrp) { 62062306a36Sopenharmony_ci address = addr_prop; 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci if (rsize > max_size) { 62462306a36Sopenharmony_ci max_size = rsize; 62562306a36Sopenharmony_ci address = OF_BAD_ADDR; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (address == OF_BAD_ADDR) 62962306a36Sopenharmony_ci address = rstart; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci skip_addr: 63262306a36Sopenharmony_ci if (address == OF_BAD_ADDR && addr_prop) 63362306a36Sopenharmony_ci address = (u64)addr_prop; 63462306a36Sopenharmony_ci if (address != OF_BAD_ADDR) { 63562306a36Sopenharmony_ci#ifdef CONFIG_PCI 63662306a36Sopenharmony_ci const __be32 *vidp, *didp; 63762306a36Sopenharmony_ci u32 vid, did; 63862306a36Sopenharmony_ci struct pci_dev *pdev; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci vidp = of_get_property(dp, "vendor-id", NULL); 64162306a36Sopenharmony_ci didp = of_get_property(dp, "device-id", NULL); 64262306a36Sopenharmony_ci if (vidp && didp) { 64362306a36Sopenharmony_ci vid = be32_to_cpup(vidp); 64462306a36Sopenharmony_ci did = be32_to_cpup(didp); 64562306a36Sopenharmony_ci pdev = pci_get_device(vid, did, NULL); 64662306a36Sopenharmony_ci if (!pdev || pci_enable_device(pdev)) 64762306a36Sopenharmony_ci return; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci#endif 65062306a36Sopenharmony_ci /* kludge for valkyrie */ 65162306a36Sopenharmony_ci if (of_node_name_eq(dp, "valkyrie")) 65262306a36Sopenharmony_ci address += 0x1000; 65362306a36Sopenharmony_ci offb_init_fb(parent, no_real_node ? "bootx" : NULL, 65462306a36Sopenharmony_ci width, height, depth, pitch, address, 65562306a36Sopenharmony_ci foreign_endian, no_real_node ? NULL : dp); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void offb_remove(struct platform_device *pdev) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (info) 66462306a36Sopenharmony_ci unregister_framebuffer(info); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic int offb_probe_bootx_noscreen(struct platform_device *pdev) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci offb_init_nodriver(pdev, of_chosen, 1); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic struct platform_driver offb_driver_bootx_noscreen = { 67562306a36Sopenharmony_ci .driver = { 67662306a36Sopenharmony_ci .name = "bootx-noscreen", 67762306a36Sopenharmony_ci }, 67862306a36Sopenharmony_ci .probe = offb_probe_bootx_noscreen, 67962306a36Sopenharmony_ci .remove_new = offb_remove, 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int offb_probe_display(struct platform_device *pdev) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci offb_init_nodriver(pdev, pdev->dev.of_node, 0); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic const struct of_device_id offb_of_match_display[] = { 69062306a36Sopenharmony_ci { .compatible = "display", }, 69162306a36Sopenharmony_ci { }, 69262306a36Sopenharmony_ci}; 69362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, offb_of_match_display); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic struct platform_driver offb_driver_display = { 69662306a36Sopenharmony_ci .driver = { 69762306a36Sopenharmony_ci .name = "of-display", 69862306a36Sopenharmony_ci .of_match_table = offb_of_match_display, 69962306a36Sopenharmony_ci }, 70062306a36Sopenharmony_ci .probe = offb_probe_display, 70162306a36Sopenharmony_ci .remove_new = offb_remove, 70262306a36Sopenharmony_ci}; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int __init offb_init(void) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci if (fb_get_options("offb", NULL)) 70762306a36Sopenharmony_ci return -ENODEV; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci platform_driver_register(&offb_driver_bootx_noscreen); 71062306a36Sopenharmony_ci platform_driver_register(&offb_driver_display); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_cimodule_init(offb_init); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void __exit offb_exit(void) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci platform_driver_unregister(&offb_driver_display); 71962306a36Sopenharmony_ci platform_driver_unregister(&offb_driver_bootx_noscreen); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_cimodule_exit(offb_exit); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 724