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