18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/video/acornfb.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1998-2001 Russell King
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Frame buffer code for Acorn platforms
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * NOTE: Most of the modes with X!=640 will disappear shortly.
108c2ecf20Sopenharmony_ci * NOTE: Startup setting of HS & VS polarity not supported.
118c2ecf20Sopenharmony_ci *       (do we need to support it if we're coming up in 640x480?)
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * FIXME: (things broken by the "new improved" FBCON API)
148c2ecf20Sopenharmony_ci *  - Blanking 8bpp displays with VIDC
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/string.h>
218c2ecf20Sopenharmony_ci#include <linux/ctype.h>
228c2ecf20Sopenharmony_ci#include <linux/mm.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/fb.h>
258c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
278c2ecf20Sopenharmony_ci#include <linux/io.h>
288c2ecf20Sopenharmony_ci#include <linux/gfp.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <mach/hardware.h>
318c2ecf20Sopenharmony_ci#include <asm/irq.h>
328c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "acornfb.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Default resolution.
388c2ecf20Sopenharmony_ci * NOTE that it has to be supported in the table towards
398c2ecf20Sopenharmony_ci * the end of this file.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci#define DEFAULT_XRES	640
428c2ecf20Sopenharmony_ci#define DEFAULT_YRES	480
438c2ecf20Sopenharmony_ci#define DEFAULT_BPP	4
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * define this to debug the video mode selection
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci#undef DEBUG_MODE_SELECTION
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Translation from RISC OS monitor types to actual
528c2ecf20Sopenharmony_ci * HSYNC and VSYNC frequency ranges.  These are
538c2ecf20Sopenharmony_ci * probably not right, but they're the best info I
548c2ecf20Sopenharmony_ci * have.  Allow 1% either way on the nominal for TVs.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci#define NR_MONTYPES	6
578c2ecf20Sopenharmony_cistatic struct fb_monspecs monspecs[NR_MONTYPES] = {
588c2ecf20Sopenharmony_ci	{	/* TV		*/
598c2ecf20Sopenharmony_ci		.hfmin	= 15469,
608c2ecf20Sopenharmony_ci		.hfmax	= 15781,
618c2ecf20Sopenharmony_ci		.vfmin	= 49,
628c2ecf20Sopenharmony_ci		.vfmax	= 51,
638c2ecf20Sopenharmony_ci	}, {	/* Multi Freq	*/
648c2ecf20Sopenharmony_ci		.hfmin	= 0,
658c2ecf20Sopenharmony_ci		.hfmax	= 99999,
668c2ecf20Sopenharmony_ci		.vfmin	= 0,
678c2ecf20Sopenharmony_ci		.vfmax	= 199,
688c2ecf20Sopenharmony_ci	}, {	/* Hi-res mono	*/
698c2ecf20Sopenharmony_ci		.hfmin	= 58608,
708c2ecf20Sopenharmony_ci		.hfmax	= 58608,
718c2ecf20Sopenharmony_ci		.vfmin	= 64,
728c2ecf20Sopenharmony_ci		.vfmax	= 64,
738c2ecf20Sopenharmony_ci	}, {	/* VGA		*/
748c2ecf20Sopenharmony_ci		.hfmin	= 30000,
758c2ecf20Sopenharmony_ci		.hfmax	= 70000,
768c2ecf20Sopenharmony_ci		.vfmin	= 60,
778c2ecf20Sopenharmony_ci		.vfmax	= 60,
788c2ecf20Sopenharmony_ci	}, {	/* SVGA		*/
798c2ecf20Sopenharmony_ci		.hfmin	= 30000,
808c2ecf20Sopenharmony_ci		.hfmax	= 70000,
818c2ecf20Sopenharmony_ci		.vfmin	= 56,
828c2ecf20Sopenharmony_ci		.vfmax	= 75,
838c2ecf20Sopenharmony_ci	}, {
848c2ecf20Sopenharmony_ci		.hfmin	= 30000,
858c2ecf20Sopenharmony_ci		.hfmax	= 70000,
868c2ecf20Sopenharmony_ci		.vfmin	= 60,
878c2ecf20Sopenharmony_ci		.vfmax	= 60,
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic struct fb_info fb_info;
928c2ecf20Sopenharmony_cistatic struct acornfb_par current_par;
938c2ecf20Sopenharmony_cistatic struct vidc_timing current_vidc;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciextern unsigned int vram_size;	/* set by setup.c */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci#ifdef HAS_VIDC20
988c2ecf20Sopenharmony_ci#include <mach/acornfb.h>
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define MAX_SIZE	(2*1024*1024)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* VIDC20 has a different set of rules from the VIDC:
1038c2ecf20Sopenharmony_ci *  hcr  : must be multiple of 4
1048c2ecf20Sopenharmony_ci *  hswr : must be even
1058c2ecf20Sopenharmony_ci *  hdsr : must be even
1068c2ecf20Sopenharmony_ci *  hder : must be even
1078c2ecf20Sopenharmony_ci *  vcr  : >= 2, (interlace, must be odd)
1088c2ecf20Sopenharmony_ci *  vswr : >= 1
1098c2ecf20Sopenharmony_ci *  vdsr : >= 1
1108c2ecf20Sopenharmony_ci *  vder : >= vdsr
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistatic void acornfb_set_timing(struct fb_info *info)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
1158c2ecf20Sopenharmony_ci	struct vidc_timing vidc;
1168c2ecf20Sopenharmony_ci	u_int vcr, fsize;
1178c2ecf20Sopenharmony_ci	u_int ext_ctl, dat_ctl;
1188c2ecf20Sopenharmony_ci	u_int words_per_line;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	memset(&vidc, 0, sizeof(vidc));
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	vidc.h_sync_width	= var->hsync_len - 8;
1238c2ecf20Sopenharmony_ci	vidc.h_border_start	= vidc.h_sync_width + var->left_margin + 8 - 12;
1248c2ecf20Sopenharmony_ci	vidc.h_display_start	= vidc.h_border_start + 12 - 18;
1258c2ecf20Sopenharmony_ci	vidc.h_display_end	= vidc.h_display_start + var->xres;
1268c2ecf20Sopenharmony_ci	vidc.h_border_end	= vidc.h_display_end + 18 - 12;
1278c2ecf20Sopenharmony_ci	vidc.h_cycle		= vidc.h_border_end + var->right_margin + 12 - 8;
1288c2ecf20Sopenharmony_ci	vidc.h_interlace	= vidc.h_cycle / 2;
1298c2ecf20Sopenharmony_ci	vidc.v_sync_width	= var->vsync_len - 1;
1308c2ecf20Sopenharmony_ci	vidc.v_border_start	= vidc.v_sync_width + var->upper_margin;
1318c2ecf20Sopenharmony_ci	vidc.v_display_start	= vidc.v_border_start;
1328c2ecf20Sopenharmony_ci	vidc.v_display_end	= vidc.v_display_start + var->yres;
1338c2ecf20Sopenharmony_ci	vidc.v_border_end	= vidc.v_display_end;
1348c2ecf20Sopenharmony_ci	vidc.control		= acornfb_default_control();
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	vcr = var->vsync_len + var->upper_margin + var->yres +
1378c2ecf20Sopenharmony_ci	      var->lower_margin;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
1408c2ecf20Sopenharmony_ci		vidc.v_cycle = (vcr - 3) / 2;
1418c2ecf20Sopenharmony_ci		vidc.control |= VIDC20_CTRL_INT;
1428c2ecf20Sopenharmony_ci	} else
1438c2ecf20Sopenharmony_ci		vidc.v_cycle = vcr - 2;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
1468c2ecf20Sopenharmony_ci	case  1: vidc.control |= VIDC20_CTRL_1BPP;	break;
1478c2ecf20Sopenharmony_ci	case  2: vidc.control |= VIDC20_CTRL_2BPP;	break;
1488c2ecf20Sopenharmony_ci	case  4: vidc.control |= VIDC20_CTRL_4BPP;	break;
1498c2ecf20Sopenharmony_ci	default:
1508c2ecf20Sopenharmony_ci	case  8: vidc.control |= VIDC20_CTRL_8BPP;	break;
1518c2ecf20Sopenharmony_ci	case 16: vidc.control |= VIDC20_CTRL_16BPP;	break;
1528c2ecf20Sopenharmony_ci	case 32: vidc.control |= VIDC20_CTRL_32BPP;	break;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	acornfb_vidc20_find_rates(&vidc, var);
1568c2ecf20Sopenharmony_ci	fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
1598c2ecf20Sopenharmony_ci		current_vidc = vidc;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		vidc_writel(VIDC20_CTRL | vidc.control);
1628c2ecf20Sopenharmony_ci		vidc_writel(0xd0000000 | vidc.pll_ctl);
1638c2ecf20Sopenharmony_ci		vidc_writel(0x80000000 | vidc.h_cycle);
1648c2ecf20Sopenharmony_ci		vidc_writel(0x81000000 | vidc.h_sync_width);
1658c2ecf20Sopenharmony_ci		vidc_writel(0x82000000 | vidc.h_border_start);
1668c2ecf20Sopenharmony_ci		vidc_writel(0x83000000 | vidc.h_display_start);
1678c2ecf20Sopenharmony_ci		vidc_writel(0x84000000 | vidc.h_display_end);
1688c2ecf20Sopenharmony_ci		vidc_writel(0x85000000 | vidc.h_border_end);
1698c2ecf20Sopenharmony_ci		vidc_writel(0x86000000);
1708c2ecf20Sopenharmony_ci		vidc_writel(0x87000000 | vidc.h_interlace);
1718c2ecf20Sopenharmony_ci		vidc_writel(0x90000000 | vidc.v_cycle);
1728c2ecf20Sopenharmony_ci		vidc_writel(0x91000000 | vidc.v_sync_width);
1738c2ecf20Sopenharmony_ci		vidc_writel(0x92000000 | vidc.v_border_start);
1748c2ecf20Sopenharmony_ci		vidc_writel(0x93000000 | vidc.v_display_start);
1758c2ecf20Sopenharmony_ci		vidc_writel(0x94000000 | vidc.v_display_end);
1768c2ecf20Sopenharmony_ci		vidc_writel(0x95000000 | vidc.v_border_end);
1778c2ecf20Sopenharmony_ci		vidc_writel(0x96000000);
1788c2ecf20Sopenharmony_ci		vidc_writel(0x97000000);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	iomd_writel(fsize, IOMD_FSIZE);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ext_ctl = acornfb_default_econtrol();
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
1868c2ecf20Sopenharmony_ci		ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
1878c2ecf20Sopenharmony_ci	else {
1888c2ecf20Sopenharmony_ci		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1898c2ecf20Sopenharmony_ci			ext_ctl |= VIDC20_ECTL_HS_HSYNC;
1908c2ecf20Sopenharmony_ci		else
1918c2ecf20Sopenharmony_ci			ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1948c2ecf20Sopenharmony_ci			ext_ctl |= VIDC20_ECTL_VS_VSYNC;
1958c2ecf20Sopenharmony_ci		else
1968c2ecf20Sopenharmony_ci			ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	vidc_writel(VIDC20_ECTL | ext_ctl);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	words_per_line = var->xres * var->bits_per_pixel / 32;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (current_par.using_vram && info->fix.smem_len == 2048*1024)
2048c2ecf20Sopenharmony_ci		words_per_line /= 2;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* RiscPC doesn't use the VIDC's VRAM control. */
2078c2ecf20Sopenharmony_ci	dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* The data bus width is dependent on both the type
2108c2ecf20Sopenharmony_ci	 * and amount of video memory.
2118c2ecf20Sopenharmony_ci	 *     DRAM	32bit low
2128c2ecf20Sopenharmony_ci	 * 1MB VRAM	32bit
2138c2ecf20Sopenharmony_ci	 * 2MB VRAM	64bit
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	if (current_par.using_vram && current_par.vram_half_sam == 2048)
2168c2ecf20Sopenharmony_ci		dat_ctl |= VIDC20_DCTL_BUS_D63_0;
2178c2ecf20Sopenharmony_ci	else
2188c2ecf20Sopenharmony_ci		dat_ctl |= VIDC20_DCTL_BUS_D31_0;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	vidc_writel(VIDC20_DCTL | dat_ctl);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci#ifdef DEBUG_MODE_SELECTION
2238c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
2248c2ecf20Sopenharmony_ci	       var->yres, var->bits_per_pixel);
2258c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-cycle          : %d\n", vidc.h_cycle);
2268c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-sync-width     : %d\n", vidc.h_sync_width);
2278c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-border-start   : %d\n", vidc.h_border_start);
2288c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-display-start  : %d\n", vidc.h_display_start);
2298c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-display-end    : %d\n", vidc.h_display_end);
2308c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-border-end     : %d\n", vidc.h_border_end);
2318c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " H-interlace      : %d\n", vidc.h_interlace);
2328c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " V-cycle          : %d\n", vidc.v_cycle);
2338c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " V-sync-width     : %d\n", vidc.v_sync_width);
2348c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " V-border-start   : %d\n", vidc.v_border_start);
2358c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " V-display-start  : %d\n", vidc.v_display_start);
2368c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " V-display-end    : %d\n", vidc.v_display_end);
2378c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " V-border-end     : %d\n", vidc.v_border_end);
2388c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " Ext Ctrl  (C)    : 0x%08X\n", ext_ctl);
2398c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " PLL Ctrl  (D)    : 0x%08X\n", vidc.pll_ctl);
2408c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " Ctrl      (E)    : 0x%08X\n", vidc.control);
2418c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " Data Ctrl (F)    : 0x%08X\n", dat_ctl);
2428c2ecf20Sopenharmony_ci	printk(KERN_DEBUG " Fsize            : 0x%08X\n", fsize);
2438c2ecf20Sopenharmony_ci#endif
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/*
2478c2ecf20Sopenharmony_ci * We have to take note of the VIDC20's 16-bit palette here.
2488c2ecf20Sopenharmony_ci * The VIDC20 looks up a 16 bit pixel as follows:
2498c2ecf20Sopenharmony_ci *
2508c2ecf20Sopenharmony_ci *   bits   111111
2518c2ecf20Sopenharmony_ci *          5432109876543210
2528c2ecf20Sopenharmony_ci *   red            ++++++++  (8 bits,  7 to 0)
2538c2ecf20Sopenharmony_ci *  green       ++++++++      (8 bits, 11 to 4)
2548c2ecf20Sopenharmony_ci *   blue   ++++++++          (8 bits, 15 to 8)
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci * We use a pixel which looks like:
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci *   bits   111111
2598c2ecf20Sopenharmony_ci *          5432109876543210
2608c2ecf20Sopenharmony_ci *   red               +++++  (5 bits,  4 to  0)
2618c2ecf20Sopenharmony_ci *  green         +++++       (5 bits,  9 to  5)
2628c2ecf20Sopenharmony_ci *   blue    +++++            (5 bits, 14 to 10)
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistatic int
2658c2ecf20Sopenharmony_ciacornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
2668c2ecf20Sopenharmony_ci		  u_int trans, struct fb_info *info)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	union palette pal;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (regno >= current_par.palette_size)
2718c2ecf20Sopenharmony_ci		return 1;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
2748c2ecf20Sopenharmony_ci		u32 pseudo_val;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		pseudo_val  = regno << info->var.red.offset;
2778c2ecf20Sopenharmony_ci		pseudo_val |= regno << info->var.green.offset;
2788c2ecf20Sopenharmony_ci		pseudo_val |= regno << info->var.blue.offset;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		((u32 *)info->pseudo_palette)[regno] = pseudo_val;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	pal.p = 0;
2848c2ecf20Sopenharmony_ci	pal.vidc20.red   = red >> 8;
2858c2ecf20Sopenharmony_ci	pal.vidc20.green = green >> 8;
2868c2ecf20Sopenharmony_ci	pal.vidc20.blue  = blue >> 8;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	current_par.palette[regno] = pal;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel == 16) {
2918c2ecf20Sopenharmony_ci		int i;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		pal.p = 0;
2948c2ecf20Sopenharmony_ci		vidc_writel(0x10000000);
2958c2ecf20Sopenharmony_ci		for (i = 0; i < 256; i += 1) {
2968c2ecf20Sopenharmony_ci			pal.vidc20.red   = current_par.palette[i       & 31].vidc20.red;
2978c2ecf20Sopenharmony_ci			pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
2988c2ecf20Sopenharmony_ci			pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
2998c2ecf20Sopenharmony_ci			vidc_writel(pal.p);
3008c2ecf20Sopenharmony_ci			/* Palette register pointer auto-increments */
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	} else {
3038c2ecf20Sopenharmony_ci		vidc_writel(0x10000000 | regno);
3048c2ecf20Sopenharmony_ci		vidc_writel(pal.p);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci#endif
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci/*
3128c2ecf20Sopenharmony_ci * Before selecting the timing parameters, adjust
3138c2ecf20Sopenharmony_ci * the resolution to fit the rules.
3148c2ecf20Sopenharmony_ci */
3158c2ecf20Sopenharmony_cistatic int
3168c2ecf20Sopenharmony_ciacornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	u_int font_line_len, sam_size, min_size, size, nr_y;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* xres must be even */
3218c2ecf20Sopenharmony_ci	var->xres = (var->xres + 1) & ~1;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/*
3248c2ecf20Sopenharmony_ci	 * We don't allow xres_virtual to differ from xres
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	var->xres_virtual = var->xres;
3278c2ecf20Sopenharmony_ci	var->xoffset = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (current_par.using_vram)
3308c2ecf20Sopenharmony_ci		sam_size = current_par.vram_half_sam * 2;
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		sam_size = 16;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/*
3358c2ecf20Sopenharmony_ci	 * Now, find a value for yres_virtual which allows
3368c2ecf20Sopenharmony_ci	 * us to do ywrap scrolling.  The value of
3378c2ecf20Sopenharmony_ci	 * yres_virtual must be such that the end of the
3388c2ecf20Sopenharmony_ci	 * displayable frame buffer must be aligned with
3398c2ecf20Sopenharmony_ci	 * the start of a font line.
3408c2ecf20Sopenharmony_ci	 */
3418c2ecf20Sopenharmony_ci	font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
3428c2ecf20Sopenharmony_ci	min_size = var->xres * var->yres * var->bits_per_pixel / 8;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/*
3458c2ecf20Sopenharmony_ci	 * If minimum screen size is greater than that we have
3468c2ecf20Sopenharmony_ci	 * available, reject it.
3478c2ecf20Sopenharmony_ci	 */
3488c2ecf20Sopenharmony_ci	if (min_size > info->fix.smem_len)
3498c2ecf20Sopenharmony_ci		return -EINVAL;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Find int 'y', such that y * fll == s * sam < maxsize
3528c2ecf20Sopenharmony_ci	 * y = s * sam / fll; s = maxsize / sam
3538c2ecf20Sopenharmony_ci	 */
3548c2ecf20Sopenharmony_ci	for (size = info->fix.smem_len;
3558c2ecf20Sopenharmony_ci	     nr_y = size / font_line_len, min_size <= size;
3568c2ecf20Sopenharmony_ci	     size -= sam_size) {
3578c2ecf20Sopenharmony_ci		if (nr_y * font_line_len == size)
3588c2ecf20Sopenharmony_ci			break;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci	nr_y *= fontht;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (var->accel_flags & FB_ACCELF_TEXT) {
3638c2ecf20Sopenharmony_ci		if (min_size > size) {
3648c2ecf20Sopenharmony_ci			/*
3658c2ecf20Sopenharmony_ci			 * failed, use ypan
3668c2ecf20Sopenharmony_ci			 */
3678c2ecf20Sopenharmony_ci			size = info->fix.smem_len;
3688c2ecf20Sopenharmony_ci			var->yres_virtual = size / (font_line_len / fontht);
3698c2ecf20Sopenharmony_ci		} else
3708c2ecf20Sopenharmony_ci			var->yres_virtual = nr_y;
3718c2ecf20Sopenharmony_ci	} else if (var->yres_virtual > nr_y)
3728c2ecf20Sopenharmony_ci		var->yres_virtual = nr_y;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	current_par.screen_end = info->fix.smem_start + size;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/*
3778c2ecf20Sopenharmony_ci	 * Fix yres & yoffset if needed.
3788c2ecf20Sopenharmony_ci	 */
3798c2ecf20Sopenharmony_ci	if (var->yres > var->yres_virtual)
3808c2ecf20Sopenharmony_ci		var->yres = var->yres_virtual;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (var->vmode & FB_VMODE_YWRAP) {
3838c2ecf20Sopenharmony_ci		if (var->yoffset > var->yres_virtual)
3848c2ecf20Sopenharmony_ci			var->yoffset = var->yres_virtual;
3858c2ecf20Sopenharmony_ci	} else {
3868c2ecf20Sopenharmony_ci		if (var->yoffset + var->yres > var->yres_virtual)
3878c2ecf20Sopenharmony_ci			var->yoffset = var->yres_virtual - var->yres;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* hsync_len must be even */
3918c2ecf20Sopenharmony_ci	var->hsync_len = (var->hsync_len + 1) & ~1;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci#if defined(HAS_VIDC20)
3948c2ecf20Sopenharmony_ci	/* left_margin must be even */
3958c2ecf20Sopenharmony_ci	if (var->left_margin & 1) {
3968c2ecf20Sopenharmony_ci		var->left_margin += 1;
3978c2ecf20Sopenharmony_ci		var->right_margin -= 1;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* right_margin must be even */
4018c2ecf20Sopenharmony_ci	if (var->right_margin & 1)
4028c2ecf20Sopenharmony_ci		var->right_margin += 1;
4038c2ecf20Sopenharmony_ci#endif
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (var->vsync_len < 1)
4068c2ecf20Sopenharmony_ci		var->vsync_len = 1;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int
4128c2ecf20Sopenharmony_ciacornfb_validate_timing(struct fb_var_screeninfo *var,
4138c2ecf20Sopenharmony_ci			struct fb_monspecs *monspecs)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	unsigned long hs, vs;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/*
4188c2ecf20Sopenharmony_ci	 * hs(Hz) = 10^12 / (pixclock * xtotal)
4198c2ecf20Sopenharmony_ci	 * vs(Hz) = hs(Hz) / ytotal
4208c2ecf20Sopenharmony_ci	 *
4218c2ecf20Sopenharmony_ci	 * No need to do long long divisions or anything
4228c2ecf20Sopenharmony_ci	 * like that if you factor it correctly
4238c2ecf20Sopenharmony_ci	 */
4248c2ecf20Sopenharmony_ci	hs = 1953125000 / var->pixclock;
4258c2ecf20Sopenharmony_ci	hs = hs * 512 /
4268c2ecf20Sopenharmony_ci	     (var->xres + var->left_margin + var->right_margin + var->hsync_len);
4278c2ecf20Sopenharmony_ci	vs = hs /
4288c2ecf20Sopenharmony_ci	     (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
4318c2ecf20Sopenharmony_ci		hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic inline void
4358c2ecf20Sopenharmony_ciacornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	u_int off = var->yoffset * info->fix.line_length;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci#if defined(HAS_MEMC)
4408c2ecf20Sopenharmony_ci	memc_write(VDMA_INIT, off >> 2);
4418c2ecf20Sopenharmony_ci#elif defined(HAS_IOMD)
4428c2ecf20Sopenharmony_ci	iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);
4438c2ecf20Sopenharmony_ci#endif
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int
4478c2ecf20Sopenharmony_ciacornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	u_int fontht;
4508c2ecf20Sopenharmony_ci	int err;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/*
4538c2ecf20Sopenharmony_ci	 * FIXME: Find the font height
4548c2ecf20Sopenharmony_ci	 */
4558c2ecf20Sopenharmony_ci	fontht = 8;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	var->red.msb_right = 0;
4588c2ecf20Sopenharmony_ci	var->green.msb_right = 0;
4598c2ecf20Sopenharmony_ci	var->blue.msb_right = 0;
4608c2ecf20Sopenharmony_ci	var->transp.msb_right = 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
4638c2ecf20Sopenharmony_ci	case 1:	case 2:	case 4:	case 8:
4648c2ecf20Sopenharmony_ci		var->red.offset    = 0;
4658c2ecf20Sopenharmony_ci		var->red.length    = var->bits_per_pixel;
4668c2ecf20Sopenharmony_ci		var->green         = var->red;
4678c2ecf20Sopenharmony_ci		var->blue          = var->red;
4688c2ecf20Sopenharmony_ci		var->transp.offset = 0;
4698c2ecf20Sopenharmony_ci		var->transp.length = 0;
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci#ifdef HAS_VIDC20
4738c2ecf20Sopenharmony_ci	case 16:
4748c2ecf20Sopenharmony_ci		var->red.offset    = 0;
4758c2ecf20Sopenharmony_ci		var->red.length    = 5;
4768c2ecf20Sopenharmony_ci		var->green.offset  = 5;
4778c2ecf20Sopenharmony_ci		var->green.length  = 5;
4788c2ecf20Sopenharmony_ci		var->blue.offset   = 10;
4798c2ecf20Sopenharmony_ci		var->blue.length   = 5;
4808c2ecf20Sopenharmony_ci		var->transp.offset = 15;
4818c2ecf20Sopenharmony_ci		var->transp.length = 1;
4828c2ecf20Sopenharmony_ci		break;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	case 32:
4858c2ecf20Sopenharmony_ci		var->red.offset    = 0;
4868c2ecf20Sopenharmony_ci		var->red.length    = 8;
4878c2ecf20Sopenharmony_ci		var->green.offset  = 8;
4888c2ecf20Sopenharmony_ci		var->green.length  = 8;
4898c2ecf20Sopenharmony_ci		var->blue.offset   = 16;
4908c2ecf20Sopenharmony_ci		var->blue.length   = 8;
4918c2ecf20Sopenharmony_ci		var->transp.offset = 24;
4928c2ecf20Sopenharmony_ci		var->transp.length = 4;
4938c2ecf20Sopenharmony_ci		break;
4948c2ecf20Sopenharmony_ci#endif
4958c2ecf20Sopenharmony_ci	default:
4968c2ecf20Sopenharmony_ci		return -EINVAL;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/*
5008c2ecf20Sopenharmony_ci	 * Check to see if the pixel rate is valid.
5018c2ecf20Sopenharmony_ci	 */
5028c2ecf20Sopenharmony_ci	if (!acornfb_valid_pixrate(var))
5038c2ecf20Sopenharmony_ci		return -EINVAL;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/*
5068c2ecf20Sopenharmony_ci	 * Validate and adjust the resolution to
5078c2ecf20Sopenharmony_ci	 * match the video generator hardware.
5088c2ecf20Sopenharmony_ci	 */
5098c2ecf20Sopenharmony_ci	err = acornfb_adjust_timing(info, var, fontht);
5108c2ecf20Sopenharmony_ci	if (err)
5118c2ecf20Sopenharmony_ci		return err;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/*
5148c2ecf20Sopenharmony_ci	 * Validate the timing against the
5158c2ecf20Sopenharmony_ci	 * monitor hardware.
5168c2ecf20Sopenharmony_ci	 */
5178c2ecf20Sopenharmony_ci	return acornfb_validate_timing(var, &info->monspecs);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int acornfb_set_par(struct fb_info *info)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	switch (info->var.bits_per_pixel) {
5238c2ecf20Sopenharmony_ci	case 1:
5248c2ecf20Sopenharmony_ci		current_par.palette_size = 2;
5258c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_MONO10;
5268c2ecf20Sopenharmony_ci		break;
5278c2ecf20Sopenharmony_ci	case 2:
5288c2ecf20Sopenharmony_ci		current_par.palette_size = 4;
5298c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
5308c2ecf20Sopenharmony_ci		break;
5318c2ecf20Sopenharmony_ci	case 4:
5328c2ecf20Sopenharmony_ci		current_par.palette_size = 16;
5338c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
5348c2ecf20Sopenharmony_ci		break;
5358c2ecf20Sopenharmony_ci	case 8:
5368c2ecf20Sopenharmony_ci		current_par.palette_size = VIDC_PALETTE_SIZE;
5378c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
5388c2ecf20Sopenharmony_ci		break;
5398c2ecf20Sopenharmony_ci#ifdef HAS_VIDC20
5408c2ecf20Sopenharmony_ci	case 16:
5418c2ecf20Sopenharmony_ci		current_par.palette_size = 32;
5428c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci	case 32:
5458c2ecf20Sopenharmony_ci		current_par.palette_size = VIDC_PALETTE_SIZE;
5468c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
5478c2ecf20Sopenharmony_ci		break;
5488c2ecf20Sopenharmony_ci#endif
5498c2ecf20Sopenharmony_ci	default:
5508c2ecf20Sopenharmony_ci		BUG();
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	info->fix.line_length	= (info->var.xres * info->var.bits_per_pixel) / 8;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci#if defined(HAS_MEMC)
5568c2ecf20Sopenharmony_ci	{
5578c2ecf20Sopenharmony_ci		unsigned long size = info->fix.smem_len - VDMA_XFERSIZE;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		memc_write(VDMA_START, 0);
5608c2ecf20Sopenharmony_ci		memc_write(VDMA_END, size >> 2);
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci#elif defined(HAS_IOMD)
5638c2ecf20Sopenharmony_ci	{
5648c2ecf20Sopenharmony_ci		unsigned long start, size;
5658c2ecf20Sopenharmony_ci		u_int control;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci		start = info->fix.smem_start;
5688c2ecf20Sopenharmony_ci		size  = current_par.screen_end;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci		if (current_par.using_vram) {
5718c2ecf20Sopenharmony_ci			size -= current_par.vram_half_sam;
5728c2ecf20Sopenharmony_ci			control = DMA_CR_E | (current_par.vram_half_sam / 256);
5738c2ecf20Sopenharmony_ci		} else {
5748c2ecf20Sopenharmony_ci			size -= 16;
5758c2ecf20Sopenharmony_ci			control = DMA_CR_E | DMA_CR_D | 16;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		iomd_writel(start,   IOMD_VIDSTART);
5798c2ecf20Sopenharmony_ci		iomd_writel(size,    IOMD_VIDEND);
5808c2ecf20Sopenharmony_ci		iomd_writel(control, IOMD_VIDCR);
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci#endif
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	acornfb_update_dma(info, &info->var);
5858c2ecf20Sopenharmony_ci	acornfb_set_timing(info);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int
5918c2ecf20Sopenharmony_ciacornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	u_int y_bottom = var->yoffset;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	if (!(var->vmode & FB_VMODE_YWRAP))
5968c2ecf20Sopenharmony_ci		y_bottom += info->var.yres;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (y_bottom > info->var.yres_virtual)
5998c2ecf20Sopenharmony_ci		return -EINVAL;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	acornfb_update_dma(info, var);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic const struct fb_ops acornfb_ops = {
6078c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
6088c2ecf20Sopenharmony_ci	.fb_check_var	= acornfb_check_var,
6098c2ecf20Sopenharmony_ci	.fb_set_par	= acornfb_set_par,
6108c2ecf20Sopenharmony_ci	.fb_setcolreg	= acornfb_setcolreg,
6118c2ecf20Sopenharmony_ci	.fb_pan_display	= acornfb_pan_display,
6128c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
6138c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
6148c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
6158c2ecf20Sopenharmony_ci};
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci/*
6188c2ecf20Sopenharmony_ci * Everything after here is initialisation!!!
6198c2ecf20Sopenharmony_ci */
6208c2ecf20Sopenharmony_cistatic struct fb_videomode modedb[] = {
6218c2ecf20Sopenharmony_ci	{	/* 320x256 @ 50Hz */
6228c2ecf20Sopenharmony_ci		NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
6238c2ecf20Sopenharmony_ci		FB_SYNC_COMP_HIGH_ACT,
6248c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6258c2ecf20Sopenharmony_ci	}, {	/* 640x250 @ 50Hz, 15.6 kHz hsync */
6268c2ecf20Sopenharmony_ci		NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
6278c2ecf20Sopenharmony_ci		0,
6288c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6298c2ecf20Sopenharmony_ci	}, {	/* 640x256 @ 50Hz, 15.6 kHz hsync */
6308c2ecf20Sopenharmony_ci		NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
6318c2ecf20Sopenharmony_ci		0,
6328c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6338c2ecf20Sopenharmony_ci	}, {	/* 640x512 @ 50Hz, 26.8 kHz hsync */
6348c2ecf20Sopenharmony_ci		NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
6358c2ecf20Sopenharmony_ci		0,
6368c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6378c2ecf20Sopenharmony_ci	}, {	/* 640x250 @ 70Hz, 31.5 kHz hsync */
6388c2ecf20Sopenharmony_ci		NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
6398c2ecf20Sopenharmony_ci		0,
6408c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6418c2ecf20Sopenharmony_ci	}, {	/* 640x256 @ 70Hz, 31.5 kHz hsync */
6428c2ecf20Sopenharmony_ci		NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
6438c2ecf20Sopenharmony_ci		0,
6448c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6458c2ecf20Sopenharmony_ci	}, {	/* 640x352 @ 70Hz, 31.5 kHz hsync */
6468c2ecf20Sopenharmony_ci		NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
6478c2ecf20Sopenharmony_ci		0,
6488c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6498c2ecf20Sopenharmony_ci	}, {	/* 640x480 @ 60Hz, 31.5 kHz hsync */
6508c2ecf20Sopenharmony_ci		NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
6518c2ecf20Sopenharmony_ci		0,
6528c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6538c2ecf20Sopenharmony_ci	}, {	/* 800x600 @ 56Hz, 35.2 kHz hsync */
6548c2ecf20Sopenharmony_ci		NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
6558c2ecf20Sopenharmony_ci		0,
6568c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6578c2ecf20Sopenharmony_ci	}, {	/* 896x352 @ 60Hz, 21.8 kHz hsync */
6588c2ecf20Sopenharmony_ci		NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
6598c2ecf20Sopenharmony_ci		0,
6608c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6618c2ecf20Sopenharmony_ci	}, {	/* 1024x 768 @ 60Hz, 48.4 kHz hsync */
6628c2ecf20Sopenharmony_ci		NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
6638c2ecf20Sopenharmony_ci		0,
6648c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6658c2ecf20Sopenharmony_ci	}, {	/* 1280x1024 @ 60Hz, 63.8 kHz hsync */
6668c2ecf20Sopenharmony_ci		NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
6678c2ecf20Sopenharmony_ci		0,
6688c2ecf20Sopenharmony_ci		FB_VMODE_NONINTERLACED
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci};
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic struct fb_videomode acornfb_default_mode = {
6738c2ecf20Sopenharmony_ci	.name =		NULL,
6748c2ecf20Sopenharmony_ci	.refresh =	60,
6758c2ecf20Sopenharmony_ci	.xres =		640,
6768c2ecf20Sopenharmony_ci	.yres =		480,
6778c2ecf20Sopenharmony_ci	.pixclock =	39722,
6788c2ecf20Sopenharmony_ci	.left_margin =	56,
6798c2ecf20Sopenharmony_ci	.right_margin =	16,
6808c2ecf20Sopenharmony_ci	.upper_margin =	34,
6818c2ecf20Sopenharmony_ci	.lower_margin =	9,
6828c2ecf20Sopenharmony_ci	.hsync_len =	88,
6838c2ecf20Sopenharmony_ci	.vsync_len =	2,
6848c2ecf20Sopenharmony_ci	.sync =		0,
6858c2ecf20Sopenharmony_ci	.vmode =	FB_VMODE_NONINTERLACED
6868c2ecf20Sopenharmony_ci};
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic void acornfb_init_fbinfo(void)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	static int first = 1;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (!first)
6938c2ecf20Sopenharmony_ci		return;
6948c2ecf20Sopenharmony_ci	first = 0;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	fb_info.fbops		= &acornfb_ops;
6978c2ecf20Sopenharmony_ci	fb_info.flags		= FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
6988c2ecf20Sopenharmony_ci	fb_info.pseudo_palette	= current_par.pseudo_palette;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	strcpy(fb_info.fix.id, "Acorn");
7018c2ecf20Sopenharmony_ci	fb_info.fix.type	= FB_TYPE_PACKED_PIXELS;
7028c2ecf20Sopenharmony_ci	fb_info.fix.type_aux	= 0;
7038c2ecf20Sopenharmony_ci	fb_info.fix.xpanstep	= 0;
7048c2ecf20Sopenharmony_ci	fb_info.fix.ypanstep	= 1;
7058c2ecf20Sopenharmony_ci	fb_info.fix.ywrapstep	= 1;
7068c2ecf20Sopenharmony_ci	fb_info.fix.line_length	= 0;
7078c2ecf20Sopenharmony_ci	fb_info.fix.accel	= FB_ACCEL_NONE;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/*
7108c2ecf20Sopenharmony_ci	 * setup initial parameters
7118c2ecf20Sopenharmony_ci	 */
7128c2ecf20Sopenharmony_ci	memset(&fb_info.var, 0, sizeof(fb_info.var));
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci#if defined(HAS_VIDC20)
7158c2ecf20Sopenharmony_ci	fb_info.var.red.length	   = 8;
7168c2ecf20Sopenharmony_ci	fb_info.var.transp.length  = 4;
7178c2ecf20Sopenharmony_ci#endif
7188c2ecf20Sopenharmony_ci	fb_info.var.green	   = fb_info.var.red;
7198c2ecf20Sopenharmony_ci	fb_info.var.blue	   = fb_info.var.red;
7208c2ecf20Sopenharmony_ci	fb_info.var.nonstd	   = 0;
7218c2ecf20Sopenharmony_ci	fb_info.var.activate	   = FB_ACTIVATE_NOW;
7228c2ecf20Sopenharmony_ci	fb_info.var.height	   = -1;
7238c2ecf20Sopenharmony_ci	fb_info.var.width	   = -1;
7248c2ecf20Sopenharmony_ci	fb_info.var.vmode	   = FB_VMODE_NONINTERLACED;
7258c2ecf20Sopenharmony_ci	fb_info.var.accel_flags	   = FB_ACCELF_TEXT;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	current_par.dram_size	   = 0;
7288c2ecf20Sopenharmony_ci	current_par.montype	   = -1;
7298c2ecf20Sopenharmony_ci	current_par.dpms	   = 0;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci/*
7338c2ecf20Sopenharmony_ci * setup acornfb options:
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci *  mon:hmin-hmax:vmin-vmax:dpms:width:height
7368c2ecf20Sopenharmony_ci *	Set monitor parameters:
7378c2ecf20Sopenharmony_ci *		hmin   = horizontal minimum frequency (Hz)
7388c2ecf20Sopenharmony_ci *		hmax   = horizontal maximum frequency (Hz)	(optional)
7398c2ecf20Sopenharmony_ci *		vmin   = vertical minimum frequency (Hz)
7408c2ecf20Sopenharmony_ci *		vmax   = vertical maximum frequency (Hz)	(optional)
7418c2ecf20Sopenharmony_ci *		dpms   = DPMS supported?			(optional)
7428c2ecf20Sopenharmony_ci *		width  = width of picture in mm.		(optional)
7438c2ecf20Sopenharmony_ci *		height = height of picture in mm.		(optional)
7448c2ecf20Sopenharmony_ci *
7458c2ecf20Sopenharmony_ci * montype:type
7468c2ecf20Sopenharmony_ci *	Set RISC-OS style monitor type:
7478c2ecf20Sopenharmony_ci *		0 (or tv)	- TV frequency
7488c2ecf20Sopenharmony_ci *		1 (or multi)	- Multi frequency
7498c2ecf20Sopenharmony_ci *		2 (or hires)	- Hi-res monochrome
7508c2ecf20Sopenharmony_ci *		3 (or vga)	- VGA
7518c2ecf20Sopenharmony_ci *		4 (or svga)	- SVGA
7528c2ecf20Sopenharmony_ci *		auto, or option missing
7538c2ecf20Sopenharmony_ci *				- try hardware detect
7548c2ecf20Sopenharmony_ci *
7558c2ecf20Sopenharmony_ci * dram:size
7568c2ecf20Sopenharmony_ci *	Set the amount of DRAM to use for the frame buffer
7578c2ecf20Sopenharmony_ci *	(even if you have VRAM).
7588c2ecf20Sopenharmony_ci *	size can optionally be followed by 'M' or 'K' for
7598c2ecf20Sopenharmony_ci *	MB or KB respectively.
7608c2ecf20Sopenharmony_ci */
7618c2ecf20Sopenharmony_cistatic void acornfb_parse_mon(char *opt)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	char *p = opt;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	current_par.montype = -2;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
7688c2ecf20Sopenharmony_ci	if (*p == '-')
7698c2ecf20Sopenharmony_ci		fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
7708c2ecf20Sopenharmony_ci	else
7718c2ecf20Sopenharmony_ci		fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	if (*p != ':')
7748c2ecf20Sopenharmony_ci		goto bad;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
7778c2ecf20Sopenharmony_ci	if (*p == '-')
7788c2ecf20Sopenharmony_ci		fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
7798c2ecf20Sopenharmony_ci	else
7808c2ecf20Sopenharmony_ci		fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (*p != ':')
7838c2ecf20Sopenharmony_ci		goto check_values;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (*p != ':')
7888c2ecf20Sopenharmony_ci		goto check_values;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	fb_info.var.width = simple_strtoul(p + 1, &p, 0);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (*p != ':')
7938c2ecf20Sopenharmony_ci		goto check_values;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cicheck_values:
7988c2ecf20Sopenharmony_ci	if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
7998c2ecf20Sopenharmony_ci	    fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
8008c2ecf20Sopenharmony_ci		goto bad;
8018c2ecf20Sopenharmony_ci	return;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cibad:
8048c2ecf20Sopenharmony_ci	printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
8058c2ecf20Sopenharmony_ci	current_par.montype = -1;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void acornfb_parse_montype(char *opt)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	current_par.montype = -2;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (strncmp(opt, "tv", 2) == 0) {
8138c2ecf20Sopenharmony_ci		opt += 2;
8148c2ecf20Sopenharmony_ci		current_par.montype = 0;
8158c2ecf20Sopenharmony_ci	} else if (strncmp(opt, "multi", 5) == 0) {
8168c2ecf20Sopenharmony_ci		opt += 5;
8178c2ecf20Sopenharmony_ci		current_par.montype = 1;
8188c2ecf20Sopenharmony_ci	} else if (strncmp(opt, "hires", 5) == 0) {
8198c2ecf20Sopenharmony_ci		opt += 5;
8208c2ecf20Sopenharmony_ci		current_par.montype = 2;
8218c2ecf20Sopenharmony_ci	} else if (strncmp(opt, "vga", 3) == 0) {
8228c2ecf20Sopenharmony_ci		opt += 3;
8238c2ecf20Sopenharmony_ci		current_par.montype = 3;
8248c2ecf20Sopenharmony_ci	} else if (strncmp(opt, "svga", 4) == 0) {
8258c2ecf20Sopenharmony_ci		opt += 4;
8268c2ecf20Sopenharmony_ci		current_par.montype = 4;
8278c2ecf20Sopenharmony_ci	} else if (strncmp(opt, "auto", 4) == 0) {
8288c2ecf20Sopenharmony_ci		opt += 4;
8298c2ecf20Sopenharmony_ci		current_par.montype = -1;
8308c2ecf20Sopenharmony_ci	} else if (isdigit(*opt))
8318c2ecf20Sopenharmony_ci		current_par.montype = simple_strtoul(opt, &opt, 0);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (current_par.montype == -2 ||
8348c2ecf20Sopenharmony_ci	    current_par.montype > NR_MONTYPES) {
8358c2ecf20Sopenharmony_ci		printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
8368c2ecf20Sopenharmony_ci			opt);
8378c2ecf20Sopenharmony_ci		current_par.montype = -1;
8388c2ecf20Sopenharmony_ci	} else
8398c2ecf20Sopenharmony_ci	if (opt && *opt) {
8408c2ecf20Sopenharmony_ci		if (strcmp(opt, ",dpms") == 0)
8418c2ecf20Sopenharmony_ci			current_par.dpms = 1;
8428c2ecf20Sopenharmony_ci		else
8438c2ecf20Sopenharmony_ci			printk(KERN_ERR
8448c2ecf20Sopenharmony_ci			       "acornfb: unknown monitor option: %s\n",
8458c2ecf20Sopenharmony_ci			       opt);
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic void acornfb_parse_dram(char *opt)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	unsigned int size;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	size = simple_strtoul(opt, &opt, 0);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	if (opt) {
8568c2ecf20Sopenharmony_ci		switch (*opt) {
8578c2ecf20Sopenharmony_ci		case 'M':
8588c2ecf20Sopenharmony_ci		case 'm':
8598c2ecf20Sopenharmony_ci			size *= 1024;
8608c2ecf20Sopenharmony_ci			fallthrough;
8618c2ecf20Sopenharmony_ci		case 'K':
8628c2ecf20Sopenharmony_ci		case 'k':
8638c2ecf20Sopenharmony_ci			size *= 1024;
8648c2ecf20Sopenharmony_ci		default:
8658c2ecf20Sopenharmony_ci			break;
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	current_par.dram_size = size;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic struct options {
8738c2ecf20Sopenharmony_ci	char *name;
8748c2ecf20Sopenharmony_ci	void (*parse)(char *opt);
8758c2ecf20Sopenharmony_ci} opt_table[] = {
8768c2ecf20Sopenharmony_ci	{ "mon",     acornfb_parse_mon     },
8778c2ecf20Sopenharmony_ci	{ "montype", acornfb_parse_montype },
8788c2ecf20Sopenharmony_ci	{ "dram",    acornfb_parse_dram    },
8798c2ecf20Sopenharmony_ci	{ NULL, NULL }
8808c2ecf20Sopenharmony_ci};
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic int acornfb_setup(char *options)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct options *optp;
8858c2ecf20Sopenharmony_ci	char *opt;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	if (!options || !*options)
8888c2ecf20Sopenharmony_ci		return 0;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	acornfb_init_fbinfo();
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	while ((opt = strsep(&options, ",")) != NULL) {
8938c2ecf20Sopenharmony_ci		if (!*opt)
8948c2ecf20Sopenharmony_ci			continue;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		for (optp = opt_table; optp->name; optp++) {
8978c2ecf20Sopenharmony_ci			int optlen;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci			optlen = strlen(optp->name);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci			if (strncmp(opt, optp->name, optlen) == 0 &&
9028c2ecf20Sopenharmony_ci			    opt[optlen] == ':') {
9038c2ecf20Sopenharmony_ci				optp->parse(opt + optlen + 1);
9048c2ecf20Sopenharmony_ci				break;
9058c2ecf20Sopenharmony_ci			}
9068c2ecf20Sopenharmony_ci		}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		if (!optp->name)
9098c2ecf20Sopenharmony_ci			printk(KERN_ERR "acornfb: unknown parameter: %s\n",
9108c2ecf20Sopenharmony_ci			       opt);
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci	return 0;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/*
9168c2ecf20Sopenharmony_ci * Detect type of monitor connected
9178c2ecf20Sopenharmony_ci *  For now, we just assume SVGA
9188c2ecf20Sopenharmony_ci */
9198c2ecf20Sopenharmony_cistatic int acornfb_detect_monitortype(void)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	return 4;
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci/*
9258c2ecf20Sopenharmony_ci * This enables the unused memory to be freed on older Acorn machines.
9268c2ecf20Sopenharmony_ci * We are freeing memory on behalf of the architecture initialisation
9278c2ecf20Sopenharmony_ci * code here.
9288c2ecf20Sopenharmony_ci */
9298c2ecf20Sopenharmony_cistatic inline void
9308c2ecf20Sopenharmony_cifree_unused_pages(unsigned int virtual_start, unsigned int virtual_end)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	int mb_freed = 0;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/*
9358c2ecf20Sopenharmony_ci	 * Align addresses
9368c2ecf20Sopenharmony_ci	 */
9378c2ecf20Sopenharmony_ci	virtual_start = PAGE_ALIGN(virtual_start);
9388c2ecf20Sopenharmony_ci	virtual_end = PAGE_ALIGN(virtual_end);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	while (virtual_start < virtual_end) {
9418c2ecf20Sopenharmony_ci		struct page *page;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci		/*
9448c2ecf20Sopenharmony_ci		 * Clear page reserved bit,
9458c2ecf20Sopenharmony_ci		 * set count to 1, and free
9468c2ecf20Sopenharmony_ci		 * the page.
9478c2ecf20Sopenharmony_ci		 */
9488c2ecf20Sopenharmony_ci		page = virt_to_page(virtual_start);
9498c2ecf20Sopenharmony_ci		__free_reserved_page(page);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		virtual_start += PAGE_SIZE;
9528c2ecf20Sopenharmony_ci		mb_freed += PAGE_SIZE / 1024;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	printk("acornfb: freed %dK memory\n", mb_freed);
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic int acornfb_probe(struct platform_device *dev)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	unsigned long size;
9618c2ecf20Sopenharmony_ci	u_int h_sync, v_sync;
9628c2ecf20Sopenharmony_ci	int rc, i;
9638c2ecf20Sopenharmony_ci	char *option = NULL;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	if (fb_get_options("acornfb", &option))
9668c2ecf20Sopenharmony_ci		return -ENODEV;
9678c2ecf20Sopenharmony_ci	acornfb_setup(option);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	acornfb_init_fbinfo();
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	current_par.dev = &dev->dev;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (current_par.montype == -1)
9748c2ecf20Sopenharmony_ci		current_par.montype = acornfb_detect_monitortype();
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
9778c2ecf20Sopenharmony_ci		current_par.montype = 4;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (current_par.montype >= 0) {
9808c2ecf20Sopenharmony_ci		fb_info.monspecs = monspecs[current_par.montype];
9818c2ecf20Sopenharmony_ci		fb_info.monspecs.dpms = current_par.dpms;
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/*
9858c2ecf20Sopenharmony_ci	 * Try to select a suitable default mode
9868c2ecf20Sopenharmony_ci	 */
9878c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(modedb); i++) {
9888c2ecf20Sopenharmony_ci		unsigned long hs;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci		hs = modedb[i].refresh *
9918c2ecf20Sopenharmony_ci		     (modedb[i].yres + modedb[i].upper_margin +
9928c2ecf20Sopenharmony_ci		      modedb[i].lower_margin + modedb[i].vsync_len);
9938c2ecf20Sopenharmony_ci		if (modedb[i].xres == DEFAULT_XRES &&
9948c2ecf20Sopenharmony_ci		    modedb[i].yres == DEFAULT_YRES &&
9958c2ecf20Sopenharmony_ci		    modedb[i].refresh >= fb_info.monspecs.vfmin &&
9968c2ecf20Sopenharmony_ci		    modedb[i].refresh <= fb_info.monspecs.vfmax &&
9978c2ecf20Sopenharmony_ci		    hs                >= fb_info.monspecs.hfmin &&
9988c2ecf20Sopenharmony_ci		    hs                <= fb_info.monspecs.hfmax) {
9998c2ecf20Sopenharmony_ci			acornfb_default_mode = modedb[i];
10008c2ecf20Sopenharmony_ci			break;
10018c2ecf20Sopenharmony_ci		}
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	fb_info.screen_base    = (char *)SCREEN_BASE;
10058c2ecf20Sopenharmony_ci	fb_info.fix.smem_start = SCREEN_START;
10068c2ecf20Sopenharmony_ci	current_par.using_vram = 0;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/*
10098c2ecf20Sopenharmony_ci	 * If vram_size is set, we are using VRAM in
10108c2ecf20Sopenharmony_ci	 * a Risc PC.  However, if the user has specified
10118c2ecf20Sopenharmony_ci	 * an amount of DRAM then use that instead.
10128c2ecf20Sopenharmony_ci	 */
10138c2ecf20Sopenharmony_ci	if (vram_size && !current_par.dram_size) {
10148c2ecf20Sopenharmony_ci		size = vram_size;
10158c2ecf20Sopenharmony_ci		current_par.vram_half_sam = vram_size / 1024;
10168c2ecf20Sopenharmony_ci		current_par.using_vram = 1;
10178c2ecf20Sopenharmony_ci	} else if (current_par.dram_size)
10188c2ecf20Sopenharmony_ci		size = current_par.dram_size;
10198c2ecf20Sopenharmony_ci	else
10208c2ecf20Sopenharmony_ci		size = MAX_SIZE;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/*
10238c2ecf20Sopenharmony_ci	 * Limit maximum screen size.
10248c2ecf20Sopenharmony_ci	 */
10258c2ecf20Sopenharmony_ci	if (size > MAX_SIZE)
10268c2ecf20Sopenharmony_ci		size = MAX_SIZE;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	size = PAGE_ALIGN(size);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci#if defined(HAS_VIDC20)
10318c2ecf20Sopenharmony_ci	if (!current_par.using_vram) {
10328c2ecf20Sopenharmony_ci		dma_addr_t handle;
10338c2ecf20Sopenharmony_ci		void *base;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci		/*
10368c2ecf20Sopenharmony_ci		 * RiscPC needs to allocate the DRAM memory
10378c2ecf20Sopenharmony_ci		 * for the framebuffer if we are not using
10388c2ecf20Sopenharmony_ci		 * VRAM.
10398c2ecf20Sopenharmony_ci		 */
10408c2ecf20Sopenharmony_ci		base = dma_alloc_wc(current_par.dev, size, &handle,
10418c2ecf20Sopenharmony_ci				    GFP_KERNEL);
10428c2ecf20Sopenharmony_ci		if (base == NULL) {
10438c2ecf20Sopenharmony_ci			printk(KERN_ERR "acornfb: unable to allocate screen memory\n");
10448c2ecf20Sopenharmony_ci			return -ENOMEM;
10458c2ecf20Sopenharmony_ci		}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci		fb_info.screen_base = base;
10488c2ecf20Sopenharmony_ci		fb_info.fix.smem_start = handle;
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci#endif
10518c2ecf20Sopenharmony_ci	fb_info.fix.smem_len = size;
10528c2ecf20Sopenharmony_ci	current_par.palette_size   = VIDC_PALETTE_SIZE;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/*
10558c2ecf20Sopenharmony_ci	 * Lookup the timing for this resolution.  If we can't
10568c2ecf20Sopenharmony_ci	 * find it, then we can't restore it if we change
10578c2ecf20Sopenharmony_ci	 * the resolution, so we disable this feature.
10588c2ecf20Sopenharmony_ci	 */
10598c2ecf20Sopenharmony_ci	do {
10608c2ecf20Sopenharmony_ci		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
10618c2ecf20Sopenharmony_ci				 ARRAY_SIZE(modedb),
10628c2ecf20Sopenharmony_ci				 &acornfb_default_mode, DEFAULT_BPP);
10638c2ecf20Sopenharmony_ci		/*
10648c2ecf20Sopenharmony_ci		 * If we found an exact match, all ok.
10658c2ecf20Sopenharmony_ci		 */
10668c2ecf20Sopenharmony_ci		if (rc == 1)
10678c2ecf20Sopenharmony_ci			break;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
10708c2ecf20Sopenharmony_ci				  &acornfb_default_mode, DEFAULT_BPP);
10718c2ecf20Sopenharmony_ci		/*
10728c2ecf20Sopenharmony_ci		 * If we found an exact match, all ok.
10738c2ecf20Sopenharmony_ci		 */
10748c2ecf20Sopenharmony_ci		if (rc == 1)
10758c2ecf20Sopenharmony_ci			break;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
10788c2ecf20Sopenharmony_ci				 ARRAY_SIZE(modedb),
10798c2ecf20Sopenharmony_ci				 &acornfb_default_mode, DEFAULT_BPP);
10808c2ecf20Sopenharmony_ci		if (rc)
10818c2ecf20Sopenharmony_ci			break;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
10848c2ecf20Sopenharmony_ci				  &acornfb_default_mode, DEFAULT_BPP);
10858c2ecf20Sopenharmony_ci	} while (0);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	/*
10888c2ecf20Sopenharmony_ci	 * If we didn't find an exact match, try the
10898c2ecf20Sopenharmony_ci	 * generic database.
10908c2ecf20Sopenharmony_ci	 */
10918c2ecf20Sopenharmony_ci	if (rc == 0) {
10928c2ecf20Sopenharmony_ci		printk("Acornfb: no valid mode found\n");
10938c2ecf20Sopenharmony_ci		return -EINVAL;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	h_sync = 1953125000 / fb_info.var.pixclock;
10978c2ecf20Sopenharmony_ci	h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
10988c2ecf20Sopenharmony_ci		 fb_info.var.right_margin + fb_info.var.hsync_len);
10998c2ecf20Sopenharmony_ci	v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
11008c2ecf20Sopenharmony_ci		 fb_info.var.lower_margin + fb_info.var.vsync_len);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, %d.%03dkHz, %dHz\n",
11038c2ecf20Sopenharmony_ci		fb_info.fix.smem_len / 1024,
11048c2ecf20Sopenharmony_ci		current_par.using_vram ? 'V' : 'D',
11058c2ecf20Sopenharmony_ci		VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
11068c2ecf20Sopenharmony_ci		h_sync / 1000, h_sync % 1000, v_sync);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
11098c2ecf20Sopenharmony_ci		fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
11108c2ecf20Sopenharmony_ci		fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
11118c2ecf20Sopenharmony_ci		fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
11128c2ecf20Sopenharmony_ci		fb_info.monspecs.dpms ? ", DPMS" : "");
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if (fb_set_var(&fb_info, &fb_info.var))
11158c2ecf20Sopenharmony_ci		printk(KERN_ERR "Acornfb: unable to set display parameters\n");
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	if (register_framebuffer(&fb_info) < 0)
11188c2ecf20Sopenharmony_ci		return -EINVAL;
11198c2ecf20Sopenharmony_ci	return 0;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic struct platform_driver acornfb_driver = {
11238c2ecf20Sopenharmony_ci	.probe	= acornfb_probe,
11248c2ecf20Sopenharmony_ci	.driver	= {
11258c2ecf20Sopenharmony_ci		.name	= "acornfb",
11268c2ecf20Sopenharmony_ci	},
11278c2ecf20Sopenharmony_ci};
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_cistatic int __init acornfb_init(void)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	return platform_driver_register(&acornfb_driver);
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cimodule_init(acornfb_init);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King");
11378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
11388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1139