18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/pmag-ba-fb.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * PMAG-BA TURBOchannel Color Frame Buffer (CFB) card support, 58c2ecf20Sopenharmony_ci * derived from: 68c2ecf20Sopenharmony_ci * "HP300 Topcat framebuffer support (derived from macfb of all things) 78c2ecf20Sopenharmony_ci * Phil Blundell <philb@gnu.org> 1998", the original code can be 88c2ecf20Sopenharmony_ci * found in the file hpfb.c in the same directory. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on digital document: 118c2ecf20Sopenharmony_ci * "PMAG-BA TURBOchannel Color Frame Buffer 128c2ecf20Sopenharmony_ci * Functional Specification", Revision 1.2, August 27, 1990 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * DECstation related code Copyright (C) 1999, 2000, 2001 by 158c2ecf20Sopenharmony_ci * Michael Engel <engel@unix-ag.org>, 168c2ecf20Sopenharmony_ci * Karsten Merker <merker@linuxtag.org> and 178c2ecf20Sopenharmony_ci * Harald Koerfgen. 188c2ecf20Sopenharmony_ci * Copyright (c) 2005, 2006 Maciej W. Rozycki 198c2ecf20Sopenharmony_ci * Copyright (c) 2005 James Simmons 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 228c2ecf20Sopenharmony_ci * Public License. See the file COPYING in the main directory of this 238c2ecf20Sopenharmony_ci * archive for more details. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/compiler.h> 278c2ecf20Sopenharmony_ci#include <linux/errno.h> 288c2ecf20Sopenharmony_ci#include <linux/fb.h> 298c2ecf20Sopenharmony_ci#include <linux/init.h> 308c2ecf20Sopenharmony_ci#include <linux/kernel.h> 318c2ecf20Sopenharmony_ci#include <linux/module.h> 328c2ecf20Sopenharmony_ci#include <linux/tc.h> 338c2ecf20Sopenharmony_ci#include <linux/types.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <asm/io.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <video/pmag-ba-fb.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct pmagbafb_par { 418c2ecf20Sopenharmony_ci volatile void __iomem *mmio; 428c2ecf20Sopenharmony_ci volatile u32 __iomem *dac; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo pmagbafb_defined = { 478c2ecf20Sopenharmony_ci .xres = 1024, 488c2ecf20Sopenharmony_ci .yres = 864, 498c2ecf20Sopenharmony_ci .xres_virtual = 1024, 508c2ecf20Sopenharmony_ci .yres_virtual = 864, 518c2ecf20Sopenharmony_ci .bits_per_pixel = 8, 528c2ecf20Sopenharmony_ci .red.length = 8, 538c2ecf20Sopenharmony_ci .green.length = 8, 548c2ecf20Sopenharmony_ci .blue.length = 8, 558c2ecf20Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 568c2ecf20Sopenharmony_ci .height = -1, 578c2ecf20Sopenharmony_ci .width = -1, 588c2ecf20Sopenharmony_ci .accel_flags = FB_ACCEL_NONE, 598c2ecf20Sopenharmony_ci .pixclock = 14452, 608c2ecf20Sopenharmony_ci .left_margin = 116, 618c2ecf20Sopenharmony_ci .right_margin = 12, 628c2ecf20Sopenharmony_ci .upper_margin = 34, 638c2ecf20Sopenharmony_ci .lower_margin = 0, 648c2ecf20Sopenharmony_ci .hsync_len = 128, 658c2ecf20Sopenharmony_ci .vsync_len = 3, 668c2ecf20Sopenharmony_ci .sync = FB_SYNC_ON_GREEN, 678c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo pmagbafb_fix = { 718c2ecf20Sopenharmony_ci .id = "PMAG-BA", 728c2ecf20Sopenharmony_ci .smem_len = (1024 * 1024), 738c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 748c2ecf20Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 758c2ecf20Sopenharmony_ci .line_length = 1024, 768c2ecf20Sopenharmony_ci .mmio_len = PMAG_BA_SIZE - PMAG_BA_BT459, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline void dac_write(struct pmagbafb_par *par, unsigned int reg, u8 v) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci writeb(v, par->dac + reg / 4); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic inline u8 dac_read(struct pmagbafb_par *par, unsigned int reg) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return readb(par->dac + reg / 4); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * Set the palette. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic int pmagbafb_setcolreg(unsigned int regno, unsigned int red, 958c2ecf20Sopenharmony_ci unsigned int green, unsigned int blue, 968c2ecf20Sopenharmony_ci unsigned int transp, struct fb_info *info) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct pmagbafb_par *par = info->par; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (regno >= info->cmap.len) 1018c2ecf20Sopenharmony_ci return 1; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci red >>= 8; /* The cmap fields are 16 bits */ 1048c2ecf20Sopenharmony_ci green >>= 8; /* wide, but the hardware colormap */ 1058c2ecf20Sopenharmony_ci blue >>= 8; /* registers are only 8 bits wide */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci mb(); 1088c2ecf20Sopenharmony_ci dac_write(par, BT459_ADDR_LO, regno); 1098c2ecf20Sopenharmony_ci dac_write(par, BT459_ADDR_HI, 0x00); 1108c2ecf20Sopenharmony_ci wmb(); 1118c2ecf20Sopenharmony_ci dac_write(par, BT459_CMAP, red); 1128c2ecf20Sopenharmony_ci wmb(); 1138c2ecf20Sopenharmony_ci dac_write(par, BT459_CMAP, green); 1148c2ecf20Sopenharmony_ci wmb(); 1158c2ecf20Sopenharmony_ci dac_write(par, BT459_CMAP, blue); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const struct fb_ops pmagbafb_ops = { 1218c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1228c2ecf20Sopenharmony_ci .fb_setcolreg = pmagbafb_setcolreg, 1238c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 1248c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 1258c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * Turn the hardware cursor off. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic void pmagbafb_erase_cursor(struct fb_info *info) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct pmagbafb_par *par = info->par; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mb(); 1378c2ecf20Sopenharmony_ci dac_write(par, BT459_ADDR_LO, 0x00); 1388c2ecf20Sopenharmony_ci dac_write(par, BT459_ADDR_HI, 0x03); 1398c2ecf20Sopenharmony_ci wmb(); 1408c2ecf20Sopenharmony_ci dac_write(par, BT459_DATA, 0x00); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int pmagbafb_probe(struct device *dev) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct tc_dev *tdev = to_tc_dev(dev); 1478c2ecf20Sopenharmony_ci resource_size_t start, len; 1488c2ecf20Sopenharmony_ci struct fb_info *info; 1498c2ecf20Sopenharmony_ci struct pmagbafb_par *par; 1508c2ecf20Sopenharmony_ci int err; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct pmagbafb_par), dev); 1538c2ecf20Sopenharmony_ci if (!info) 1548c2ecf20Sopenharmony_ci return -ENOMEM; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci par = info->par; 1578c2ecf20Sopenharmony_ci dev_set_drvdata(dev, info); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { 1608c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot allocate color map\n", 1618c2ecf20Sopenharmony_ci dev_name(dev)); 1628c2ecf20Sopenharmony_ci err = -ENOMEM; 1638c2ecf20Sopenharmony_ci goto err_alloc; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci info->fbops = &pmagbafb_ops; 1678c2ecf20Sopenharmony_ci info->fix = pmagbafb_fix; 1688c2ecf20Sopenharmony_ci info->var = pmagbafb_defined; 1698c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Request the I/O MEM resource. */ 1728c2ecf20Sopenharmony_ci start = tdev->resource.start; 1738c2ecf20Sopenharmony_ci len = tdev->resource.end - start + 1; 1748c2ecf20Sopenharmony_ci if (!request_mem_region(start, len, dev_name(dev))) { 1758c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot reserve FB region\n", 1768c2ecf20Sopenharmony_ci dev_name(dev)); 1778c2ecf20Sopenharmony_ci err = -EBUSY; 1788c2ecf20Sopenharmony_ci goto err_cmap; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* MMIO mapping setup. */ 1828c2ecf20Sopenharmony_ci info->fix.mmio_start = start; 1838c2ecf20Sopenharmony_ci par->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); 1848c2ecf20Sopenharmony_ci if (!par->mmio) { 1858c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev)); 1868c2ecf20Sopenharmony_ci err = -ENOMEM; 1878c2ecf20Sopenharmony_ci goto err_resource; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci par->dac = par->mmio + PMAG_BA_BT459; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Frame buffer mapping setup. */ 1928c2ecf20Sopenharmony_ci info->fix.smem_start = start + PMAG_BA_FBMEM; 1938c2ecf20Sopenharmony_ci info->screen_base = ioremap(info->fix.smem_start, 1948c2ecf20Sopenharmony_ci info->fix.smem_len); 1958c2ecf20Sopenharmony_ci if (!info->screen_base) { 1968c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev)); 1978c2ecf20Sopenharmony_ci err = -ENOMEM; 1988c2ecf20Sopenharmony_ci goto err_mmio_map; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci info->screen_size = info->fix.smem_len; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pmagbafb_erase_cursor(info); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci err = register_framebuffer(info); 2058c2ecf20Sopenharmony_ci if (err < 0) { 2068c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot register framebuffer\n", 2078c2ecf20Sopenharmony_ci dev_name(dev)); 2088c2ecf20Sopenharmony_ci goto err_smem_map; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci get_device(dev); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device at %s\n", 2148c2ecf20Sopenharmony_ci info->fix.id, dev_name(dev)); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cierr_smem_map: 2208c2ecf20Sopenharmony_ci iounmap(info->screen_base); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cierr_mmio_map: 2238c2ecf20Sopenharmony_ci iounmap(par->mmio); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cierr_resource: 2268c2ecf20Sopenharmony_ci release_mem_region(start, len); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cierr_cmap: 2298c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cierr_alloc: 2328c2ecf20Sopenharmony_ci framebuffer_release(info); 2338c2ecf20Sopenharmony_ci return err; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int pmagbafb_remove(struct device *dev) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct tc_dev *tdev = to_tc_dev(dev); 2398c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 2408c2ecf20Sopenharmony_ci struct pmagbafb_par *par = info->par; 2418c2ecf20Sopenharmony_ci resource_size_t start, len; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci put_device(dev); 2448c2ecf20Sopenharmony_ci unregister_framebuffer(info); 2458c2ecf20Sopenharmony_ci iounmap(info->screen_base); 2468c2ecf20Sopenharmony_ci iounmap(par->mmio); 2478c2ecf20Sopenharmony_ci start = tdev->resource.start; 2488c2ecf20Sopenharmony_ci len = tdev->resource.end - start + 1; 2498c2ecf20Sopenharmony_ci release_mem_region(start, len); 2508c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 2518c2ecf20Sopenharmony_ci framebuffer_release(info); 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* 2578c2ecf20Sopenharmony_ci * Initialize the framebuffer. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic const struct tc_device_id pmagbafb_tc_table[] = { 2608c2ecf20Sopenharmony_ci { "DEC ", "PMAG-BA " }, 2618c2ecf20Sopenharmony_ci { } 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(tc, pmagbafb_tc_table); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic struct tc_driver pmagbafb_driver = { 2668c2ecf20Sopenharmony_ci .id_table = pmagbafb_tc_table, 2678c2ecf20Sopenharmony_ci .driver = { 2688c2ecf20Sopenharmony_ci .name = "pmagbafb", 2698c2ecf20Sopenharmony_ci .bus = &tc_bus_type, 2708c2ecf20Sopenharmony_ci .probe = pmagbafb_probe, 2718c2ecf20Sopenharmony_ci .remove = pmagbafb_remove, 2728c2ecf20Sopenharmony_ci }, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int __init pmagbafb_init(void) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci#ifndef MODULE 2788c2ecf20Sopenharmony_ci if (fb_get_options("pmagbafb", NULL)) 2798c2ecf20Sopenharmony_ci return -ENXIO; 2808c2ecf20Sopenharmony_ci#endif 2818c2ecf20Sopenharmony_ci return tc_register_driver(&pmagbafb_driver); 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void __exit pmagbafb_exit(void) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci tc_unregister_driver(&pmagbafb_driver); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cimodule_init(pmagbafb_init); 2918c2ecf20Sopenharmony_cimodule_exit(pmagbafb_exit); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 294