18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/arkfb.c -- Frame buffer device driver for ARK 2000PV 38c2ecf20Sopenharmony_ci * with ICS 5342 dac (it is easy to add support for different dacs). 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Ondrej Zajicek <santiago@crfreenet.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 88c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 98c2ecf20Sopenharmony_ci * more details. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Code is based on s3fb 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/tty.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/fb.h> 238c2ecf20Sopenharmony_ci#include <linux/svga.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/pci.h> 268c2ecf20Sopenharmony_ci#include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */ 278c2ecf20Sopenharmony_ci#include <video/vga.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct arkfb_info { 308c2ecf20Sopenharmony_ci int mclk_freq; 318c2ecf20Sopenharmony_ci int wc_cookie; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci struct dac_info *dac; 348c2ecf20Sopenharmony_ci struct vgastate state; 358c2ecf20Sopenharmony_ci struct mutex open_lock; 368c2ecf20Sopenharmony_ci unsigned int ref_count; 378c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct svga_fb_format arkfb_formats[] = { 458c2ecf20Sopenharmony_ci { 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 468c2ecf20Sopenharmony_ci FB_TYPE_TEXT, FB_AUX_TEXT_SVGA_STEP4, FB_VISUAL_PSEUDOCOLOR, 8, 8}, 478c2ecf20Sopenharmony_ci { 4, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 488c2ecf20Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 16}, 498c2ecf20Sopenharmony_ci { 4, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 1, 508c2ecf20Sopenharmony_ci FB_TYPE_INTERLEAVED_PLANES, 1, FB_VISUAL_PSEUDOCOLOR, 8, 16}, 518c2ecf20Sopenharmony_ci { 8, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 528c2ecf20Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 8}, 538c2ecf20Sopenharmony_ci {16, {10, 5, 0}, {5, 5, 0}, {0, 5, 0}, {0, 0, 0}, 0, 548c2ecf20Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 4, 4}, 558c2ecf20Sopenharmony_ci {16, {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, 0, 568c2ecf20Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 4, 4}, 578c2ecf20Sopenharmony_ci {24, {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 588c2ecf20Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 8, 8}, 598c2ecf20Sopenharmony_ci {32, {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, 608c2ecf20Sopenharmony_ci FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 2, 2}, 618c2ecf20Sopenharmony_ci SVGA_FORMAT_END 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* CRT timing register sets */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic const struct vga_regset ark_h_total_regs[] = {{0x00, 0, 7}, {0x41, 7, 7}, VGA_REGSET_END}; 688c2ecf20Sopenharmony_cistatic const struct vga_regset ark_h_display_regs[] = {{0x01, 0, 7}, {0x41, 6, 6}, VGA_REGSET_END}; 698c2ecf20Sopenharmony_cistatic const struct vga_regset ark_h_blank_start_regs[] = {{0x02, 0, 7}, {0x41, 5, 5}, VGA_REGSET_END}; 708c2ecf20Sopenharmony_cistatic const struct vga_regset ark_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7 }, VGA_REGSET_END}; 718c2ecf20Sopenharmony_cistatic const struct vga_regset ark_h_sync_start_regs[] = {{0x04, 0, 7}, {0x41, 4, 4}, VGA_REGSET_END}; 728c2ecf20Sopenharmony_cistatic const struct vga_regset ark_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic const struct vga_regset ark_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x40, 7, 7}, VGA_REGSET_END}; 758c2ecf20Sopenharmony_cistatic const struct vga_regset ark_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x40, 6, 6}, VGA_REGSET_END}; 768c2ecf20Sopenharmony_cistatic const struct vga_regset ark_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x40, 5, 5}, VGA_REGSET_END}; 778c2ecf20Sopenharmony_ci// const struct vga_regset ark_v_blank_end_regs[] = {{0x16, 0, 6}, VGA_REGSET_END}; 788c2ecf20Sopenharmony_cistatic const struct vga_regset ark_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; 798c2ecf20Sopenharmony_cistatic const struct vga_regset ark_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x40, 4, 4}, VGA_REGSET_END}; 808c2ecf20Sopenharmony_cistatic const struct vga_regset ark_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const struct vga_regset ark_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, VGA_REGSET_END}; 838c2ecf20Sopenharmony_cistatic const struct vga_regset ark_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x40, 0, 2}, VGA_REGSET_END}; 848c2ecf20Sopenharmony_cistatic const struct vga_regset ark_offset_regs[] = {{0x13, 0, 7}, {0x41, 3, 3}, VGA_REGSET_END}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct svga_timing_regs ark_timing_regs = { 878c2ecf20Sopenharmony_ci ark_h_total_regs, ark_h_display_regs, ark_h_blank_start_regs, 888c2ecf20Sopenharmony_ci ark_h_blank_end_regs, ark_h_sync_start_regs, ark_h_sync_end_regs, 898c2ecf20Sopenharmony_ci ark_v_total_regs, ark_v_display_regs, ark_v_blank_start_regs, 908c2ecf20Sopenharmony_ci ark_v_blank_end_regs, ark_v_sync_start_regs, ark_v_sync_end_regs, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Module parameters */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic char *mode_option = "640x480-8@60"; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c) 2007 Ondrej Zajicek <santiago@crfreenet.org>"); 1028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for ARK 2000PV"); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0444); 1068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)"); 1078c2ecf20Sopenharmony_cimodule_param_named(mode, mode_option, charp, 0444); 1088c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode, "Default video mode ('640x480-8@60', etc) (deprecated)"); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int threshold = 4; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cimodule_param(threshold, int, 0644); 1138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(threshold, "FIFO threshold"); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void arkfb_settile(struct fb_info *info, struct fb_tilemap *map) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci const u8 *font = map->data; 1228c2ecf20Sopenharmony_ci u8 __iomem *fb = (u8 __iomem *)info->screen_base; 1238c2ecf20Sopenharmony_ci int i, c; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if ((map->width != 8) || (map->height != 16) || 1268c2ecf20Sopenharmony_ci (map->depth != 1) || (map->length != 256)) { 1278c2ecf20Sopenharmony_ci fb_err(info, "unsupported font parameters: width %d, height %d, depth %d, length %d\n", 1288c2ecf20Sopenharmony_ci map->width, map->height, map->depth, map->length); 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci fb += 2; 1338c2ecf20Sopenharmony_ci for (c = 0; c < map->length; c++) { 1348c2ecf20Sopenharmony_ci for (i = 0; i < map->height; i++) { 1358c2ecf20Sopenharmony_ci fb_writeb(font[i], &fb[i * 4]); 1368c2ecf20Sopenharmony_ci fb_writeb(font[i], &fb[i * 4 + (128 * 8)]); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci fb += 128; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if ((c % 8) == 7) 1418c2ecf20Sopenharmony_ci fb += 128*8; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci font += map->height; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void arkfb_tilecursor(struct fb_info *info, struct fb_tilecursor *cursor) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci svga_tilecursor(par->state.vgabase, info, cursor); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic struct fb_tile_ops arkfb_tile_ops = { 1558c2ecf20Sopenharmony_ci .fb_settile = arkfb_settile, 1568c2ecf20Sopenharmony_ci .fb_tilecopy = svga_tilecopy, 1578c2ecf20Sopenharmony_ci .fb_tilefill = svga_tilefill, 1588c2ecf20Sopenharmony_ci .fb_tileblit = svga_tileblit, 1598c2ecf20Sopenharmony_ci .fb_tilecursor = arkfb_tilecursor, 1608c2ecf20Sopenharmony_ci .fb_get_tilemax = svga_get_tilemax, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* image data is MSB-first, fb structure is MSB-first too */ 1688c2ecf20Sopenharmony_cistatic inline u32 expand_color(u32 c) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return ((c & 1) | ((c & 2) << 7) | ((c & 4) << 14) | ((c & 8) << 21)) * 0xFF; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* arkfb_iplan_imageblit silently assumes that almost everything is 8-pixel aligned */ 1748c2ecf20Sopenharmony_cistatic void arkfb_iplan_imageblit(struct fb_info *info, const struct fb_image *image) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci u32 fg = expand_color(image->fg_color); 1778c2ecf20Sopenharmony_ci u32 bg = expand_color(image->bg_color); 1788c2ecf20Sopenharmony_ci const u8 *src1, *src; 1798c2ecf20Sopenharmony_ci u8 __iomem *dst1; 1808c2ecf20Sopenharmony_ci u32 __iomem *dst; 1818c2ecf20Sopenharmony_ci u32 val; 1828c2ecf20Sopenharmony_ci int x, y; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci src1 = image->data; 1858c2ecf20Sopenharmony_ci dst1 = info->screen_base + (image->dy * info->fix.line_length) 1868c2ecf20Sopenharmony_ci + ((image->dx / 8) * 4); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (y = 0; y < image->height; y++) { 1898c2ecf20Sopenharmony_ci src = src1; 1908c2ecf20Sopenharmony_ci dst = (u32 __iomem *) dst1; 1918c2ecf20Sopenharmony_ci for (x = 0; x < image->width; x += 8) { 1928c2ecf20Sopenharmony_ci val = *(src++) * 0x01010101; 1938c2ecf20Sopenharmony_ci val = (val & fg) | (~val & bg); 1948c2ecf20Sopenharmony_ci fb_writel(val, dst++); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci src1 += image->width / 8; 1978c2ecf20Sopenharmony_ci dst1 += info->fix.line_length; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* arkfb_iplan_fillrect silently assumes that almost everything is 8-pixel aligned */ 2038c2ecf20Sopenharmony_cistatic void arkfb_iplan_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci u32 fg = expand_color(rect->color); 2068c2ecf20Sopenharmony_ci u8 __iomem *dst1; 2078c2ecf20Sopenharmony_ci u32 __iomem *dst; 2088c2ecf20Sopenharmony_ci int x, y; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dst1 = info->screen_base + (rect->dy * info->fix.line_length) 2118c2ecf20Sopenharmony_ci + ((rect->dx / 8) * 4); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci for (y = 0; y < rect->height; y++) { 2148c2ecf20Sopenharmony_ci dst = (u32 __iomem *) dst1; 2158c2ecf20Sopenharmony_ci for (x = 0; x < rect->width; x += 8) { 2168c2ecf20Sopenharmony_ci fb_writel(fg, dst++); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci dst1 += info->fix.line_length; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* image data is MSB-first, fb structure is high-nibble-in-low-byte-first */ 2258c2ecf20Sopenharmony_cistatic inline u32 expand_pixel(u32 c) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return (((c & 1) << 24) | ((c & 2) << 27) | ((c & 4) << 14) | ((c & 8) << 17) | 2288c2ecf20Sopenharmony_ci ((c & 16) << 4) | ((c & 32) << 7) | ((c & 64) >> 6) | ((c & 128) >> 3)) * 0xF; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* arkfb_cfb4_imageblit silently assumes that almost everything is 8-pixel aligned */ 2328c2ecf20Sopenharmony_cistatic void arkfb_cfb4_imageblit(struct fb_info *info, const struct fb_image *image) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci u32 fg = image->fg_color * 0x11111111; 2358c2ecf20Sopenharmony_ci u32 bg = image->bg_color * 0x11111111; 2368c2ecf20Sopenharmony_ci const u8 *src1, *src; 2378c2ecf20Sopenharmony_ci u8 __iomem *dst1; 2388c2ecf20Sopenharmony_ci u32 __iomem *dst; 2398c2ecf20Sopenharmony_ci u32 val; 2408c2ecf20Sopenharmony_ci int x, y; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci src1 = image->data; 2438c2ecf20Sopenharmony_ci dst1 = info->screen_base + (image->dy * info->fix.line_length) 2448c2ecf20Sopenharmony_ci + ((image->dx / 8) * 4); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci for (y = 0; y < image->height; y++) { 2478c2ecf20Sopenharmony_ci src = src1; 2488c2ecf20Sopenharmony_ci dst = (u32 __iomem *) dst1; 2498c2ecf20Sopenharmony_ci for (x = 0; x < image->width; x += 8) { 2508c2ecf20Sopenharmony_ci val = expand_pixel(*(src++)); 2518c2ecf20Sopenharmony_ci val = (val & fg) | (~val & bg); 2528c2ecf20Sopenharmony_ci fb_writel(val, dst++); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci src1 += image->width / 8; 2558c2ecf20Sopenharmony_ci dst1 += info->fix.line_length; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void arkfb_imageblit(struct fb_info *info, const struct fb_image *image) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if ((info->var.bits_per_pixel == 4) && (image->depth == 1) 2638c2ecf20Sopenharmony_ci && ((image->width % 8) == 0) && ((image->dx % 8) == 0)) { 2648c2ecf20Sopenharmony_ci if (info->fix.type == FB_TYPE_INTERLEAVED_PLANES) 2658c2ecf20Sopenharmony_ci arkfb_iplan_imageblit(info, image); 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci arkfb_cfb4_imageblit(info, image); 2688c2ecf20Sopenharmony_ci } else 2698c2ecf20Sopenharmony_ci cfb_imageblit(info, image); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void arkfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci if ((info->var.bits_per_pixel == 4) 2758c2ecf20Sopenharmony_ci && ((rect->width % 8) == 0) && ((rect->dx % 8) == 0) 2768c2ecf20Sopenharmony_ci && (info->fix.type == FB_TYPE_INTERLEAVED_PLANES)) 2778c2ecf20Sopenharmony_ci arkfb_iplan_fillrect(info, rect); 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci cfb_fillrect(info, rect); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cienum 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci DAC_PSEUDO8_8, 2898c2ecf20Sopenharmony_ci DAC_RGB1555_8, 2908c2ecf20Sopenharmony_ci DAC_RGB0565_8, 2918c2ecf20Sopenharmony_ci DAC_RGB0888_8, 2928c2ecf20Sopenharmony_ci DAC_RGB8888_8, 2938c2ecf20Sopenharmony_ci DAC_PSEUDO8_16, 2948c2ecf20Sopenharmony_ci DAC_RGB1555_16, 2958c2ecf20Sopenharmony_ci DAC_RGB0565_16, 2968c2ecf20Sopenharmony_ci DAC_RGB0888_16, 2978c2ecf20Sopenharmony_ci DAC_RGB8888_16, 2988c2ecf20Sopenharmony_ci DAC_MAX 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistruct dac_ops { 3028c2ecf20Sopenharmony_ci int (*dac_get_mode)(struct dac_info *info); 3038c2ecf20Sopenharmony_ci int (*dac_set_mode)(struct dac_info *info, int mode); 3048c2ecf20Sopenharmony_ci int (*dac_get_freq)(struct dac_info *info, int channel); 3058c2ecf20Sopenharmony_ci int (*dac_set_freq)(struct dac_info *info, int channel, u32 freq); 3068c2ecf20Sopenharmony_ci void (*dac_release)(struct dac_info *info); 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_citypedef void (*dac_read_regs_t)(void *data, u8 *code, int count); 3108c2ecf20Sopenharmony_citypedef void (*dac_write_regs_t)(void *data, u8 *code, int count); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistruct dac_info 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct dac_ops *dacops; 3158c2ecf20Sopenharmony_ci dac_read_regs_t dac_read_regs; 3168c2ecf20Sopenharmony_ci dac_write_regs_t dac_write_regs; 3178c2ecf20Sopenharmony_ci void *data; 3188c2ecf20Sopenharmony_ci}; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic inline u8 dac_read_reg(struct dac_info *info, u8 reg) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci u8 code[2] = {reg, 0}; 3248c2ecf20Sopenharmony_ci info->dac_read_regs(info->data, code, 1); 3258c2ecf20Sopenharmony_ci return code[1]; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic inline void dac_read_regs(struct dac_info *info, u8 *code, int count) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci info->dac_read_regs(info->data, code, count); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic inline void dac_write_reg(struct dac_info *info, u8 reg, u8 val) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci u8 code[2] = {reg, val}; 3368c2ecf20Sopenharmony_ci info->dac_write_regs(info->data, code, 1); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic inline void dac_write_regs(struct dac_info *info, u8 *code, int count) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci info->dac_write_regs(info->data, code, count); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic inline int dac_set_mode(struct dac_info *info, int mode) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci return info->dacops->dac_set_mode(info, mode); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic inline int dac_set_freq(struct dac_info *info, int channel, u32 freq) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci return info->dacops->dac_set_freq(info, channel, freq); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic inline void dac_release(struct dac_info *info) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci info->dacops->dac_release(info); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* ICS5342 DAC */ 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistruct ics5342_info 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct dac_info dac; 3688c2ecf20Sopenharmony_ci u8 mode; 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci#define DAC_PAR(info) ((struct ics5342_info *) info) 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/* LSB is set to distinguish unused slots */ 3748c2ecf20Sopenharmony_cistatic const u8 ics5342_mode_table[DAC_MAX] = { 3758c2ecf20Sopenharmony_ci [DAC_PSEUDO8_8] = 0x01, [DAC_RGB1555_8] = 0x21, [DAC_RGB0565_8] = 0x61, 3768c2ecf20Sopenharmony_ci [DAC_RGB0888_8] = 0x41, [DAC_PSEUDO8_16] = 0x11, [DAC_RGB1555_16] = 0x31, 3778c2ecf20Sopenharmony_ci [DAC_RGB0565_16] = 0x51, [DAC_RGB0888_16] = 0x91, [DAC_RGB8888_16] = 0x71 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int ics5342_set_mode(struct dac_info *info, int mode) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci u8 code; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (mode >= DAC_MAX) 3858c2ecf20Sopenharmony_ci return -EINVAL; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci code = ics5342_mode_table[mode]; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (! code) 3908c2ecf20Sopenharmony_ci return -EINVAL; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci dac_write_reg(info, 6, code & 0xF0); 3938c2ecf20Sopenharmony_ci DAC_PAR(info)->mode = mode; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic const struct svga_pll ics5342_pll = {3, 129, 3, 33, 0, 3, 3998c2ecf20Sopenharmony_ci 60000, 250000, 14318}; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* pd4 - allow only posdivider 4 (r=2) */ 4028c2ecf20Sopenharmony_cistatic const struct svga_pll ics5342_pll_pd4 = {3, 129, 3, 33, 2, 2, 4038c2ecf20Sopenharmony_ci 60000, 335000, 14318}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* 270 MHz should be upper bound for VCO clock according to specs, 4068c2ecf20Sopenharmony_ci but that is too restrictive in pd4 case */ 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int ics5342_set_freq(struct dac_info *info, int channel, u32 freq) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci u16 m, n, r; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* only postdivider 4 (r=2) is valid in mode DAC_PSEUDO8_16 */ 4138c2ecf20Sopenharmony_ci int rv = svga_compute_pll((DAC_PAR(info)->mode == DAC_PSEUDO8_16) 4148c2ecf20Sopenharmony_ci ? &ics5342_pll_pd4 : &ics5342_pll, 4158c2ecf20Sopenharmony_ci freq, &m, &n, &r, 0); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (rv < 0) { 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci u8 code[6] = {4, 3, 5, m-2, 5, (n-2) | (r << 5)}; 4218c2ecf20Sopenharmony_ci dac_write_regs(info, code, 3); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void ics5342_release(struct dac_info *info) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci ics5342_set_mode(info, DAC_PSEUDO8_8); 4298c2ecf20Sopenharmony_ci kfree(info); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic struct dac_ops ics5342_ops = { 4338c2ecf20Sopenharmony_ci .dac_set_mode = ics5342_set_mode, 4348c2ecf20Sopenharmony_ci .dac_set_freq = ics5342_set_freq, 4358c2ecf20Sopenharmony_ci .dac_release = ics5342_release 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic struct dac_info * ics5342_init(dac_read_regs_t drr, dac_write_regs_t dwr, void *data) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct dac_info *info = kzalloc(sizeof(struct ics5342_info), GFP_KERNEL); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (! info) 4448c2ecf20Sopenharmony_ci return NULL; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci info->dacops = &ics5342_ops; 4478c2ecf20Sopenharmony_ci info->dac_read_regs = drr; 4488c2ecf20Sopenharmony_ci info->dac_write_regs = dwr; 4498c2ecf20Sopenharmony_ci info->data = data; 4508c2ecf20Sopenharmony_ci DAC_PAR(info)->mode = DAC_PSEUDO8_8; /* estimation */ 4518c2ecf20Sopenharmony_ci return info; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic unsigned short dac_regs[4] = {0x3c8, 0x3c9, 0x3c6, 0x3c7}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void ark_dac_read_regs(void *data, u8 *code, int count) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct fb_info *info = data; 4638c2ecf20Sopenharmony_ci struct arkfb_info *par; 4648c2ecf20Sopenharmony_ci u8 regval; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci par = info->par; 4678c2ecf20Sopenharmony_ci regval = vga_rseq(par->state.vgabase, 0x1C); 4688c2ecf20Sopenharmony_ci while (count != 0) 4698c2ecf20Sopenharmony_ci { 4708c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x1C, regval | (code[0] & 4 ? 0x80 : 0)); 4718c2ecf20Sopenharmony_ci code[1] = vga_r(par->state.vgabase, dac_regs[code[0] & 3]); 4728c2ecf20Sopenharmony_ci count--; 4738c2ecf20Sopenharmony_ci code += 2; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x1C, regval); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void ark_dac_write_regs(void *data, u8 *code, int count) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct fb_info *info = data; 4828c2ecf20Sopenharmony_ci struct arkfb_info *par; 4838c2ecf20Sopenharmony_ci u8 regval; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci par = info->par; 4868c2ecf20Sopenharmony_ci regval = vga_rseq(par->state.vgabase, 0x1C); 4878c2ecf20Sopenharmony_ci while (count != 0) 4888c2ecf20Sopenharmony_ci { 4898c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x1C, regval | (code[0] & 4 ? 0x80 : 0)); 4908c2ecf20Sopenharmony_ci vga_w(par->state.vgabase, dac_regs[code[0] & 3], code[1]); 4918c2ecf20Sopenharmony_ci count--; 4928c2ecf20Sopenharmony_ci code += 2; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x1C, regval); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void ark_set_pixclock(struct fb_info *info, u32 pixclock) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 5028c2ecf20Sopenharmony_ci u8 regval; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci int rv = dac_set_freq(par->dac, 0, 1000000000 / pixclock); 5058c2ecf20Sopenharmony_ci if (rv < 0) { 5068c2ecf20Sopenharmony_ci fb_err(info, "cannot set requested pixclock, keeping old value\n"); 5078c2ecf20Sopenharmony_ci return; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Set VGA misc register */ 5118c2ecf20Sopenharmony_ci regval = vga_r(par->state.vgabase, VGA_MIS_R); 5128c2ecf20Sopenharmony_ci vga_w(par->state.vgabase, VGA_MIS_W, regval | VGA_MIS_ENB_PLL_LOAD); 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* Open framebuffer */ 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int arkfb_open(struct fb_info *info, int user) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 5238c2ecf20Sopenharmony_ci if (par->ref_count == 0) { 5248c2ecf20Sopenharmony_ci void __iomem *vgabase = par->state.vgabase; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci memset(&(par->state), 0, sizeof(struct vgastate)); 5278c2ecf20Sopenharmony_ci par->state.vgabase = vgabase; 5288c2ecf20Sopenharmony_ci par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS | VGA_SAVE_CMAP; 5298c2ecf20Sopenharmony_ci par->state.num_crtc = 0x60; 5308c2ecf20Sopenharmony_ci par->state.num_seq = 0x30; 5318c2ecf20Sopenharmony_ci save_vga(&(par->state)); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci par->ref_count++; 5358c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* Close framebuffer */ 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic int arkfb_release(struct fb_info *info, int user) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 5478c2ecf20Sopenharmony_ci if (par->ref_count == 0) { 5488c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 5498c2ecf20Sopenharmony_ci return -EINVAL; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (par->ref_count == 1) { 5538c2ecf20Sopenharmony_ci restore_vga(&(par->state)); 5548c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_PSEUDO8_8); 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci par->ref_count--; 5588c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* Validate passed in var */ 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic int arkfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci int rv, mem, step; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Find appropriate format */ 5708c2ecf20Sopenharmony_ci rv = svga_match_format (arkfb_formats, var, NULL); 5718c2ecf20Sopenharmony_ci if (rv < 0) 5728c2ecf20Sopenharmony_ci { 5738c2ecf20Sopenharmony_ci fb_err(info, "unsupported mode requested\n"); 5748c2ecf20Sopenharmony_ci return rv; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* Do not allow to have real resoulution larger than virtual */ 5788c2ecf20Sopenharmony_ci if (var->xres > var->xres_virtual) 5798c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (var->yres > var->yres_virtual) 5828c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Round up xres_virtual to have proper alignment of lines */ 5858c2ecf20Sopenharmony_ci step = arkfb_formats[rv].xresstep - 1; 5868c2ecf20Sopenharmony_ci var->xres_virtual = (var->xres_virtual+step) & ~step; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Check whether have enough memory */ 5908c2ecf20Sopenharmony_ci mem = ((var->bits_per_pixel * var->xres_virtual) >> 3) * var->yres_virtual; 5918c2ecf20Sopenharmony_ci if (mem > info->screen_size) 5928c2ecf20Sopenharmony_ci { 5938c2ecf20Sopenharmony_ci fb_err(info, "not enough framebuffer memory (%d kB requested, %d kB available)\n", 5948c2ecf20Sopenharmony_ci mem >> 10, (unsigned int) (info->screen_size >> 10)); 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci rv = svga_check_timings (&ark_timing_regs, var, info->node); 5998c2ecf20Sopenharmony_ci if (rv < 0) 6008c2ecf20Sopenharmony_ci { 6018c2ecf20Sopenharmony_ci fb_err(info, "invalid timings requested\n"); 6028c2ecf20Sopenharmony_ci return rv; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Interlaced mode is broken */ 6068c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_INTERLACED) 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/* Set video mode from par */ 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int arkfb_set_par(struct fb_info *info) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 6178c2ecf20Sopenharmony_ci u32 value, mode, hmul, hdiv, offset_value, screen_size; 6188c2ecf20Sopenharmony_ci u32 bpp = info->var.bits_per_pixel; 6198c2ecf20Sopenharmony_ci u8 regval; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (bpp != 0) { 6228c2ecf20Sopenharmony_ci info->fix.ypanstep = 1; 6238c2ecf20Sopenharmony_ci info->fix.line_length = (info->var.xres_virtual * bpp) / 8; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci info->flags &= ~FBINFO_MISC_TILEBLITTING; 6268c2ecf20Sopenharmony_ci info->tileops = NULL; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ 6298c2ecf20Sopenharmony_ci info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0); 6308c2ecf20Sopenharmony_ci info->pixmap.blit_y = ~(u32)0; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci offset_value = (info->var.xres_virtual * bpp) / 64; 6338c2ecf20Sopenharmony_ci screen_size = info->var.yres_virtual * info->fix.line_length; 6348c2ecf20Sopenharmony_ci } else { 6358c2ecf20Sopenharmony_ci info->fix.ypanstep = 16; 6368c2ecf20Sopenharmony_ci info->fix.line_length = 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci info->flags |= FBINFO_MISC_TILEBLITTING; 6398c2ecf20Sopenharmony_ci info->tileops = &arkfb_tile_ops; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* supports 8x16 tiles only */ 6428c2ecf20Sopenharmony_ci info->pixmap.blit_x = 1 << (8 - 1); 6438c2ecf20Sopenharmony_ci info->pixmap.blit_y = 1 << (16 - 1); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci offset_value = info->var.xres_virtual / 16; 6468c2ecf20Sopenharmony_ci screen_size = (info->var.xres_virtual * info->var.yres_virtual) / 64; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci info->var.xoffset = 0; 6508c2ecf20Sopenharmony_ci info->var.yoffset = 0; 6518c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* Unlock registers */ 6548c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x80); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Blank screen and turn off sync */ 6578c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 6588c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x00, 0x80); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Set default values */ 6618c2ecf20Sopenharmony_ci svga_set_default_gfx_regs(par->state.vgabase); 6628c2ecf20Sopenharmony_ci svga_set_default_atc_regs(par->state.vgabase); 6638c2ecf20Sopenharmony_ci svga_set_default_seq_regs(par->state.vgabase); 6648c2ecf20Sopenharmony_ci svga_set_default_crt_regs(par->state.vgabase); 6658c2ecf20Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, ark_line_compare_regs, 0xFFFFFFFF); 6668c2ecf20Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, ark_start_address_regs, 0); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* ARK specific initialization */ 6698c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x10, 0x1F, 0x1F); /* enable linear framebuffer and full memory access */ 6708c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x12, 0x03, 0x03); /* 4 MB linear framebuffer size */ 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x13, info->fix.smem_start >> 16); 6738c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x14, info->fix.smem_start >> 24); 6748c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x15, 0); 6758c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x16, 0); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* Set the FIFO threshold register */ 6788c2ecf20Sopenharmony_ci /* It is fascinating way to store 5-bit value in 8-bit register */ 6798c2ecf20Sopenharmony_ci regval = 0x10 | ((threshold & 0x0E) >> 1) | (threshold & 0x01) << 7 | (threshold & 0x10) << 1; 6808c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x18, regval); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Set the offset register */ 6838c2ecf20Sopenharmony_ci fb_dbg(info, "offset register : %d\n", offset_value); 6848c2ecf20Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, ark_offset_regs, offset_value); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* fix for hi-res textmode */ 6878c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x40, 0x08, 0x08); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (info->var.vmode & FB_VMODE_DOUBLE) 6908c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x09, 0x80, 0x80); 6918c2ecf20Sopenharmony_ci else 6928c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x09, 0x00, 0x80); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (info->var.vmode & FB_VMODE_INTERLACED) 6958c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x44, 0x04, 0x04); 6968c2ecf20Sopenharmony_ci else 6978c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x44, 0x00, 0x04); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci hmul = 1; 7008c2ecf20Sopenharmony_ci hdiv = 1; 7018c2ecf20Sopenharmony_ci mode = svga_match_format(arkfb_formats, &(info->var), &(info->fix)); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* Set mode-specific register values */ 7048c2ecf20Sopenharmony_ci switch (mode) { 7058c2ecf20Sopenharmony_ci case 0: 7068c2ecf20Sopenharmony_ci fb_dbg(info, "text mode\n"); 7078c2ecf20Sopenharmony_ci svga_set_textmode_vga_regs(par->state.vgabase); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x10); /* basic VGA mode */ 7108c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x00, 0x04); /* 8bit pixel path */ 7118c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_PSEUDO8_8); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci case 1: 7158c2ecf20Sopenharmony_ci fb_dbg(info, "4 bit pseudocolor\n"); 7168c2ecf20Sopenharmony_ci vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x10); /* basic VGA mode */ 7198c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x00, 0x04); /* 8bit pixel path */ 7208c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_PSEUDO8_8); 7218c2ecf20Sopenharmony_ci break; 7228c2ecf20Sopenharmony_ci case 2: 7238c2ecf20Sopenharmony_ci fb_dbg(info, "4 bit pseudocolor, planar\n"); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x10); /* basic VGA mode */ 7268c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x00, 0x04); /* 8bit pixel path */ 7278c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_PSEUDO8_8); 7288c2ecf20Sopenharmony_ci break; 7298c2ecf20Sopenharmony_ci case 3: 7308c2ecf20Sopenharmony_ci fb_dbg(info, "8 bit pseudocolor\n"); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x16); /* 8bpp accel mode */ 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (info->var.pixclock > 20000) { 7358c2ecf20Sopenharmony_ci fb_dbg(info, "not using multiplex\n"); 7368c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x00, 0x04); /* 8bit pixel path */ 7378c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_PSEUDO8_8); 7388c2ecf20Sopenharmony_ci } else { 7398c2ecf20Sopenharmony_ci fb_dbg(info, "using multiplex\n"); 7408c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x04, 0x04); /* 16bit pixel path */ 7418c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_PSEUDO8_16); 7428c2ecf20Sopenharmony_ci hdiv = 2; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci case 4: 7468c2ecf20Sopenharmony_ci fb_dbg(info, "5/5/5 truecolor\n"); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x1A); /* 16bpp accel mode */ 7498c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x04, 0x04); /* 16bit pixel path */ 7508c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_RGB1555_16); 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci case 5: 7538c2ecf20Sopenharmony_ci fb_dbg(info, "5/6/5 truecolor\n"); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x1A); /* 16bpp accel mode */ 7568c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x04, 0x04); /* 16bit pixel path */ 7578c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_RGB0565_16); 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci case 6: 7608c2ecf20Sopenharmony_ci fb_dbg(info, "8/8/8 truecolor\n"); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x16); /* 8bpp accel mode ??? */ 7638c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x04, 0x04); /* 16bit pixel path */ 7648c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_RGB0888_16); 7658c2ecf20Sopenharmony_ci hmul = 3; 7668c2ecf20Sopenharmony_ci hdiv = 2; 7678c2ecf20Sopenharmony_ci break; 7688c2ecf20Sopenharmony_ci case 7: 7698c2ecf20Sopenharmony_ci fb_dbg(info, "8/8/8/8 truecolor\n"); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci vga_wseq(par->state.vgabase, 0x11, 0x1E); /* 32bpp accel mode */ 7728c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x46, 0x04, 0x04); /* 16bit pixel path */ 7738c2ecf20Sopenharmony_ci dac_set_mode(par->dac, DAC_RGB8888_16); 7748c2ecf20Sopenharmony_ci hmul = 2; 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci default: 7778c2ecf20Sopenharmony_ci fb_err(info, "unsupported mode - bug\n"); 7788c2ecf20Sopenharmony_ci return -EINVAL; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci value = (hdiv * info->var.pixclock) / hmul; 7828c2ecf20Sopenharmony_ci if (!value) { 7838c2ecf20Sopenharmony_ci fb_dbg(info, "invalid pixclock\n"); 7848c2ecf20Sopenharmony_ci value = 1; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci ark_set_pixclock(info, value); 7878c2ecf20Sopenharmony_ci svga_set_timings(par->state.vgabase, &ark_timing_regs, &(info->var), hmul, hdiv, 7888c2ecf20Sopenharmony_ci (info->var.vmode & FB_VMODE_DOUBLE) ? 2 : 1, 7898c2ecf20Sopenharmony_ci (info->var.vmode & FB_VMODE_INTERLACED) ? 2 : 1, 7908c2ecf20Sopenharmony_ci hmul, info->node); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Set interlaced mode start/end register */ 7938c2ecf20Sopenharmony_ci value = info->var.xres + info->var.left_margin + info->var.right_margin + info->var.hsync_len; 7948c2ecf20Sopenharmony_ci value = ((value * hmul / hdiv) / 8) - 5; 7958c2ecf20Sopenharmony_ci vga_wcrt(par->state.vgabase, 0x42, (value + 1) / 2); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (screen_size > info->screen_size) 7988c2ecf20Sopenharmony_ci screen_size = info->screen_size; 7998c2ecf20Sopenharmony_ci memset_io(info->screen_base, 0x00, screen_size); 8008c2ecf20Sopenharmony_ci /* Device and screen back on */ 8018c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x80, 0x80); 8028c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/* Set a colour register */ 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cistatic int arkfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 8108c2ecf20Sopenharmony_ci u_int transp, struct fb_info *fb) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci switch (fb->var.bits_per_pixel) { 8138c2ecf20Sopenharmony_ci case 0: 8148c2ecf20Sopenharmony_ci case 4: 8158c2ecf20Sopenharmony_ci if (regno >= 16) 8168c2ecf20Sopenharmony_ci return -EINVAL; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if ((fb->var.bits_per_pixel == 4) && 8198c2ecf20Sopenharmony_ci (fb->var.nonstd == 0)) { 8208c2ecf20Sopenharmony_ci outb(0xF0, VGA_PEL_MSK); 8218c2ecf20Sopenharmony_ci outb(regno*16, VGA_PEL_IW); 8228c2ecf20Sopenharmony_ci } else { 8238c2ecf20Sopenharmony_ci outb(0x0F, VGA_PEL_MSK); 8248c2ecf20Sopenharmony_ci outb(regno, VGA_PEL_IW); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci outb(red >> 10, VGA_PEL_D); 8278c2ecf20Sopenharmony_ci outb(green >> 10, VGA_PEL_D); 8288c2ecf20Sopenharmony_ci outb(blue >> 10, VGA_PEL_D); 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci case 8: 8318c2ecf20Sopenharmony_ci if (regno >= 256) 8328c2ecf20Sopenharmony_ci return -EINVAL; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci outb(0xFF, VGA_PEL_MSK); 8358c2ecf20Sopenharmony_ci outb(regno, VGA_PEL_IW); 8368c2ecf20Sopenharmony_ci outb(red >> 10, VGA_PEL_D); 8378c2ecf20Sopenharmony_ci outb(green >> 10, VGA_PEL_D); 8388c2ecf20Sopenharmony_ci outb(blue >> 10, VGA_PEL_D); 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci case 16: 8418c2ecf20Sopenharmony_ci if (regno >= 16) 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (fb->var.green.length == 5) 8458c2ecf20Sopenharmony_ci ((u32*)fb->pseudo_palette)[regno] = ((red & 0xF800) >> 1) | 8468c2ecf20Sopenharmony_ci ((green & 0xF800) >> 6) | ((blue & 0xF800) >> 11); 8478c2ecf20Sopenharmony_ci else if (fb->var.green.length == 6) 8488c2ecf20Sopenharmony_ci ((u32*)fb->pseudo_palette)[regno] = (red & 0xF800) | 8498c2ecf20Sopenharmony_ci ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11); 8508c2ecf20Sopenharmony_ci else 8518c2ecf20Sopenharmony_ci return -EINVAL; 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci case 24: 8548c2ecf20Sopenharmony_ci case 32: 8558c2ecf20Sopenharmony_ci if (regno >= 16) 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci ((u32*)fb->pseudo_palette)[regno] = ((red & 0xFF00) << 8) | 8598c2ecf20Sopenharmony_ci (green & 0xFF00) | ((blue & 0xFF00) >> 8); 8608c2ecf20Sopenharmony_ci break; 8618c2ecf20Sopenharmony_ci default: 8628c2ecf20Sopenharmony_ci return -EINVAL; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci/* Set the display blanking state */ 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic int arkfb_blank(int blank_mode, struct fb_info *info) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci switch (blank_mode) { 8758c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 8768c2ecf20Sopenharmony_ci fb_dbg(info, "unblank\n"); 8778c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); 8788c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x80, 0x80); 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 8818c2ecf20Sopenharmony_ci fb_dbg(info, "blank\n"); 8828c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 8838c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x80, 0x80); 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 8868c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 8878c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 8888c2ecf20Sopenharmony_ci fb_dbg(info, "sync down\n"); 8898c2ecf20Sopenharmony_ci svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); 8908c2ecf20Sopenharmony_ci svga_wcrt_mask(par->state.vgabase, 0x17, 0x00, 0x80); 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci return 0; 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci/* Pan the display */ 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic int arkfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 9028c2ecf20Sopenharmony_ci unsigned int offset; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* Calculate the offset */ 9058c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 0) { 9068c2ecf20Sopenharmony_ci offset = (var->yoffset / 16) * (info->var.xres_virtual / 2) 9078c2ecf20Sopenharmony_ci + (var->xoffset / 2); 9088c2ecf20Sopenharmony_ci offset = offset >> 2; 9098c2ecf20Sopenharmony_ci } else { 9108c2ecf20Sopenharmony_ci offset = (var->yoffset * info->fix.line_length) + 9118c2ecf20Sopenharmony_ci (var->xoffset * info->var.bits_per_pixel / 8); 9128c2ecf20Sopenharmony_ci offset = offset >> ((info->var.bits_per_pixel == 4) ? 2 : 3); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* Set the offset */ 9168c2ecf20Sopenharmony_ci svga_wcrt_multi(par->state.vgabase, ark_start_address_regs, offset); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return 0; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci/* Frame buffer operations */ 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic const struct fb_ops arkfb_ops = { 9288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9298c2ecf20Sopenharmony_ci .fb_open = arkfb_open, 9308c2ecf20Sopenharmony_ci .fb_release = arkfb_release, 9318c2ecf20Sopenharmony_ci .fb_check_var = arkfb_check_var, 9328c2ecf20Sopenharmony_ci .fb_set_par = arkfb_set_par, 9338c2ecf20Sopenharmony_ci .fb_setcolreg = arkfb_setcolreg, 9348c2ecf20Sopenharmony_ci .fb_blank = arkfb_blank, 9358c2ecf20Sopenharmony_ci .fb_pan_display = arkfb_pan_display, 9368c2ecf20Sopenharmony_ci .fb_fillrect = arkfb_fillrect, 9378c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 9388c2ecf20Sopenharmony_ci .fb_imageblit = arkfb_imageblit, 9398c2ecf20Sopenharmony_ci .fb_get_caps = svga_get_caps, 9408c2ecf20Sopenharmony_ci}; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci/* PCI probe */ 9478c2ecf20Sopenharmony_cistatic int ark_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct pci_bus_region bus_reg; 9508c2ecf20Sopenharmony_ci struct resource vga_res; 9518c2ecf20Sopenharmony_ci struct fb_info *info; 9528c2ecf20Sopenharmony_ci struct arkfb_info *par; 9538c2ecf20Sopenharmony_ci int rc; 9548c2ecf20Sopenharmony_ci u8 regval; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* Ignore secondary VGA device because there is no VGA arbitration */ 9578c2ecf20Sopenharmony_ci if (! svga_primary_device(dev)) { 9588c2ecf20Sopenharmony_ci dev_info(&(dev->dev), "ignoring secondary device\n"); 9598c2ecf20Sopenharmony_ci return -ENODEV; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* Allocate and fill driver data structure */ 9638c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct arkfb_info), &(dev->dev)); 9648c2ecf20Sopenharmony_ci if (!info) 9658c2ecf20Sopenharmony_ci return -ENOMEM; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci par = info->par; 9688c2ecf20Sopenharmony_ci mutex_init(&par->open_lock); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN; 9718c2ecf20Sopenharmony_ci info->fbops = &arkfb_ops; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* Prepare PCI device */ 9748c2ecf20Sopenharmony_ci rc = pci_enable_device(dev); 9758c2ecf20Sopenharmony_ci if (rc < 0) { 9768c2ecf20Sopenharmony_ci dev_err(info->device, "cannot enable PCI device\n"); 9778c2ecf20Sopenharmony_ci goto err_enable_device; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci rc = pci_request_regions(dev, "arkfb"); 9818c2ecf20Sopenharmony_ci if (rc < 0) { 9828c2ecf20Sopenharmony_ci dev_err(info->device, "cannot reserve framebuffer region\n"); 9838c2ecf20Sopenharmony_ci goto err_request_regions; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci par->dac = ics5342_init(ark_dac_read_regs, ark_dac_write_regs, info); 9878c2ecf20Sopenharmony_ci if (! par->dac) { 9888c2ecf20Sopenharmony_ci rc = -ENOMEM; 9898c2ecf20Sopenharmony_ci dev_err(info->device, "RAMDAC initialization failed\n"); 9908c2ecf20Sopenharmony_ci goto err_dac; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci info->fix.smem_start = pci_resource_start(dev, 0); 9948c2ecf20Sopenharmony_ci info->fix.smem_len = pci_resource_len(dev, 0); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* Map physical IO memory address into kernel space */ 9978c2ecf20Sopenharmony_ci info->screen_base = pci_iomap_wc(dev, 0, 0); 9988c2ecf20Sopenharmony_ci if (! info->screen_base) { 9998c2ecf20Sopenharmony_ci rc = -ENOMEM; 10008c2ecf20Sopenharmony_ci dev_err(info->device, "iomap for framebuffer failed\n"); 10018c2ecf20Sopenharmony_ci goto err_iomap; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci bus_reg.start = 0; 10058c2ecf20Sopenharmony_ci bus_reg.end = 64 * 1024; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci vga_res.flags = IORESOURCE_IO; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci par->state.vgabase = (void __iomem *) (unsigned long) vga_res.start; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* FIXME get memsize */ 10148c2ecf20Sopenharmony_ci regval = vga_rseq(par->state.vgabase, 0x10); 10158c2ecf20Sopenharmony_ci info->screen_size = (1 << (regval >> 6)) << 20; 10168c2ecf20Sopenharmony_ci info->fix.smem_len = info->screen_size; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci strcpy(info->fix.id, "ARK 2000PV"); 10198c2ecf20Sopenharmony_ci info->fix.mmio_start = 0; 10208c2ecf20Sopenharmony_ci info->fix.mmio_len = 0; 10218c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 10228c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 10238c2ecf20Sopenharmony_ci info->fix.ypanstep = 0; 10248c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 10258c2ecf20Sopenharmony_ci info->pseudo_palette = (void*) (par->pseudo_palette); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* Prepare startup mode */ 10288c2ecf20Sopenharmony_ci rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); 10298c2ecf20Sopenharmony_ci if (! ((rc == 1) || (rc == 2))) { 10308c2ecf20Sopenharmony_ci rc = -EINVAL; 10318c2ecf20Sopenharmony_ci dev_err(info->device, "mode %s not found\n", mode_option); 10328c2ecf20Sopenharmony_ci goto err_find_mode; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci rc = fb_alloc_cmap(&info->cmap, 256, 0); 10368c2ecf20Sopenharmony_ci if (rc < 0) { 10378c2ecf20Sopenharmony_ci dev_err(info->device, "cannot allocate colormap\n"); 10388c2ecf20Sopenharmony_ci goto err_alloc_cmap; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci rc = register_framebuffer(info); 10428c2ecf20Sopenharmony_ci if (rc < 0) { 10438c2ecf20Sopenharmony_ci dev_err(info->device, "cannot register framebuffer\n"); 10448c2ecf20Sopenharmony_ci goto err_reg_fb; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci fb_info(info, "%s on %s, %d MB RAM\n", 10488c2ecf20Sopenharmony_ci info->fix.id, pci_name(dev), info->fix.smem_len >> 20); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* Record a reference to the driver data */ 10518c2ecf20Sopenharmony_ci pci_set_drvdata(dev, info); 10528c2ecf20Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, 10538c2ecf20Sopenharmony_ci info->fix.smem_len); 10548c2ecf20Sopenharmony_ci return 0; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Error handling */ 10578c2ecf20Sopenharmony_cierr_reg_fb: 10588c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 10598c2ecf20Sopenharmony_cierr_alloc_cmap: 10608c2ecf20Sopenharmony_cierr_find_mode: 10618c2ecf20Sopenharmony_ci pci_iounmap(dev, info->screen_base); 10628c2ecf20Sopenharmony_cierr_iomap: 10638c2ecf20Sopenharmony_ci dac_release(par->dac); 10648c2ecf20Sopenharmony_cierr_dac: 10658c2ecf20Sopenharmony_ci pci_release_regions(dev); 10668c2ecf20Sopenharmony_cierr_request_regions: 10678c2ecf20Sopenharmony_ci/* pci_disable_device(dev); */ 10688c2ecf20Sopenharmony_cierr_enable_device: 10698c2ecf20Sopenharmony_ci framebuffer_release(info); 10708c2ecf20Sopenharmony_ci return rc; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci/* PCI remove */ 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic void ark_pci_remove(struct pci_dev *dev) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(dev); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (info) { 10808c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 10818c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 10828c2ecf20Sopenharmony_ci dac_release(par->dac); 10838c2ecf20Sopenharmony_ci unregister_framebuffer(info); 10848c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci pci_iounmap(dev, info->screen_base); 10878c2ecf20Sopenharmony_ci pci_release_regions(dev); 10888c2ecf20Sopenharmony_ci/* pci_disable_device(dev); */ 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci framebuffer_release(info); 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci/* PCI suspend */ 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic int __maybe_unused ark_pci_suspend(struct device *dev) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 11008c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci dev_info(info->device, "suspend\n"); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci console_lock(); 11058c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (par->ref_count == 0) { 11088c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 11098c2ecf20Sopenharmony_ci console_unlock(); 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci fb_set_suspend(info, 1); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 11168c2ecf20Sopenharmony_ci console_unlock(); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci/* PCI resume */ 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic int __maybe_unused ark_pci_resume(struct device *dev) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 11278c2ecf20Sopenharmony_ci struct arkfb_info *par = info->par; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci dev_info(info->device, "resume\n"); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci console_lock(); 11328c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (par->ref_count == 0) 11358c2ecf20Sopenharmony_ci goto fail; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci arkfb_set_par(info); 11388c2ecf20Sopenharmony_ci fb_set_suspend(info, 0); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cifail: 11418c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 11428c2ecf20Sopenharmony_ci console_unlock(); 11438c2ecf20Sopenharmony_ci return 0; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ark_pci_pm_ops = { 11478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11488c2ecf20Sopenharmony_ci .suspend = ark_pci_suspend, 11498c2ecf20Sopenharmony_ci .resume = ark_pci_resume, 11508c2ecf20Sopenharmony_ci .freeze = NULL, 11518c2ecf20Sopenharmony_ci .thaw = ark_pci_resume, 11528c2ecf20Sopenharmony_ci .poweroff = ark_pci_suspend, 11538c2ecf20Sopenharmony_ci .restore = ark_pci_resume, 11548c2ecf20Sopenharmony_ci#endif 11558c2ecf20Sopenharmony_ci}; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci/* List of boards that we are trying to support */ 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic const struct pci_device_id ark_devices[] = { 11608c2ecf20Sopenharmony_ci {PCI_DEVICE(0xEDD8, 0xA099)}, 11618c2ecf20Sopenharmony_ci {0, 0, 0, 0, 0, 0, 0} 11628c2ecf20Sopenharmony_ci}; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ark_devices); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_cistatic struct pci_driver arkfb_pci_driver = { 11688c2ecf20Sopenharmony_ci .name = "arkfb", 11698c2ecf20Sopenharmony_ci .id_table = ark_devices, 11708c2ecf20Sopenharmony_ci .probe = ark_pci_probe, 11718c2ecf20Sopenharmony_ci .remove = ark_pci_remove, 11728c2ecf20Sopenharmony_ci .driver.pm = &ark_pci_pm_ops, 11738c2ecf20Sopenharmony_ci}; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/* Cleanup */ 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic void __exit arkfb_cleanup(void) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci pr_debug("arkfb: cleaning up\n"); 11808c2ecf20Sopenharmony_ci pci_unregister_driver(&arkfb_pci_driver); 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci/* Driver Initialisation */ 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic int __init arkfb_init(void) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci#ifndef MODULE 11898c2ecf20Sopenharmony_ci char *option = NULL; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (fb_get_options("arkfb", &option)) 11928c2ecf20Sopenharmony_ci return -ENODEV; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (option && *option) 11958c2ecf20Sopenharmony_ci mode_option = option; 11968c2ecf20Sopenharmony_ci#endif 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci pr_debug("arkfb: initializing\n"); 11998c2ecf20Sopenharmony_ci return pci_register_driver(&arkfb_pci_driver); 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cimodule_init(arkfb_init); 12038c2ecf20Sopenharmony_cimodule_exit(arkfb_cleanup); 1204