18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/pmag-aa-fb.c 38c2ecf20Sopenharmony_ci * Copyright 2002 Karsten Merker <merker@debian.org> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * PMAG-AA TurboChannel framebuffer card support ... derived from 68c2ecf20Sopenharmony_ci * pmag-ba-fb.c, which is Copyright (C) 1999, 2000, 2001 by 78c2ecf20Sopenharmony_ci * Michael Engel <engel@unix-ag.org>, Karsten Merker <merker@debian.org> 88c2ecf20Sopenharmony_ci * and Harald Koerfgen <hkoerfg@web.de>, which itself is derived from 98c2ecf20Sopenharmony_ci * "HP300 Topcat framebuffer support (derived from macfb of all things) 108c2ecf20Sopenharmony_ci * Phil Blundell <philb@gnu.org> 1998" 118c2ecf20Sopenharmony_ci * Copyright (c) 2016 Maciej W. Rozycki 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 148c2ecf20Sopenharmony_ci * Public License. See the file COPYING in the main directory of this 158c2ecf20Sopenharmony_ci * archive for more details. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * 2002-09-28 Karsten Merker <merker@linuxtag.org> 188c2ecf20Sopenharmony_ci * Version 0.01: First try to get a PMAG-AA running. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * 2003-02-24 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> 218c2ecf20Sopenharmony_ci * Version 0.02: Major code cleanup. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * 2003-09-21 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> 248c2ecf20Sopenharmony_ci * Hardware cursor support. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * 2016-02-21 Maciej W. Rozycki <macro@linux-mips.org> 278c2ecf20Sopenharmony_ci * Version 0.03: Rewritten for the new FB and TC APIs. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/compiler.h> 318c2ecf20Sopenharmony_ci#include <linux/errno.h> 328c2ecf20Sopenharmony_ci#include <linux/fb.h> 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci#include <linux/io.h> 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/tc.h> 388c2ecf20Sopenharmony_ci#include <linux/timer.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "bt455.h" 418c2ecf20Sopenharmony_ci#include "bt431.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Version information */ 448c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.03" 458c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Karsten Merker <merker@linuxtag.org>" 468c2ecf20Sopenharmony_ci#define DRIVER_DESCRIPTION "PMAG-AA Framebuffer Driver" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * Bt455 RAM DAC register base offset (rel. to TC slot base address). 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci#define PMAG_AA_BT455_OFFSET 0x100000 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Bt431 cursor generator offset (rel. to TC slot base address). 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define PMAG_AA_BT431_OFFSET 0x180000 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Begin of PMAG-AA framebuffer memory relative to TC slot address, 608c2ecf20Sopenharmony_ci * resolution is 1280x1024x1 (8 bits deep, but only LSB is used). 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci#define PMAG_AA_ONBOARD_FBMEM_OFFSET 0x200000 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct aafb_par { 658c2ecf20Sopenharmony_ci void __iomem *mmio; 668c2ecf20Sopenharmony_ci struct bt455_regs __iomem *bt455; 678c2ecf20Sopenharmony_ci struct bt431_regs __iomem *bt431; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo aafb_defined = { 718c2ecf20Sopenharmony_ci .xres = 1280, 728c2ecf20Sopenharmony_ci .yres = 1024, 738c2ecf20Sopenharmony_ci .xres_virtual = 2048, 748c2ecf20Sopenharmony_ci .yres_virtual = 1024, 758c2ecf20Sopenharmony_ci .bits_per_pixel = 8, 768c2ecf20Sopenharmony_ci .grayscale = 1, 778c2ecf20Sopenharmony_ci .red.length = 0, 788c2ecf20Sopenharmony_ci .green.length = 1, 798c2ecf20Sopenharmony_ci .blue.length = 0, 808c2ecf20Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 818c2ecf20Sopenharmony_ci .accel_flags = FB_ACCEL_NONE, 828c2ecf20Sopenharmony_ci .pixclock = 7645, 838c2ecf20Sopenharmony_ci .left_margin = 224, 848c2ecf20Sopenharmony_ci .right_margin = 32, 858c2ecf20Sopenharmony_ci .upper_margin = 33, 868c2ecf20Sopenharmony_ci .lower_margin = 3, 878c2ecf20Sopenharmony_ci .hsync_len = 160, 888c2ecf20Sopenharmony_ci .vsync_len = 3, 898c2ecf20Sopenharmony_ci .sync = FB_SYNC_ON_GREEN, 908c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo aafb_fix = { 948c2ecf20Sopenharmony_ci .id = "PMAG-AA", 958c2ecf20Sopenharmony_ci .smem_len = (2048 * 1024), 968c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 978c2ecf20Sopenharmony_ci .visual = FB_VISUAL_MONO10, 988c2ecf20Sopenharmony_ci .ypanstep = 1, 998c2ecf20Sopenharmony_ci .ywrapstep = 1, 1008c2ecf20Sopenharmony_ci .line_length = 2048, 1018c2ecf20Sopenharmony_ci .mmio_len = PMAG_AA_ONBOARD_FBMEM_OFFSET - PMAG_AA_BT455_OFFSET, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int aafb_cursor(struct fb_info *info, struct fb_cursor *cursor) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct aafb_par *par = info->par; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (cursor->image.height > BT431_CURSOR_SIZE || 1098c2ecf20Sopenharmony_ci cursor->image.width > BT431_CURSOR_SIZE) { 1108c2ecf20Sopenharmony_ci bt431_erase_cursor(par->bt431); 1118c2ecf20Sopenharmony_ci return -EINVAL; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!cursor->enable) 1158c2ecf20Sopenharmony_ci bt431_erase_cursor(par->bt431); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (cursor->set & FB_CUR_SETPOS) 1188c2ecf20Sopenharmony_ci bt431_position_cursor(par->bt431, 1198c2ecf20Sopenharmony_ci cursor->image.dx, cursor->image.dy); 1208c2ecf20Sopenharmony_ci if (cursor->set & FB_CUR_SETCMAP) { 1218c2ecf20Sopenharmony_ci u8 fg = cursor->image.fg_color ? 0xf : 0x0; 1228c2ecf20Sopenharmony_ci u8 bg = cursor->image.bg_color ? 0xf : 0x0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci bt455_write_cmap_entry(par->bt455, 8, bg); 1258c2ecf20Sopenharmony_ci bt455_write_cmap_next(par->bt455, bg); 1268c2ecf20Sopenharmony_ci bt455_write_ovly_next(par->bt455, fg); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci if (cursor->set & (FB_CUR_SETSIZE | FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) 1298c2ecf20Sopenharmony_ci bt431_set_cursor(par->bt431, 1308c2ecf20Sopenharmony_ci cursor->image.data, cursor->mask, cursor->rop, 1318c2ecf20Sopenharmony_ci cursor->image.width, cursor->image.height); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (cursor->enable) 1348c2ecf20Sopenharmony_ci bt431_enable_cursor(par->bt431); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 0 unblanks, any other blanks. */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int aafb_blank(int blank, struct fb_info *info) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct aafb_par *par = info->par; 1448c2ecf20Sopenharmony_ci u8 val = blank ? 0x00 : 0x0f; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci bt455_write_cmap_entry(par->bt455, 1, val); 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct fb_ops aafb_ops = { 1518c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1528c2ecf20Sopenharmony_ci .fb_blank = aafb_blank, 1538c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 1548c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 1558c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 1568c2ecf20Sopenharmony_ci .fb_cursor = aafb_cursor, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int pmagaafb_probe(struct device *dev) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct tc_dev *tdev = to_tc_dev(dev); 1628c2ecf20Sopenharmony_ci resource_size_t start, len; 1638c2ecf20Sopenharmony_ci struct fb_info *info; 1648c2ecf20Sopenharmony_ci struct aafb_par *par; 1658c2ecf20Sopenharmony_ci int err; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct aafb_par), dev); 1688c2ecf20Sopenharmony_ci if (!info) 1698c2ecf20Sopenharmony_ci return -ENOMEM; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci par = info->par; 1728c2ecf20Sopenharmony_ci dev_set_drvdata(dev, info); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci info->fbops = &aafb_ops; 1758c2ecf20Sopenharmony_ci info->fix = aafb_fix; 1768c2ecf20Sopenharmony_ci info->var = aafb_defined; 1778c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Request the I/O MEM resource. */ 1808c2ecf20Sopenharmony_ci start = tdev->resource.start; 1818c2ecf20Sopenharmony_ci len = tdev->resource.end - start + 1; 1828c2ecf20Sopenharmony_ci if (!request_mem_region(start, len, dev_name(dev))) { 1838c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot reserve FB region\n", 1848c2ecf20Sopenharmony_ci dev_name(dev)); 1858c2ecf20Sopenharmony_ci err = -EBUSY; 1868c2ecf20Sopenharmony_ci goto err_alloc; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* MMIO mapping setup. */ 1908c2ecf20Sopenharmony_ci info->fix.mmio_start = start + PMAG_AA_BT455_OFFSET; 1918c2ecf20Sopenharmony_ci par->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); 1928c2ecf20Sopenharmony_ci if (!par->mmio) { 1938c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev)); 1948c2ecf20Sopenharmony_ci err = -ENOMEM; 1958c2ecf20Sopenharmony_ci goto err_resource; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci par->bt455 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT455_OFFSET; 1988c2ecf20Sopenharmony_ci par->bt431 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT431_OFFSET; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Frame buffer mapping setup. */ 2018c2ecf20Sopenharmony_ci info->fix.smem_start = start + PMAG_AA_ONBOARD_FBMEM_OFFSET; 2028c2ecf20Sopenharmony_ci info->screen_base = ioremap(info->fix.smem_start, 2038c2ecf20Sopenharmony_ci info->fix.smem_len); 2048c2ecf20Sopenharmony_ci if (!info->screen_base) { 2058c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev)); 2068c2ecf20Sopenharmony_ci err = -ENOMEM; 2078c2ecf20Sopenharmony_ci goto err_mmio_map; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci info->screen_size = info->fix.smem_len; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Init colormap. */ 2128c2ecf20Sopenharmony_ci bt455_write_cmap_entry(par->bt455, 0, 0x0); 2138c2ecf20Sopenharmony_ci bt455_write_cmap_next(par->bt455, 0xf); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Init hardware cursor. */ 2168c2ecf20Sopenharmony_ci bt431_erase_cursor(par->bt431); 2178c2ecf20Sopenharmony_ci bt431_init_cursor(par->bt431); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci err = register_framebuffer(info); 2208c2ecf20Sopenharmony_ci if (err < 0) { 2218c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Cannot register framebuffer\n", 2228c2ecf20Sopenharmony_ci dev_name(dev)); 2238c2ecf20Sopenharmony_ci goto err_smem_map; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci get_device(dev); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci pr_info("fb%d: %s frame buffer device at %s\n", 2298c2ecf20Sopenharmony_ci info->node, info->fix.id, dev_name(dev)); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cierr_smem_map: 2358c2ecf20Sopenharmony_ci iounmap(info->screen_base); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cierr_mmio_map: 2388c2ecf20Sopenharmony_ci iounmap(par->mmio); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cierr_resource: 2418c2ecf20Sopenharmony_ci release_mem_region(start, len); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cierr_alloc: 2448c2ecf20Sopenharmony_ci framebuffer_release(info); 2458c2ecf20Sopenharmony_ci return err; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int pmagaafb_remove(struct device *dev) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct tc_dev *tdev = to_tc_dev(dev); 2518c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 2528c2ecf20Sopenharmony_ci struct aafb_par *par = info->par; 2538c2ecf20Sopenharmony_ci resource_size_t start, len; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci put_device(dev); 2568c2ecf20Sopenharmony_ci unregister_framebuffer(info); 2578c2ecf20Sopenharmony_ci iounmap(info->screen_base); 2588c2ecf20Sopenharmony_ci iounmap(par->mmio); 2598c2ecf20Sopenharmony_ci start = tdev->resource.start; 2608c2ecf20Sopenharmony_ci len = tdev->resource.end - start + 1; 2618c2ecf20Sopenharmony_ci release_mem_region(start, len); 2628c2ecf20Sopenharmony_ci framebuffer_release(info); 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Initialise the framebuffer. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic const struct tc_device_id pmagaafb_tc_table[] = { 2708c2ecf20Sopenharmony_ci { "DEC ", "PMAG-AA " }, 2718c2ecf20Sopenharmony_ci { } 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(tc, pmagaafb_tc_table); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct tc_driver pmagaafb_driver = { 2768c2ecf20Sopenharmony_ci .id_table = pmagaafb_tc_table, 2778c2ecf20Sopenharmony_ci .driver = { 2788c2ecf20Sopenharmony_ci .name = "pmagaafb", 2798c2ecf20Sopenharmony_ci .bus = &tc_bus_type, 2808c2ecf20Sopenharmony_ci .probe = pmagaafb_probe, 2818c2ecf20Sopenharmony_ci .remove = pmagaafb_remove, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int __init pmagaafb_init(void) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci#ifndef MODULE 2888c2ecf20Sopenharmony_ci if (fb_get_options("pmagaafb", NULL)) 2898c2ecf20Sopenharmony_ci return -ENXIO; 2908c2ecf20Sopenharmony_ci#endif 2918c2ecf20Sopenharmony_ci return tc_register_driver(&pmagaafb_driver); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void __exit pmagaafb_exit(void) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci tc_unregister_driver(&pmagaafb_driver); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cimodule_init(pmagaafb_init); 3008c2ecf20Sopenharmony_cimodule_exit(pmagaafb_exit); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 3038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESCRIPTION); 3048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 305