18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * platinumfb.c -- frame buffer device for the PowerMac 'platinum' display 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1998 Franz Sirl 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Frame buffer structure from: 78c2ecf20Sopenharmony_ci * drivers/video/controlfb.c -- frame buffer device for 88c2ecf20Sopenharmony_ci * Apple 'control' display chip. 98c2ecf20Sopenharmony_ci * Copyright (C) 1998 Dan Jacobowitz 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Hardware information from: 128c2ecf20Sopenharmony_ci * platinum.c: Console support for PowerMac "platinum" display adaptor. 138c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras and Mark Abene 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 168c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 178c2ecf20Sopenharmony_ci * more details. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#undef DEBUG 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/errno.h> 258c2ecf20Sopenharmony_ci#include <linux/string.h> 268c2ecf20Sopenharmony_ci#include <linux/mm.h> 278c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 288c2ecf20Sopenharmony_ci#include <linux/delay.h> 298c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 308c2ecf20Sopenharmony_ci#include <linux/fb.h> 318c2ecf20Sopenharmony_ci#include <linux/init.h> 328c2ecf20Sopenharmony_ci#include <linux/nvram.h> 338c2ecf20Sopenharmony_ci#include <linux/of_device.h> 348c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 358c2ecf20Sopenharmony_ci#include <asm/prom.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "macmodes.h" 388c2ecf20Sopenharmony_ci#include "platinumfb.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int default_vmode = VMODE_NVRAM; 418c2ecf20Sopenharmony_cistatic int default_cmode = CMODE_NVRAM; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct fb_info_platinum { 448c2ecf20Sopenharmony_ci struct fb_info *info; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci int vmode, cmode; 478c2ecf20Sopenharmony_ci int xres, yres; 488c2ecf20Sopenharmony_ci int vxres, vyres; 498c2ecf20Sopenharmony_ci int xoffset, yoffset; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci struct { 528c2ecf20Sopenharmony_ci __u8 red, green, blue; 538c2ecf20Sopenharmony_ci } palette[256]; 548c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci volatile struct cmap_regs __iomem *cmap_regs; 578c2ecf20Sopenharmony_ci unsigned long cmap_regs_phys; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci volatile struct platinum_regs __iomem *platinum_regs; 608c2ecf20Sopenharmony_ci unsigned long platinum_regs_phys; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci __u8 __iomem *frame_buffer; 638c2ecf20Sopenharmony_ci volatile __u8 __iomem *base_frame_buffer; 648c2ecf20Sopenharmony_ci unsigned long frame_buffer_phys; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci unsigned long total_vram; 678c2ecf20Sopenharmony_ci int clktype; 688c2ecf20Sopenharmony_ci int dactype; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct resource rsrc_fb, rsrc_reg; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* 748c2ecf20Sopenharmony_ci * Frame buffer device API 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 788c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info); 798c2ecf20Sopenharmony_cistatic int platinumfb_blank(int blank_mode, struct fb_info *info); 808c2ecf20Sopenharmony_cistatic int platinumfb_set_par (struct fb_info *info); 818c2ecf20Sopenharmony_cistatic int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * internal functions 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline int platinum_vram_reqd(int video_mode, int color_mode); 888c2ecf20Sopenharmony_cistatic int read_platinum_sense(struct fb_info_platinum *pinfo); 898c2ecf20Sopenharmony_cistatic void set_platinum_clock(struct fb_info_platinum *pinfo); 908c2ecf20Sopenharmony_cistatic void platinum_set_hardware(struct fb_info_platinum *pinfo); 918c2ecf20Sopenharmony_cistatic int platinum_var_to_par(struct fb_var_screeninfo *var, 928c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo, 938c2ecf20Sopenharmony_ci int check_only); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Interface used by the world 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct fb_ops platinumfb_ops = { 1008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1018c2ecf20Sopenharmony_ci .fb_check_var = platinumfb_check_var, 1028c2ecf20Sopenharmony_ci .fb_set_par = platinumfb_set_par, 1038c2ecf20Sopenharmony_ci .fb_setcolreg = platinumfb_setcolreg, 1048c2ecf20Sopenharmony_ci .fb_blank = platinumfb_blank, 1058c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 1068c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 1078c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * Checks a var structure 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci return platinum_var_to_par(var, info->par, 1); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* 1198c2ecf20Sopenharmony_ci * Applies current var to display 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic int platinumfb_set_par (struct fb_info *info) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo = info->par; 1248c2ecf20Sopenharmony_ci struct platinum_regvals *init; 1258c2ecf20Sopenharmony_ci int err, offset = 0x20; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if((err = platinum_var_to_par(&info->var, pinfo, 0))) { 1288c2ecf20Sopenharmony_ci printk (KERN_ERR "platinumfb_set_par: error calling" 1298c2ecf20Sopenharmony_ci " platinum_var_to_par: %d.\n", err); 1308c2ecf20Sopenharmony_ci return err; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci platinum_set_hardware(pinfo); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci init = platinum_reg_init[pinfo->vmode-1]; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if ((pinfo->vmode == VMODE_832_624_75) && (pinfo->cmode > CMODE_8)) 1388c2ecf20Sopenharmony_ci offset = 0x10; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci info->screen_base = pinfo->frame_buffer + init->fb_offset + offset; 1418c2ecf20Sopenharmony_ci mutex_lock(&info->mm_lock); 1428c2ecf20Sopenharmony_ci info->fix.smem_start = (pinfo->frame_buffer_phys) + init->fb_offset + offset; 1438c2ecf20Sopenharmony_ci mutex_unlock(&info->mm_lock); 1448c2ecf20Sopenharmony_ci info->fix.visual = (pinfo->cmode == CMODE_8) ? 1458c2ecf20Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; 1468c2ecf20Sopenharmony_ci info->fix.line_length = vmode_attrs[pinfo->vmode-1].hres * (1<<pinfo->cmode) 1478c2ecf20Sopenharmony_ci + offset; 1488c2ecf20Sopenharmony_ci printk("line_length: %x\n", info->fix.line_length); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int platinumfb_blank(int blank, struct fb_info *fb) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * Blank the screen if blank_mode != 0, else unblank. If blank == NULL 1568c2ecf20Sopenharmony_ci * then the caller blanks by setting the CLUT (Color Look Up Table) to all 1578c2ecf20Sopenharmony_ci * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due 1588c2ecf20Sopenharmony_ci * to e.g. a video mode which doesn't support it. Implements VESA suspend 1598c2ecf20Sopenharmony_ci * and powerdown modes on hardware that supports disabling hsync/vsync: 1608c2ecf20Sopenharmony_ci * blank_mode == 2: suspend vsync 1618c2ecf20Sopenharmony_ci * blank_mode == 3: suspend hsync 1628c2ecf20Sopenharmony_ci * blank_mode == 4: powerdown 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci/* [danj] I think there's something fishy about those constants... */ 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci struct fb_info_platinum *info = (struct fb_info_platinum *) fb; 1678c2ecf20Sopenharmony_ci int ctrl; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ctrl = le32_to_cpup(&info->platinum_regs->ctrl.r) | 0x33; 1708c2ecf20Sopenharmony_ci if (blank) 1718c2ecf20Sopenharmony_ci --blank_mode; 1728c2ecf20Sopenharmony_ci if (blank & VESA_VSYNC_SUSPEND) 1738c2ecf20Sopenharmony_ci ctrl &= ~3; 1748c2ecf20Sopenharmony_ci if (blank & VESA_HSYNC_SUSPEND) 1758c2ecf20Sopenharmony_ci ctrl &= ~0x30; 1768c2ecf20Sopenharmony_ci out_le32(&info->platinum_regs->ctrl.r, ctrl); 1778c2ecf20Sopenharmony_ci*/ 1788c2ecf20Sopenharmony_ci/* TODO: Figure out how the heck to powerdown this thing! */ 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 1838c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo = info->par; 1868c2ecf20Sopenharmony_ci volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (regno > 255) 1898c2ecf20Sopenharmony_ci return 1; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci red >>= 8; 1928c2ecf20Sopenharmony_ci green >>= 8; 1938c2ecf20Sopenharmony_ci blue >>= 8; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci pinfo->palette[regno].red = red; 1968c2ecf20Sopenharmony_ci pinfo->palette[regno].green = green; 1978c2ecf20Sopenharmony_ci pinfo->palette[regno].blue = blue; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci out_8(&cmap_regs->addr, regno); /* tell clut what addr to fill */ 2008c2ecf20Sopenharmony_ci out_8(&cmap_regs->lut, red); /* send one color channel at */ 2018c2ecf20Sopenharmony_ci out_8(&cmap_regs->lut, green); /* a time... */ 2028c2ecf20Sopenharmony_ci out_8(&cmap_regs->lut, blue); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (regno < 16) { 2058c2ecf20Sopenharmony_ci int i; 2068c2ecf20Sopenharmony_ci u32 *pal = info->pseudo_palette; 2078c2ecf20Sopenharmony_ci switch (pinfo->cmode) { 2088c2ecf20Sopenharmony_ci case CMODE_16: 2098c2ecf20Sopenharmony_ci pal[regno] = (regno << 10) | (regno << 5) | regno; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci case CMODE_32: 2128c2ecf20Sopenharmony_ci i = (regno << 8) | regno; 2138c2ecf20Sopenharmony_ci pal[regno] = (i << 16) | i; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic inline int platinum_vram_reqd(int video_mode, int color_mode) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int baseval = vmode_attrs[video_mode-1].hres * (1<<color_mode); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if ((video_mode == VMODE_832_624_75) && (color_mode > CMODE_8)) 2268c2ecf20Sopenharmony_ci baseval += 0x10; 2278c2ecf20Sopenharmony_ci else 2288c2ecf20Sopenharmony_ci baseval += 0x20; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return vmode_attrs[video_mode-1].vres * baseval + 0x1000; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define STORE_D2(a, d) { \ 2348c2ecf20Sopenharmony_ci out_8(&cmap_regs->addr, (a+32)); \ 2358c2ecf20Sopenharmony_ci out_8(&cmap_regs->d2, (d)); \ 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void set_platinum_clock(struct fb_info_platinum *pinfo) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs; 2418c2ecf20Sopenharmony_ci struct platinum_regvals *init; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci init = platinum_reg_init[pinfo->vmode-1]; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci STORE_D2(6, 0xc6); 2468c2ecf20Sopenharmony_ci out_8(&cmap_regs->addr,3+32); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (in_8(&cmap_regs->d2) == 2) { 2498c2ecf20Sopenharmony_ci STORE_D2(7, init->clock_params[pinfo->clktype][0]); 2508c2ecf20Sopenharmony_ci STORE_D2(8, init->clock_params[pinfo->clktype][1]); 2518c2ecf20Sopenharmony_ci STORE_D2(3, 3); 2528c2ecf20Sopenharmony_ci } else { 2538c2ecf20Sopenharmony_ci STORE_D2(4, init->clock_params[pinfo->clktype][0]); 2548c2ecf20Sopenharmony_ci STORE_D2(5, init->clock_params[pinfo->clktype][1]); 2558c2ecf20Sopenharmony_ci STORE_D2(3, 2); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci __delay(5000); 2598c2ecf20Sopenharmony_ci STORE_D2(9, 0xa6); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* Now how about actually saying, Make it so! */ 2648c2ecf20Sopenharmony_ci/* Some things in here probably don't need to be done each time. */ 2658c2ecf20Sopenharmony_cistatic void platinum_set_hardware(struct fb_info_platinum *pinfo) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci volatile struct platinum_regs __iomem *platinum_regs = pinfo->platinum_regs; 2688c2ecf20Sopenharmony_ci volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs; 2698c2ecf20Sopenharmony_ci struct platinum_regvals *init; 2708c2ecf20Sopenharmony_ci int i; 2718c2ecf20Sopenharmony_ci int vmode, cmode; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci vmode = pinfo->vmode; 2748c2ecf20Sopenharmony_ci cmode = pinfo->cmode; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci init = platinum_reg_init[vmode - 1]; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Initialize display timing registers */ 2798c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[24].r, 7); /* turn display off */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci for (i = 0; i < 26; ++i) 2828c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[i+32].r, init->regs[i]); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[26+32].r, (pinfo->total_vram == 0x100000 ? 2858c2ecf20Sopenharmony_ci init->offset[cmode] + 4 - cmode : 2868c2ecf20Sopenharmony_ci init->offset[cmode])); 2878c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[16].r, (unsigned) pinfo->frame_buffer_phys+init->fb_offset+0x10); 2888c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]); 2898c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[19].r, (pinfo->total_vram == 0x100000 ? 2908c2ecf20Sopenharmony_ci init->mode[cmode+1] : 2918c2ecf20Sopenharmony_ci init->mode[cmode])); 2928c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[20].r, (pinfo->total_vram == 0x100000 ? 0x11 : 0x1011)); 2938c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[21].r, 0x100); 2948c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[22].r, 1); 2958c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[23].r, 1); 2968c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[26].r, 0xc00); 2978c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[27].r, 0x235); 2988c2ecf20Sopenharmony_ci /* out_be32(&platinum_regs->reg[27].r, 0x2aa); */ 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci STORE_D2(0, (pinfo->total_vram == 0x100000 ? 3018c2ecf20Sopenharmony_ci init->dacula_ctrl[cmode] & 0xf : 3028c2ecf20Sopenharmony_ci init->dacula_ctrl[cmode])); 3038c2ecf20Sopenharmony_ci STORE_D2(1, 4); 3048c2ecf20Sopenharmony_ci STORE_D2(2, 0); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci set_platinum_clock(pinfo); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[24].r, 0); /* turn display on */ 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* 3128c2ecf20Sopenharmony_ci * Set misc info vars for this driver 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_cistatic void platinum_init_info(struct fb_info *info, 3158c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci /* Fill fb_info */ 3188c2ecf20Sopenharmony_ci info->fbops = &platinumfb_ops; 3198c2ecf20Sopenharmony_ci info->pseudo_palette = pinfo->pseudo_palette; 3208c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 3218c2ecf20Sopenharmony_ci info->screen_base = pinfo->frame_buffer + 0x20; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci fb_alloc_cmap(&info->cmap, 256, 0); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* Fill fix common fields */ 3268c2ecf20Sopenharmony_ci strcpy(info->fix.id, "platinum"); 3278c2ecf20Sopenharmony_ci info->fix.mmio_start = pinfo->platinum_regs_phys; 3288c2ecf20Sopenharmony_ci info->fix.mmio_len = 0x1000; 3298c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 3308c2ecf20Sopenharmony_ci info->fix.smem_start = pinfo->frame_buffer_phys + 0x20; /* will be updated later */ 3318c2ecf20Sopenharmony_ci info->fix.smem_len = pinfo->total_vram - 0x20; 3328c2ecf20Sopenharmony_ci info->fix.ywrapstep = 0; 3338c2ecf20Sopenharmony_ci info->fix.xpanstep = 0; 3348c2ecf20Sopenharmony_ci info->fix.ypanstep = 0; 3358c2ecf20Sopenharmony_ci info->fix.type_aux = 0; 3368c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int platinum_init_fb(struct fb_info *info) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo = info->par; 3438c2ecf20Sopenharmony_ci struct fb_var_screeninfo var; 3448c2ecf20Sopenharmony_ci int sense, rc; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci sense = read_platinum_sense(pinfo); 3478c2ecf20Sopenharmony_ci printk(KERN_INFO "platinumfb: Monitor sense value = 0x%x, ", sense); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (IS_REACHABLE(CONFIG_NVRAM) && default_vmode == VMODE_NVRAM) 3508c2ecf20Sopenharmony_ci default_vmode = nvram_read_byte(NV_VMODE); 3518c2ecf20Sopenharmony_ci if (default_vmode <= 0 || default_vmode > VMODE_MAX || 3528c2ecf20Sopenharmony_ci !platinum_reg_init[default_vmode - 1]) { 3538c2ecf20Sopenharmony_ci default_vmode = mac_map_monitor_sense(sense); 3548c2ecf20Sopenharmony_ci if (!platinum_reg_init[default_vmode - 1]) 3558c2ecf20Sopenharmony_ci default_vmode = VMODE_640_480_60; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (IS_REACHABLE(CONFIG_NVRAM) && default_cmode == CMODE_NVRAM) 3598c2ecf20Sopenharmony_ci default_cmode = nvram_read_byte(NV_CMODE); 3608c2ecf20Sopenharmony_ci if (default_cmode < CMODE_8 || default_cmode > CMODE_32) 3618c2ecf20Sopenharmony_ci default_cmode = CMODE_8; 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * Reduce the pixel size if we don't have enough VRAM. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci while(default_cmode > CMODE_8 && 3668c2ecf20Sopenharmony_ci platinum_vram_reqd(default_vmode, default_cmode) > pinfo->total_vram) 3678c2ecf20Sopenharmony_ci default_cmode--; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci printk("platinumfb: Using video mode %d and color mode %d.\n", default_vmode, default_cmode); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Setup default var */ 3728c2ecf20Sopenharmony_ci if (mac_vmode_to_var(default_vmode, default_cmode, &var) < 0) { 3738c2ecf20Sopenharmony_ci /* This shouldn't happen! */ 3748c2ecf20Sopenharmony_ci printk("mac_vmode_to_var(%d, %d,) failed\n", default_vmode, default_cmode); 3758c2ecf20Sopenharmony_citry_again: 3768c2ecf20Sopenharmony_ci default_vmode = VMODE_640_480_60; 3778c2ecf20Sopenharmony_ci default_cmode = CMODE_8; 3788c2ecf20Sopenharmony_ci if (mac_vmode_to_var(default_vmode, default_cmode, &var) < 0) { 3798c2ecf20Sopenharmony_ci printk(KERN_ERR "platinumfb: mac_vmode_to_var() failed\n"); 3808c2ecf20Sopenharmony_ci return -ENXIO; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* Initialize info structure */ 3858c2ecf20Sopenharmony_ci platinum_init_info(info, pinfo); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Apply default var */ 3888c2ecf20Sopenharmony_ci info->var = var; 3898c2ecf20Sopenharmony_ci var.activate = FB_ACTIVATE_NOW; 3908c2ecf20Sopenharmony_ci rc = fb_set_var(info, &var); 3918c2ecf20Sopenharmony_ci if (rc && (default_vmode != VMODE_640_480_60 || default_cmode != CMODE_8)) 3928c2ecf20Sopenharmony_ci goto try_again; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Register with fbdev layer */ 3958c2ecf20Sopenharmony_ci rc = register_framebuffer(info); 3968c2ecf20Sopenharmony_ci if (rc < 0) 3978c2ecf20Sopenharmony_ci return rc; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci fb_info(info, "Apple Platinum frame buffer device\n"); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* 4058c2ecf20Sopenharmony_ci * Get the monitor sense value. 4068c2ecf20Sopenharmony_ci * Note that this can be called before calibrate_delay, 4078c2ecf20Sopenharmony_ci * so we can't use udelay. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_cistatic int read_platinum_sense(struct fb_info_platinum *info) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci volatile struct platinum_regs __iomem *platinum_regs = info->platinum_regs; 4128c2ecf20Sopenharmony_ci int sense; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[23].r, 7); /* turn off drivers */ 4158c2ecf20Sopenharmony_ci __delay(2000); 4168c2ecf20Sopenharmony_ci sense = (~in_be32(&platinum_regs->reg[23].r) & 7) << 8; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* drive each sense line low in turn and collect the other 2 */ 4198c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[23].r, 3); /* drive A low */ 4208c2ecf20Sopenharmony_ci __delay(2000); 4218c2ecf20Sopenharmony_ci sense |= (~in_be32(&platinum_regs->reg[23].r) & 3) << 4; 4228c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[23].r, 5); /* drive B low */ 4238c2ecf20Sopenharmony_ci __delay(2000); 4248c2ecf20Sopenharmony_ci sense |= (~in_be32(&platinum_regs->reg[23].r) & 4) << 1; 4258c2ecf20Sopenharmony_ci sense |= (~in_be32(&platinum_regs->reg[23].r) & 1) << 2; 4268c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[23].r, 6); /* drive C low */ 4278c2ecf20Sopenharmony_ci __delay(2000); 4288c2ecf20Sopenharmony_ci sense |= (~in_be32(&platinum_regs->reg[23].r) & 6) >> 1; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci out_be32(&platinum_regs->reg[23].r, 7); /* turn off drivers */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return sense; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/* 4368c2ecf20Sopenharmony_ci * This routine takes a user-supplied var, and picks the best vmode/cmode from it. 4378c2ecf20Sopenharmony_ci * It also updates the var structure to the actual mode data obtained 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic int platinum_var_to_par(struct fb_var_screeninfo *var, 4408c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo, 4418c2ecf20Sopenharmony_ci int check_only) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int vmode, cmode; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (mac_var_to_vmode(var, &vmode, &cmode) != 0) { 4468c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: mac_var_to_vmode unsuccessful.\n"); 4478c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->xres = %d\n", var->xres); 4488c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->yres = %d\n", var->yres); 4498c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->xres_virtual = %d\n", var->xres_virtual); 4508c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->yres_virtual = %d\n", var->yres_virtual); 4518c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->bits_per_pixel = %d\n", var->bits_per_pixel); 4528c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->pixclock = %d\n", var->pixclock); 4538c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par: var->vmode = %d\n", var->vmode); 4548c2ecf20Sopenharmony_ci return -EINVAL; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (!platinum_reg_init[vmode-1]) { 4588c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par, vmode %d not valid.\n", vmode); 4598c2ecf20Sopenharmony_ci return -EINVAL; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (platinum_vram_reqd(vmode, cmode) > pinfo->total_vram) { 4638c2ecf20Sopenharmony_ci printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", vmode, cmode); 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (mac_vmode_to_var(vmode, cmode, var)) 4688c2ecf20Sopenharmony_ci return -EINVAL; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (check_only) 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pinfo->vmode = vmode; 4748c2ecf20Sopenharmony_ci pinfo->cmode = cmode; 4758c2ecf20Sopenharmony_ci pinfo->xres = vmode_attrs[vmode-1].hres; 4768c2ecf20Sopenharmony_ci pinfo->yres = vmode_attrs[vmode-1].vres; 4778c2ecf20Sopenharmony_ci pinfo->xoffset = 0; 4788c2ecf20Sopenharmony_ci pinfo->yoffset = 0; 4798c2ecf20Sopenharmony_ci pinfo->vxres = pinfo->xres; 4808c2ecf20Sopenharmony_ci pinfo->vyres = pinfo->yres; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* 4878c2ecf20Sopenharmony_ci * Parse user specified options (`video=platinumfb:') 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_cistatic int __init platinumfb_setup(char *options) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci char *this_opt; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!options || !*options) 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 4978c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "vmode:", 6)) { 4988c2ecf20Sopenharmony_ci int vmode = simple_strtoul(this_opt+6, NULL, 0); 4998c2ecf20Sopenharmony_ci if (vmode > 0 && vmode <= VMODE_MAX) 5008c2ecf20Sopenharmony_ci default_vmode = vmode; 5018c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "cmode:", 6)) { 5028c2ecf20Sopenharmony_ci int depth = simple_strtoul(this_opt+6, NULL, 0); 5038c2ecf20Sopenharmony_ci switch (depth) { 5048c2ecf20Sopenharmony_ci case 0: 5058c2ecf20Sopenharmony_ci case 8: 5068c2ecf20Sopenharmony_ci default_cmode = CMODE_8; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case 15: 5098c2ecf20Sopenharmony_ci case 16: 5108c2ecf20Sopenharmony_ci default_cmode = CMODE_16; 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci case 24: 5138c2ecf20Sopenharmony_ci case 32: 5148c2ecf20Sopenharmony_ci default_cmode = CMODE_32; 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci#ifdef __powerpc__ 5238c2ecf20Sopenharmony_ci#define invalidate_cache(addr) \ 5248c2ecf20Sopenharmony_ci asm volatile("eieio; dcbf 0,%1" \ 5258c2ecf20Sopenharmony_ci : "=m" (*(addr)) : "r" (addr) : "memory"); 5268c2ecf20Sopenharmony_ci#else 5278c2ecf20Sopenharmony_ci#define invalidate_cache(addr) 5288c2ecf20Sopenharmony_ci#endif 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int platinumfb_probe(struct platform_device* odev) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct device_node *dp = odev->dev.of_node; 5338c2ecf20Sopenharmony_ci struct fb_info *info; 5348c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo; 5358c2ecf20Sopenharmony_ci volatile __u8 *fbuffer; 5368c2ecf20Sopenharmony_ci int bank0, bank1, bank2, bank3, rc; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci dev_info(&odev->dev, "Found Apple Platinum video hardware\n"); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(*pinfo), &odev->dev); 5418c2ecf20Sopenharmony_ci if (!info) 5428c2ecf20Sopenharmony_ci return -ENOMEM; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci pinfo = info->par; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (of_address_to_resource(dp, 0, &pinfo->rsrc_reg) || 5478c2ecf20Sopenharmony_ci of_address_to_resource(dp, 1, &pinfo->rsrc_fb)) { 5488c2ecf20Sopenharmony_ci dev_err(&odev->dev, "Can't get resources\n"); 5498c2ecf20Sopenharmony_ci framebuffer_release(info); 5508c2ecf20Sopenharmony_ci return -ENXIO; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci dev_dbg(&odev->dev, " registers : 0x%llx...0x%llx\n", 5538c2ecf20Sopenharmony_ci (unsigned long long)pinfo->rsrc_reg.start, 5548c2ecf20Sopenharmony_ci (unsigned long long)pinfo->rsrc_reg.end); 5558c2ecf20Sopenharmony_ci dev_dbg(&odev->dev, " framebuffer: 0x%llx...0x%llx\n", 5568c2ecf20Sopenharmony_ci (unsigned long long)pinfo->rsrc_fb.start, 5578c2ecf20Sopenharmony_ci (unsigned long long)pinfo->rsrc_fb.end); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Do not try to request register space, they overlap with the 5608c2ecf20Sopenharmony_ci * northbridge and that can fail. Only request framebuffer 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci if (!request_mem_region(pinfo->rsrc_fb.start, 5638c2ecf20Sopenharmony_ci resource_size(&pinfo->rsrc_fb), 5648c2ecf20Sopenharmony_ci "platinumfb framebuffer")) { 5658c2ecf20Sopenharmony_ci printk(KERN_ERR "platinumfb: Can't request framebuffer !\n"); 5668c2ecf20Sopenharmony_ci framebuffer_release(info); 5678c2ecf20Sopenharmony_ci return -ENXIO; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* frame buffer - map only 4MB */ 5718c2ecf20Sopenharmony_ci pinfo->frame_buffer_phys = pinfo->rsrc_fb.start; 5728c2ecf20Sopenharmony_ci pinfo->frame_buffer = ioremap_wt(pinfo->rsrc_fb.start, 0x400000); 5738c2ecf20Sopenharmony_ci pinfo->base_frame_buffer = pinfo->frame_buffer; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* registers */ 5768c2ecf20Sopenharmony_ci pinfo->platinum_regs_phys = pinfo->rsrc_reg.start; 5778c2ecf20Sopenharmony_ci pinfo->platinum_regs = ioremap(pinfo->rsrc_reg.start, 0x1000); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci pinfo->cmap_regs_phys = 0xf301b000; /* XXX not in prom? */ 5808c2ecf20Sopenharmony_ci request_mem_region(pinfo->cmap_regs_phys, 0x1000, "platinumfb cmap"); 5818c2ecf20Sopenharmony_ci pinfo->cmap_regs = ioremap(pinfo->cmap_regs_phys, 0x1000); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Grok total video ram */ 5848c2ecf20Sopenharmony_ci out_be32(&pinfo->platinum_regs->reg[16].r, (unsigned)pinfo->frame_buffer_phys); 5858c2ecf20Sopenharmony_ci out_be32(&pinfo->platinum_regs->reg[20].r, 0x1011); /* select max vram */ 5868c2ecf20Sopenharmony_ci out_be32(&pinfo->platinum_regs->reg[24].r, 0); /* switch in vram */ 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci fbuffer = pinfo->base_frame_buffer; 5898c2ecf20Sopenharmony_ci fbuffer[0x100000] = 0x34; 5908c2ecf20Sopenharmony_ci fbuffer[0x100008] = 0x0; 5918c2ecf20Sopenharmony_ci invalidate_cache(&fbuffer[0x100000]); 5928c2ecf20Sopenharmony_ci fbuffer[0x200000] = 0x56; 5938c2ecf20Sopenharmony_ci fbuffer[0x200008] = 0x0; 5948c2ecf20Sopenharmony_ci invalidate_cache(&fbuffer[0x200000]); 5958c2ecf20Sopenharmony_ci fbuffer[0x300000] = 0x78; 5968c2ecf20Sopenharmony_ci fbuffer[0x300008] = 0x0; 5978c2ecf20Sopenharmony_ci invalidate_cache(&fbuffer[0x300000]); 5988c2ecf20Sopenharmony_ci bank0 = 1; /* builtin 1MB vram, always there */ 5998c2ecf20Sopenharmony_ci bank1 = fbuffer[0x100000] == 0x34; 6008c2ecf20Sopenharmony_ci bank2 = fbuffer[0x200000] == 0x56; 6018c2ecf20Sopenharmony_ci bank3 = fbuffer[0x300000] == 0x78; 6028c2ecf20Sopenharmony_ci pinfo->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000; 6038c2ecf20Sopenharmony_ci printk(KERN_INFO "platinumfb: Total VRAM = %dMB (%d%d%d%d)\n", 6048c2ecf20Sopenharmony_ci (unsigned int) (pinfo->total_vram / 1024 / 1024), 6058c2ecf20Sopenharmony_ci bank3, bank2, bank1, bank0); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* 6088c2ecf20Sopenharmony_ci * Try to determine whether we have an old or a new DACula. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci out_8(&pinfo->cmap_regs->addr, 0x40); 6118c2ecf20Sopenharmony_ci pinfo->dactype = in_8(&pinfo->cmap_regs->d2); 6128c2ecf20Sopenharmony_ci switch (pinfo->dactype) { 6138c2ecf20Sopenharmony_ci case 0x3c: 6148c2ecf20Sopenharmony_ci pinfo->clktype = 1; 6158c2ecf20Sopenharmony_ci printk(KERN_INFO "platinumfb: DACula type 0x3c\n"); 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case 0x84: 6188c2ecf20Sopenharmony_ci pinfo->clktype = 0; 6198c2ecf20Sopenharmony_ci printk(KERN_INFO "platinumfb: DACula type 0x84\n"); 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci default: 6228c2ecf20Sopenharmony_ci pinfo->clktype = 0; 6238c2ecf20Sopenharmony_ci printk(KERN_INFO "platinumfb: Unknown DACula type: %x\n", pinfo->dactype); 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci dev_set_drvdata(&odev->dev, info); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci rc = platinum_init_fb(info); 6298c2ecf20Sopenharmony_ci if (rc != 0) { 6308c2ecf20Sopenharmony_ci iounmap(pinfo->frame_buffer); 6318c2ecf20Sopenharmony_ci iounmap(pinfo->platinum_regs); 6328c2ecf20Sopenharmony_ci iounmap(pinfo->cmap_regs); 6338c2ecf20Sopenharmony_ci framebuffer_release(info); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return rc; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int platinumfb_remove(struct platform_device* odev) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(&odev->dev); 6428c2ecf20Sopenharmony_ci struct fb_info_platinum *pinfo = info->par; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci unregister_framebuffer (info); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Unmap frame buffer and registers */ 6478c2ecf20Sopenharmony_ci iounmap(pinfo->frame_buffer); 6488c2ecf20Sopenharmony_ci iounmap(pinfo->platinum_regs); 6498c2ecf20Sopenharmony_ci iounmap(pinfo->cmap_regs); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci release_mem_region(pinfo->rsrc_fb.start, 6528c2ecf20Sopenharmony_ci resource_size(&pinfo->rsrc_fb)); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci release_mem_region(pinfo->cmap_regs_phys, 0x1000); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci framebuffer_release(info); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic struct of_device_id platinumfb_match[] = 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci { 6648c2ecf20Sopenharmony_ci .name = "platinum", 6658c2ecf20Sopenharmony_ci }, 6668c2ecf20Sopenharmony_ci {}, 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic struct platform_driver platinum_driver = 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci .driver = { 6728c2ecf20Sopenharmony_ci .name = "platinumfb", 6738c2ecf20Sopenharmony_ci .of_match_table = platinumfb_match, 6748c2ecf20Sopenharmony_ci }, 6758c2ecf20Sopenharmony_ci .probe = platinumfb_probe, 6768c2ecf20Sopenharmony_ci .remove = platinumfb_remove, 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int __init platinumfb_init(void) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci#ifndef MODULE 6828c2ecf20Sopenharmony_ci char *option = NULL; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (fb_get_options("platinumfb", &option)) 6858c2ecf20Sopenharmony_ci return -ENODEV; 6868c2ecf20Sopenharmony_ci platinumfb_setup(option); 6878c2ecf20Sopenharmony_ci#endif 6888c2ecf20Sopenharmony_ci platform_driver_register(&platinum_driver); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic void __exit platinumfb_exit(void) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci platform_driver_unregister(&platinum_driver); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("framebuffer driver for Apple Platinum video"); 7008c2ecf20Sopenharmony_cimodule_init(platinumfb_init); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci#ifdef MODULE 7038c2ecf20Sopenharmony_cimodule_exit(platinumfb_exit); 7048c2ecf20Sopenharmony_ci#endif 705