cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

acornfb.c (27667B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/drivers/video/acornfb.c
      4 *
      5 *  Copyright (C) 1998-2001 Russell King
      6 *
      7 * Frame buffer code for Acorn platforms
      8 *
      9 * NOTE: Most of the modes with X!=640 will disappear shortly.
     10 * NOTE: Startup setting of HS & VS polarity not supported.
     11 *       (do we need to support it if we're coming up in 640x480?)
     12 *
     13 * FIXME: (things broken by the "new improved" FBCON API)
     14 *  - Blanking 8bpp displays with VIDC
     15 */
     16
     17#include <linux/module.h>
     18#include <linux/kernel.h>
     19#include <linux/errno.h>
     20#include <linux/string.h>
     21#include <linux/ctype.h>
     22#include <linux/mm.h>
     23#include <linux/init.h>
     24#include <linux/fb.h>
     25#include <linux/platform_device.h>
     26#include <linux/dma-mapping.h>
     27#include <linux/io.h>
     28#include <linux/gfp.h>
     29
     30#include <mach/hardware.h>
     31#include <asm/irq.h>
     32#include <asm/mach-types.h>
     33
     34#include "acornfb.h"
     35
     36/*
     37 * Default resolution.
     38 * NOTE that it has to be supported in the table towards
     39 * the end of this file.
     40 */
     41#define DEFAULT_XRES	640
     42#define DEFAULT_YRES	480
     43#define DEFAULT_BPP	4
     44
     45/*
     46 * define this to debug the video mode selection
     47 */
     48#undef DEBUG_MODE_SELECTION
     49
     50/*
     51 * Translation from RISC OS monitor types to actual
     52 * HSYNC and VSYNC frequency ranges.  These are
     53 * probably not right, but they're the best info I
     54 * have.  Allow 1% either way on the nominal for TVs.
     55 */
     56#define NR_MONTYPES	6
     57static struct fb_monspecs monspecs[NR_MONTYPES] = {
     58	{	/* TV		*/
     59		.hfmin	= 15469,
     60		.hfmax	= 15781,
     61		.vfmin	= 49,
     62		.vfmax	= 51,
     63	}, {	/* Multi Freq	*/
     64		.hfmin	= 0,
     65		.hfmax	= 99999,
     66		.vfmin	= 0,
     67		.vfmax	= 199,
     68	}, {	/* Hi-res mono	*/
     69		.hfmin	= 58608,
     70		.hfmax	= 58608,
     71		.vfmin	= 64,
     72		.vfmax	= 64,
     73	}, {	/* VGA		*/
     74		.hfmin	= 30000,
     75		.hfmax	= 70000,
     76		.vfmin	= 60,
     77		.vfmax	= 60,
     78	}, {	/* SVGA		*/
     79		.hfmin	= 30000,
     80		.hfmax	= 70000,
     81		.vfmin	= 56,
     82		.vfmax	= 75,
     83	}, {
     84		.hfmin	= 30000,
     85		.hfmax	= 70000,
     86		.vfmin	= 60,
     87		.vfmax	= 60,
     88	}
     89};
     90
     91static struct fb_info fb_info;
     92static struct acornfb_par current_par;
     93static struct vidc_timing current_vidc;
     94
     95extern unsigned int vram_size;	/* set by setup.c */
     96
     97#ifdef HAS_VIDC20
     98#include <mach/acornfb.h>
     99
    100#define MAX_SIZE	(2*1024*1024)
    101
    102/* VIDC20 has a different set of rules from the VIDC:
    103 *  hcr  : must be multiple of 4
    104 *  hswr : must be even
    105 *  hdsr : must be even
    106 *  hder : must be even
    107 *  vcr  : >= 2, (interlace, must be odd)
    108 *  vswr : >= 1
    109 *  vdsr : >= 1
    110 *  vder : >= vdsr
    111 */
    112static void acornfb_set_timing(struct fb_info *info)
    113{
    114	struct fb_var_screeninfo *var = &info->var;
    115	struct vidc_timing vidc;
    116	u_int vcr, fsize;
    117	u_int ext_ctl, dat_ctl;
    118	u_int words_per_line;
    119
    120	memset(&vidc, 0, sizeof(vidc));
    121
    122	vidc.h_sync_width	= var->hsync_len - 8;
    123	vidc.h_border_start	= vidc.h_sync_width + var->left_margin + 8 - 12;
    124	vidc.h_display_start	= vidc.h_border_start + 12 - 18;
    125	vidc.h_display_end	= vidc.h_display_start + var->xres;
    126	vidc.h_border_end	= vidc.h_display_end + 18 - 12;
    127	vidc.h_cycle		= vidc.h_border_end + var->right_margin + 12 - 8;
    128	vidc.h_interlace	= vidc.h_cycle / 2;
    129	vidc.v_sync_width	= var->vsync_len - 1;
    130	vidc.v_border_start	= vidc.v_sync_width + var->upper_margin;
    131	vidc.v_display_start	= vidc.v_border_start;
    132	vidc.v_display_end	= vidc.v_display_start + var->yres;
    133	vidc.v_border_end	= vidc.v_display_end;
    134	vidc.control		= acornfb_default_control();
    135
    136	vcr = var->vsync_len + var->upper_margin + var->yres +
    137	      var->lower_margin;
    138
    139	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
    140		vidc.v_cycle = (vcr - 3) / 2;
    141		vidc.control |= VIDC20_CTRL_INT;
    142	} else
    143		vidc.v_cycle = vcr - 2;
    144
    145	switch (var->bits_per_pixel) {
    146	case  1: vidc.control |= VIDC20_CTRL_1BPP;	break;
    147	case  2: vidc.control |= VIDC20_CTRL_2BPP;	break;
    148	case  4: vidc.control |= VIDC20_CTRL_4BPP;	break;
    149	default:
    150	case  8: vidc.control |= VIDC20_CTRL_8BPP;	break;
    151	case 16: vidc.control |= VIDC20_CTRL_16BPP;	break;
    152	case 32: vidc.control |= VIDC20_CTRL_32BPP;	break;
    153	}
    154
    155	acornfb_vidc20_find_rates(&vidc, var);
    156	fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1;
    157
    158	if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {
    159		current_vidc = vidc;
    160
    161		vidc_writel(VIDC20_CTRL | vidc.control);
    162		vidc_writel(0xd0000000 | vidc.pll_ctl);
    163		vidc_writel(0x80000000 | vidc.h_cycle);
    164		vidc_writel(0x81000000 | vidc.h_sync_width);
    165		vidc_writel(0x82000000 | vidc.h_border_start);
    166		vidc_writel(0x83000000 | vidc.h_display_start);
    167		vidc_writel(0x84000000 | vidc.h_display_end);
    168		vidc_writel(0x85000000 | vidc.h_border_end);
    169		vidc_writel(0x86000000);
    170		vidc_writel(0x87000000 | vidc.h_interlace);
    171		vidc_writel(0x90000000 | vidc.v_cycle);
    172		vidc_writel(0x91000000 | vidc.v_sync_width);
    173		vidc_writel(0x92000000 | vidc.v_border_start);
    174		vidc_writel(0x93000000 | vidc.v_display_start);
    175		vidc_writel(0x94000000 | vidc.v_display_end);
    176		vidc_writel(0x95000000 | vidc.v_border_end);
    177		vidc_writel(0x96000000);
    178		vidc_writel(0x97000000);
    179	}
    180
    181	iomd_writel(fsize, IOMD_FSIZE);
    182
    183	ext_ctl = acornfb_default_econtrol();
    184
    185	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */
    186		ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;
    187	else {
    188		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
    189			ext_ctl |= VIDC20_ECTL_HS_HSYNC;
    190		else
    191			ext_ctl |= VIDC20_ECTL_HS_NHSYNC;
    192
    193		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
    194			ext_ctl |= VIDC20_ECTL_VS_VSYNC;
    195		else
    196			ext_ctl |= VIDC20_ECTL_VS_NVSYNC;
    197	}
    198
    199	vidc_writel(VIDC20_ECTL | ext_ctl);
    200
    201	words_per_line = var->xres * var->bits_per_pixel / 32;
    202
    203	if (current_par.using_vram && info->fix.smem_len == 2048*1024)
    204		words_per_line /= 2;
    205
    206	/* RiscPC doesn't use the VIDC's VRAM control. */
    207	dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line;
    208
    209	/* The data bus width is dependent on both the type
    210	 * and amount of video memory.
    211	 *     DRAM	32bit low
    212	 * 1MB VRAM	32bit
    213	 * 2MB VRAM	64bit
    214	 */
    215	if (current_par.using_vram && current_par.vram_half_sam == 2048)
    216		dat_ctl |= VIDC20_DCTL_BUS_D63_0;
    217	else
    218		dat_ctl |= VIDC20_DCTL_BUS_D31_0;
    219
    220	vidc_writel(VIDC20_DCTL | dat_ctl);
    221
    222#ifdef DEBUG_MODE_SELECTION
    223	printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,
    224	       var->yres, var->bits_per_pixel);
    225	printk(KERN_DEBUG " H-cycle          : %d\n", vidc.h_cycle);
    226	printk(KERN_DEBUG " H-sync-width     : %d\n", vidc.h_sync_width);
    227	printk(KERN_DEBUG " H-border-start   : %d\n", vidc.h_border_start);
    228	printk(KERN_DEBUG " H-display-start  : %d\n", vidc.h_display_start);
    229	printk(KERN_DEBUG " H-display-end    : %d\n", vidc.h_display_end);
    230	printk(KERN_DEBUG " H-border-end     : %d\n", vidc.h_border_end);
    231	printk(KERN_DEBUG " H-interlace      : %d\n", vidc.h_interlace);
    232	printk(KERN_DEBUG " V-cycle          : %d\n", vidc.v_cycle);
    233	printk(KERN_DEBUG " V-sync-width     : %d\n", vidc.v_sync_width);
    234	printk(KERN_DEBUG " V-border-start   : %d\n", vidc.v_border_start);
    235	printk(KERN_DEBUG " V-display-start  : %d\n", vidc.v_display_start);
    236	printk(KERN_DEBUG " V-display-end    : %d\n", vidc.v_display_end);
    237	printk(KERN_DEBUG " V-border-end     : %d\n", vidc.v_border_end);
    238	printk(KERN_DEBUG " Ext Ctrl  (C)    : 0x%08X\n", ext_ctl);
    239	printk(KERN_DEBUG " PLL Ctrl  (D)    : 0x%08X\n", vidc.pll_ctl);
    240	printk(KERN_DEBUG " Ctrl      (E)    : 0x%08X\n", vidc.control);
    241	printk(KERN_DEBUG " Data Ctrl (F)    : 0x%08X\n", dat_ctl);
    242	printk(KERN_DEBUG " Fsize            : 0x%08X\n", fsize);
    243#endif
    244}
    245
    246/*
    247 * We have to take note of the VIDC20's 16-bit palette here.
    248 * The VIDC20 looks up a 16 bit pixel as follows:
    249 *
    250 *   bits   111111
    251 *          5432109876543210
    252 *   red            ++++++++  (8 bits,  7 to 0)
    253 *  green       ++++++++      (8 bits, 11 to 4)
    254 *   blue   ++++++++          (8 bits, 15 to 8)
    255 *
    256 * We use a pixel which looks like:
    257 *
    258 *   bits   111111
    259 *          5432109876543210
    260 *   red               +++++  (5 bits,  4 to  0)
    261 *  green         +++++       (5 bits,  9 to  5)
    262 *   blue    +++++            (5 bits, 14 to 10)
    263 */
    264static int
    265acornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
    266		  u_int trans, struct fb_info *info)
    267{
    268	union palette pal;
    269
    270	if (regno >= current_par.palette_size)
    271		return 1;
    272
    273	if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
    274		u32 pseudo_val;
    275
    276		pseudo_val  = regno << info->var.red.offset;
    277		pseudo_val |= regno << info->var.green.offset;
    278		pseudo_val |= regno << info->var.blue.offset;
    279
    280		((u32 *)info->pseudo_palette)[regno] = pseudo_val;
    281	}
    282
    283	pal.p = 0;
    284	pal.vidc20.red   = red >> 8;
    285	pal.vidc20.green = green >> 8;
    286	pal.vidc20.blue  = blue >> 8;
    287
    288	current_par.palette[regno] = pal;
    289
    290	if (info->var.bits_per_pixel == 16) {
    291		int i;
    292
    293		pal.p = 0;
    294		vidc_writel(0x10000000);
    295		for (i = 0; i < 256; i += 1) {
    296			pal.vidc20.red   = current_par.palette[i       & 31].vidc20.red;
    297			pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
    298			pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
    299			vidc_writel(pal.p);
    300			/* Palette register pointer auto-increments */
    301		}
    302	} else {
    303		vidc_writel(0x10000000 | regno);
    304		vidc_writel(pal.p);
    305	}
    306
    307	return 0;
    308}
    309#endif
    310
    311/*
    312 * Before selecting the timing parameters, adjust
    313 * the resolution to fit the rules.
    314 */
    315static int
    316acornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht)
    317{
    318	u_int font_line_len, sam_size, min_size, size, nr_y;
    319
    320	/* xres must be even */
    321	var->xres = (var->xres + 1) & ~1;
    322
    323	/*
    324	 * We don't allow xres_virtual to differ from xres
    325	 */
    326	var->xres_virtual = var->xres;
    327	var->xoffset = 0;
    328
    329	if (current_par.using_vram)
    330		sam_size = current_par.vram_half_sam * 2;
    331	else
    332		sam_size = 16;
    333
    334	/*
    335	 * Now, find a value for yres_virtual which allows
    336	 * us to do ywrap scrolling.  The value of
    337	 * yres_virtual must be such that the end of the
    338	 * displayable frame buffer must be aligned with
    339	 * the start of a font line.
    340	 */
    341	font_line_len = var->xres * var->bits_per_pixel * fontht / 8;
    342	min_size = var->xres * var->yres * var->bits_per_pixel / 8;
    343
    344	/*
    345	 * If minimum screen size is greater than that we have
    346	 * available, reject it.
    347	 */
    348	if (min_size > info->fix.smem_len)
    349		return -EINVAL;
    350
    351	/* Find int 'y', such that y * fll == s * sam < maxsize
    352	 * y = s * sam / fll; s = maxsize / sam
    353	 */
    354	for (size = info->fix.smem_len;
    355	     nr_y = size / font_line_len, min_size <= size;
    356	     size -= sam_size) {
    357		if (nr_y * font_line_len == size)
    358			break;
    359	}
    360	nr_y *= fontht;
    361
    362	if (var->accel_flags & FB_ACCELF_TEXT) {
    363		if (min_size > size) {
    364			/*
    365			 * failed, use ypan
    366			 */
    367			size = info->fix.smem_len;
    368			var->yres_virtual = size / (font_line_len / fontht);
    369		} else
    370			var->yres_virtual = nr_y;
    371	} else if (var->yres_virtual > nr_y)
    372		var->yres_virtual = nr_y;
    373
    374	current_par.screen_end = info->fix.smem_start + size;
    375
    376	/*
    377	 * Fix yres & yoffset if needed.
    378	 */
    379	if (var->yres > var->yres_virtual)
    380		var->yres = var->yres_virtual;
    381
    382	if (var->vmode & FB_VMODE_YWRAP) {
    383		if (var->yoffset > var->yres_virtual)
    384			var->yoffset = var->yres_virtual;
    385	} else {
    386		if (var->yoffset + var->yres > var->yres_virtual)
    387			var->yoffset = var->yres_virtual - var->yres;
    388	}
    389
    390	/* hsync_len must be even */
    391	var->hsync_len = (var->hsync_len + 1) & ~1;
    392
    393#if defined(HAS_VIDC20)
    394	/* left_margin must be even */
    395	if (var->left_margin & 1) {
    396		var->left_margin += 1;
    397		var->right_margin -= 1;
    398	}
    399
    400	/* right_margin must be even */
    401	if (var->right_margin & 1)
    402		var->right_margin += 1;
    403#endif
    404
    405	if (var->vsync_len < 1)
    406		var->vsync_len = 1;
    407
    408	return 0;
    409}
    410
    411static int
    412acornfb_validate_timing(struct fb_var_screeninfo *var,
    413			struct fb_monspecs *monspecs)
    414{
    415	unsigned long hs, vs;
    416
    417	/*
    418	 * hs(Hz) = 10^12 / (pixclock * xtotal)
    419	 * vs(Hz) = hs(Hz) / ytotal
    420	 *
    421	 * No need to do long long divisions or anything
    422	 * like that if you factor it correctly
    423	 */
    424	hs = 1953125000 / var->pixclock;
    425	hs = hs * 512 /
    426	     (var->xres + var->left_margin + var->right_margin + var->hsync_len);
    427	vs = hs /
    428	     (var->yres + var->upper_margin + var->lower_margin + var->vsync_len);
    429
    430	return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&
    431		hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;
    432}
    433
    434static inline void
    435acornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var)
    436{
    437	u_int off = var->yoffset * info->fix.line_length;
    438
    439#if defined(HAS_MEMC)
    440	memc_write(VDMA_INIT, off >> 2);
    441#elif defined(HAS_IOMD)
    442	iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);
    443#endif
    444}
    445
    446static int
    447acornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
    448{
    449	u_int fontht;
    450	int err;
    451
    452	/*
    453	 * FIXME: Find the font height
    454	 */
    455	fontht = 8;
    456
    457	var->red.msb_right = 0;
    458	var->green.msb_right = 0;
    459	var->blue.msb_right = 0;
    460	var->transp.msb_right = 0;
    461
    462	switch (var->bits_per_pixel) {
    463	case 1:	case 2:	case 4:	case 8:
    464		var->red.offset    = 0;
    465		var->red.length    = var->bits_per_pixel;
    466		var->green         = var->red;
    467		var->blue          = var->red;
    468		var->transp.offset = 0;
    469		var->transp.length = 0;
    470		break;
    471
    472#ifdef HAS_VIDC20
    473	case 16:
    474		var->red.offset    = 0;
    475		var->red.length    = 5;
    476		var->green.offset  = 5;
    477		var->green.length  = 5;
    478		var->blue.offset   = 10;
    479		var->blue.length   = 5;
    480		var->transp.offset = 15;
    481		var->transp.length = 1;
    482		break;
    483
    484	case 32:
    485		var->red.offset    = 0;
    486		var->red.length    = 8;
    487		var->green.offset  = 8;
    488		var->green.length  = 8;
    489		var->blue.offset   = 16;
    490		var->blue.length   = 8;
    491		var->transp.offset = 24;
    492		var->transp.length = 4;
    493		break;
    494#endif
    495	default:
    496		return -EINVAL;
    497	}
    498
    499	/*
    500	 * Check to see if the pixel rate is valid.
    501	 */
    502	if (!acornfb_valid_pixrate(var))
    503		return -EINVAL;
    504
    505	/*
    506	 * Validate and adjust the resolution to
    507	 * match the video generator hardware.
    508	 */
    509	err = acornfb_adjust_timing(info, var, fontht);
    510	if (err)
    511		return err;
    512
    513	/*
    514	 * Validate the timing against the
    515	 * monitor hardware.
    516	 */
    517	return acornfb_validate_timing(var, &info->monspecs);
    518}
    519
    520static int acornfb_set_par(struct fb_info *info)
    521{
    522	switch (info->var.bits_per_pixel) {
    523	case 1:
    524		current_par.palette_size = 2;
    525		info->fix.visual = FB_VISUAL_MONO10;
    526		break;
    527	case 2:
    528		current_par.palette_size = 4;
    529		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
    530		break;
    531	case 4:
    532		current_par.palette_size = 16;
    533		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
    534		break;
    535	case 8:
    536		current_par.palette_size = VIDC_PALETTE_SIZE;
    537		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
    538		break;
    539#ifdef HAS_VIDC20
    540	case 16:
    541		current_par.palette_size = 32;
    542		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
    543		break;
    544	case 32:
    545		current_par.palette_size = VIDC_PALETTE_SIZE;
    546		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
    547		break;
    548#endif
    549	default:
    550		BUG();
    551	}
    552
    553	info->fix.line_length	= (info->var.xres * info->var.bits_per_pixel) / 8;
    554
    555#if defined(HAS_MEMC)
    556	{
    557		unsigned long size = info->fix.smem_len - VDMA_XFERSIZE;
    558
    559		memc_write(VDMA_START, 0);
    560		memc_write(VDMA_END, size >> 2);
    561	}
    562#elif defined(HAS_IOMD)
    563	{
    564		unsigned long start, size;
    565		u_int control;
    566
    567		start = info->fix.smem_start;
    568		size  = current_par.screen_end;
    569
    570		if (current_par.using_vram) {
    571			size -= current_par.vram_half_sam;
    572			control = DMA_CR_E | (current_par.vram_half_sam / 256);
    573		} else {
    574			size -= 16;
    575			control = DMA_CR_E | DMA_CR_D | 16;
    576		}
    577
    578		iomd_writel(start,   IOMD_VIDSTART);
    579		iomd_writel(size,    IOMD_VIDEND);
    580		iomd_writel(control, IOMD_VIDCR);
    581	}
    582#endif
    583
    584	acornfb_update_dma(info, &info->var);
    585	acornfb_set_timing(info);
    586
    587	return 0;
    588}
    589
    590static int
    591acornfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
    592{
    593	u_int y_bottom = var->yoffset;
    594
    595	if (!(var->vmode & FB_VMODE_YWRAP))
    596		y_bottom += info->var.yres;
    597
    598	if (y_bottom > info->var.yres_virtual)
    599		return -EINVAL;
    600
    601	acornfb_update_dma(info, var);
    602
    603	return 0;
    604}
    605
    606static const struct fb_ops acornfb_ops = {
    607	.owner		= THIS_MODULE,
    608	.fb_check_var	= acornfb_check_var,
    609	.fb_set_par	= acornfb_set_par,
    610	.fb_setcolreg	= acornfb_setcolreg,
    611	.fb_pan_display	= acornfb_pan_display,
    612	.fb_fillrect	= cfb_fillrect,
    613	.fb_copyarea	= cfb_copyarea,
    614	.fb_imageblit	= cfb_imageblit,
    615};
    616
    617/*
    618 * Everything after here is initialisation!!!
    619 */
    620static struct fb_videomode modedb[] = {
    621	{	/* 320x256 @ 50Hz */
    622		NULL, 50,  320,  256, 125000,  92,  62,  35, 19,  38, 2,
    623		FB_SYNC_COMP_HIGH_ACT,
    624		FB_VMODE_NONINTERLACED
    625	}, {	/* 640x250 @ 50Hz, 15.6 kHz hsync */
    626		NULL, 50,  640,  250,  62500, 185, 123,  38, 21,  76, 3,
    627		0,
    628		FB_VMODE_NONINTERLACED
    629	}, {	/* 640x256 @ 50Hz, 15.6 kHz hsync */
    630		NULL, 50,  640,  256,  62500, 185, 123,  35, 18,  76, 3,
    631		0,
    632		FB_VMODE_NONINTERLACED
    633	}, {	/* 640x512 @ 50Hz, 26.8 kHz hsync */
    634		NULL, 50,  640,  512,  41667, 113,  87,  18,  1,  56, 3,
    635		0,
    636		FB_VMODE_NONINTERLACED
    637	}, {	/* 640x250 @ 70Hz, 31.5 kHz hsync */
    638		NULL, 70,  640,  250,  39722,  48,  16, 109, 88,  96, 2,
    639		0,
    640		FB_VMODE_NONINTERLACED
    641	}, {	/* 640x256 @ 70Hz, 31.5 kHz hsync */
    642		NULL, 70,  640,  256,  39722,  48,  16, 106, 85,  96, 2,
    643		0,
    644		FB_VMODE_NONINTERLACED
    645	}, {	/* 640x352 @ 70Hz, 31.5 kHz hsync */
    646		NULL, 70,  640,  352,  39722,  48,  16,  58, 37,  96, 2,
    647		0,
    648		FB_VMODE_NONINTERLACED
    649	}, {	/* 640x480 @ 60Hz, 31.5 kHz hsync */
    650		NULL, 60,  640,  480,  39722,  48,  16,  32, 11,  96, 2,
    651		0,
    652		FB_VMODE_NONINTERLACED
    653	}, {	/* 800x600 @ 56Hz, 35.2 kHz hsync */
    654		NULL, 56,  800,  600,  27778, 101,  23,  22,  1, 100, 2,
    655		0,
    656		FB_VMODE_NONINTERLACED
    657	}, {	/* 896x352 @ 60Hz, 21.8 kHz hsync */
    658		NULL, 60,  896,  352,  41667,  59,  27,   9,  0, 118, 3,
    659		0,
    660		FB_VMODE_NONINTERLACED
    661	}, {	/* 1024x 768 @ 60Hz, 48.4 kHz hsync */
    662		NULL, 60, 1024,  768,  15385, 160,  24,  29,  3, 136, 6,
    663		0,
    664		FB_VMODE_NONINTERLACED
    665	}, {	/* 1280x1024 @ 60Hz, 63.8 kHz hsync */
    666		NULL, 60, 1280, 1024,   9090, 186,  96,  38,  1, 160, 3,
    667		0,
    668		FB_VMODE_NONINTERLACED
    669	}
    670};
    671
    672static struct fb_videomode acornfb_default_mode = {
    673	.name =		NULL,
    674	.refresh =	60,
    675	.xres =		640,
    676	.yres =		480,
    677	.pixclock =	39722,
    678	.left_margin =	56,
    679	.right_margin =	16,
    680	.upper_margin =	34,
    681	.lower_margin =	9,
    682	.hsync_len =	88,
    683	.vsync_len =	2,
    684	.sync =		0,
    685	.vmode =	FB_VMODE_NONINTERLACED
    686};
    687
    688static void acornfb_init_fbinfo(void)
    689{
    690	static int first = 1;
    691
    692	if (!first)
    693		return;
    694	first = 0;
    695
    696	fb_info.fbops		= &acornfb_ops;
    697	fb_info.flags		= FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
    698	fb_info.pseudo_palette	= current_par.pseudo_palette;
    699
    700	strcpy(fb_info.fix.id, "Acorn");
    701	fb_info.fix.type	= FB_TYPE_PACKED_PIXELS;
    702	fb_info.fix.type_aux	= 0;
    703	fb_info.fix.xpanstep	= 0;
    704	fb_info.fix.ypanstep	= 1;
    705	fb_info.fix.ywrapstep	= 1;
    706	fb_info.fix.line_length	= 0;
    707	fb_info.fix.accel	= FB_ACCEL_NONE;
    708
    709	/*
    710	 * setup initial parameters
    711	 */
    712	memset(&fb_info.var, 0, sizeof(fb_info.var));
    713
    714#if defined(HAS_VIDC20)
    715	fb_info.var.red.length	   = 8;
    716	fb_info.var.transp.length  = 4;
    717#endif
    718	fb_info.var.green	   = fb_info.var.red;
    719	fb_info.var.blue	   = fb_info.var.red;
    720	fb_info.var.nonstd	   = 0;
    721	fb_info.var.activate	   = FB_ACTIVATE_NOW;
    722	fb_info.var.height	   = -1;
    723	fb_info.var.width	   = -1;
    724	fb_info.var.vmode	   = FB_VMODE_NONINTERLACED;
    725	fb_info.var.accel_flags	   = FB_ACCELF_TEXT;
    726
    727	current_par.dram_size	   = 0;
    728	current_par.montype	   = -1;
    729	current_par.dpms	   = 0;
    730}
    731
    732/*
    733 * setup acornfb options:
    734 *
    735 *  mon:hmin-hmax:vmin-vmax:dpms:width:height
    736 *	Set monitor parameters:
    737 *		hmin   = horizontal minimum frequency (Hz)
    738 *		hmax   = horizontal maximum frequency (Hz)	(optional)
    739 *		vmin   = vertical minimum frequency (Hz)
    740 *		vmax   = vertical maximum frequency (Hz)	(optional)
    741 *		dpms   = DPMS supported?			(optional)
    742 *		width  = width of picture in mm.		(optional)
    743 *		height = height of picture in mm.		(optional)
    744 *
    745 * montype:type
    746 *	Set RISC-OS style monitor type:
    747 *		0 (or tv)	- TV frequency
    748 *		1 (or multi)	- Multi frequency
    749 *		2 (or hires)	- Hi-res monochrome
    750 *		3 (or vga)	- VGA
    751 *		4 (or svga)	- SVGA
    752 *		auto, or option missing
    753 *				- try hardware detect
    754 *
    755 * dram:size
    756 *	Set the amount of DRAM to use for the frame buffer
    757 *	(even if you have VRAM).
    758 *	size can optionally be followed by 'M' or 'K' for
    759 *	MB or KB respectively.
    760 */
    761static void acornfb_parse_mon(char *opt)
    762{
    763	char *p = opt;
    764
    765	current_par.montype = -2;
    766
    767	fb_info.monspecs.hfmin = simple_strtoul(p, &p, 0);
    768	if (*p == '-')
    769		fb_info.monspecs.hfmax = simple_strtoul(p + 1, &p, 0);
    770	else
    771		fb_info.monspecs.hfmax = fb_info.monspecs.hfmin;
    772
    773	if (*p != ':')
    774		goto bad;
    775
    776	fb_info.monspecs.vfmin = simple_strtoul(p + 1, &p, 0);
    777	if (*p == '-')
    778		fb_info.monspecs.vfmax = simple_strtoul(p + 1, &p, 0);
    779	else
    780		fb_info.monspecs.vfmax = fb_info.monspecs.vfmin;
    781
    782	if (*p != ':')
    783		goto check_values;
    784
    785	fb_info.monspecs.dpms = simple_strtoul(p + 1, &p, 0);
    786
    787	if (*p != ':')
    788		goto check_values;
    789
    790	fb_info.var.width = simple_strtoul(p + 1, &p, 0);
    791
    792	if (*p != ':')
    793		goto check_values;
    794
    795	fb_info.var.height = simple_strtoul(p + 1, NULL, 0);
    796
    797check_values:
    798	if (fb_info.monspecs.hfmax < fb_info.monspecs.hfmin ||
    799	    fb_info.monspecs.vfmax < fb_info.monspecs.vfmin)
    800		goto bad;
    801	return;
    802
    803bad:
    804	printk(KERN_ERR "Acornfb: bad monitor settings: %s\n", opt);
    805	current_par.montype = -1;
    806}
    807
    808static void acornfb_parse_montype(char *opt)
    809{
    810	current_par.montype = -2;
    811
    812	if (strncmp(opt, "tv", 2) == 0) {
    813		opt += 2;
    814		current_par.montype = 0;
    815	} else if (strncmp(opt, "multi", 5) == 0) {
    816		opt += 5;
    817		current_par.montype = 1;
    818	} else if (strncmp(opt, "hires", 5) == 0) {
    819		opt += 5;
    820		current_par.montype = 2;
    821	} else if (strncmp(opt, "vga", 3) == 0) {
    822		opt += 3;
    823		current_par.montype = 3;
    824	} else if (strncmp(opt, "svga", 4) == 0) {
    825		opt += 4;
    826		current_par.montype = 4;
    827	} else if (strncmp(opt, "auto", 4) == 0) {
    828		opt += 4;
    829		current_par.montype = -1;
    830	} else if (isdigit(*opt))
    831		current_par.montype = simple_strtoul(opt, &opt, 0);
    832
    833	if (current_par.montype == -2 ||
    834	    current_par.montype > NR_MONTYPES) {
    835		printk(KERN_ERR "acornfb: unknown monitor type: %s\n",
    836			opt);
    837		current_par.montype = -1;
    838	} else
    839	if (opt && *opt) {
    840		if (strcmp(opt, ",dpms") == 0)
    841			current_par.dpms = 1;
    842		else
    843			printk(KERN_ERR
    844			       "acornfb: unknown monitor option: %s\n",
    845			       opt);
    846	}
    847}
    848
    849static void acornfb_parse_dram(char *opt)
    850{
    851	unsigned int size;
    852
    853	size = simple_strtoul(opt, &opt, 0);
    854
    855	if (opt) {
    856		switch (*opt) {
    857		case 'M':
    858		case 'm':
    859			size *= 1024;
    860			fallthrough;
    861		case 'K':
    862		case 'k':
    863			size *= 1024;
    864		default:
    865			break;
    866		}
    867	}
    868
    869	current_par.dram_size = size;
    870}
    871
    872static struct options {
    873	char *name;
    874	void (*parse)(char *opt);
    875} opt_table[] = {
    876	{ "mon",     acornfb_parse_mon     },
    877	{ "montype", acornfb_parse_montype },
    878	{ "dram",    acornfb_parse_dram    },
    879	{ NULL, NULL }
    880};
    881
    882static int acornfb_setup(char *options)
    883{
    884	struct options *optp;
    885	char *opt;
    886
    887	if (!options || !*options)
    888		return 0;
    889
    890	acornfb_init_fbinfo();
    891
    892	while ((opt = strsep(&options, ",")) != NULL) {
    893		if (!*opt)
    894			continue;
    895
    896		for (optp = opt_table; optp->name; optp++) {
    897			int optlen;
    898
    899			optlen = strlen(optp->name);
    900
    901			if (strncmp(opt, optp->name, optlen) == 0 &&
    902			    opt[optlen] == ':') {
    903				optp->parse(opt + optlen + 1);
    904				break;
    905			}
    906		}
    907
    908		if (!optp->name)
    909			printk(KERN_ERR "acornfb: unknown parameter: %s\n",
    910			       opt);
    911	}
    912	return 0;
    913}
    914
    915/*
    916 * Detect type of monitor connected
    917 *  For now, we just assume SVGA
    918 */
    919static int acornfb_detect_monitortype(void)
    920{
    921	return 4;
    922}
    923
    924static int acornfb_probe(struct platform_device *dev)
    925{
    926	unsigned long size;
    927	u_int h_sync, v_sync;
    928	int rc, i;
    929	char *option = NULL;
    930
    931	if (fb_get_options("acornfb", &option))
    932		return -ENODEV;
    933	acornfb_setup(option);
    934
    935	acornfb_init_fbinfo();
    936
    937	current_par.dev = &dev->dev;
    938
    939	if (current_par.montype == -1)
    940		current_par.montype = acornfb_detect_monitortype();
    941
    942	if (current_par.montype == -1 || current_par.montype > NR_MONTYPES)
    943		current_par.montype = 4;
    944
    945	if (current_par.montype >= 0) {
    946		fb_info.monspecs = monspecs[current_par.montype];
    947		fb_info.monspecs.dpms = current_par.dpms;
    948	}
    949
    950	/*
    951	 * Try to select a suitable default mode
    952	 */
    953	for (i = 0; i < ARRAY_SIZE(modedb); i++) {
    954		unsigned long hs;
    955
    956		hs = modedb[i].refresh *
    957		     (modedb[i].yres + modedb[i].upper_margin +
    958		      modedb[i].lower_margin + modedb[i].vsync_len);
    959		if (modedb[i].xres == DEFAULT_XRES &&
    960		    modedb[i].yres == DEFAULT_YRES &&
    961		    modedb[i].refresh >= fb_info.monspecs.vfmin &&
    962		    modedb[i].refresh <= fb_info.monspecs.vfmax &&
    963		    hs                >= fb_info.monspecs.hfmin &&
    964		    hs                <= fb_info.monspecs.hfmax) {
    965			acornfb_default_mode = modedb[i];
    966			break;
    967		}
    968	}
    969
    970	fb_info.screen_base    = (char *)SCREEN_BASE;
    971	fb_info.fix.smem_start = SCREEN_START;
    972	current_par.using_vram = 0;
    973
    974	/*
    975	 * If vram_size is set, we are using VRAM in
    976	 * a Risc PC.  However, if the user has specified
    977	 * an amount of DRAM then use that instead.
    978	 */
    979	if (vram_size && !current_par.dram_size) {
    980		size = vram_size;
    981		current_par.vram_half_sam = vram_size / 1024;
    982		current_par.using_vram = 1;
    983	} else if (current_par.dram_size)
    984		size = current_par.dram_size;
    985	else
    986		size = MAX_SIZE;
    987
    988	/*
    989	 * Limit maximum screen size.
    990	 */
    991	if (size > MAX_SIZE)
    992		size = MAX_SIZE;
    993
    994	size = PAGE_ALIGN(size);
    995
    996#if defined(HAS_VIDC20)
    997	if (!current_par.using_vram) {
    998		dma_addr_t handle;
    999		void *base;
   1000
   1001		/*
   1002		 * RiscPC needs to allocate the DRAM memory
   1003		 * for the framebuffer if we are not using
   1004		 * VRAM.
   1005		 */
   1006		base = dma_alloc_wc(current_par.dev, size, &handle,
   1007				    GFP_KERNEL);
   1008		if (base == NULL) {
   1009			printk(KERN_ERR "acornfb: unable to allocate screen memory\n");
   1010			return -ENOMEM;
   1011		}
   1012
   1013		fb_info.screen_base = base;
   1014		fb_info.fix.smem_start = handle;
   1015	}
   1016#endif
   1017	fb_info.fix.smem_len = size;
   1018	current_par.palette_size   = VIDC_PALETTE_SIZE;
   1019
   1020	/*
   1021	 * Lookup the timing for this resolution.  If we can't
   1022	 * find it, then we can't restore it if we change
   1023	 * the resolution, so we disable this feature.
   1024	 */
   1025	do {
   1026		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
   1027				 ARRAY_SIZE(modedb),
   1028				 &acornfb_default_mode, DEFAULT_BPP);
   1029		/*
   1030		 * If we found an exact match, all ok.
   1031		 */
   1032		if (rc == 1)
   1033			break;
   1034
   1035		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
   1036				  &acornfb_default_mode, DEFAULT_BPP);
   1037		/*
   1038		 * If we found an exact match, all ok.
   1039		 */
   1040		if (rc == 1)
   1041			break;
   1042
   1043		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, modedb,
   1044				 ARRAY_SIZE(modedb),
   1045				 &acornfb_default_mode, DEFAULT_BPP);
   1046		if (rc)
   1047			break;
   1048
   1049		rc = fb_find_mode(&fb_info.var, &fb_info, NULL, NULL, 0,
   1050				  &acornfb_default_mode, DEFAULT_BPP);
   1051	} while (0);
   1052
   1053	/*
   1054	 * If we didn't find an exact match, try the
   1055	 * generic database.
   1056	 */
   1057	if (rc == 0) {
   1058		printk("Acornfb: no valid mode found\n");
   1059		return -EINVAL;
   1060	}
   1061
   1062	h_sync = 1953125000 / fb_info.var.pixclock;
   1063	h_sync = h_sync * 512 / (fb_info.var.xres + fb_info.var.left_margin +
   1064		 fb_info.var.right_margin + fb_info.var.hsync_len);
   1065	v_sync = h_sync / (fb_info.var.yres + fb_info.var.upper_margin +
   1066		 fb_info.var.lower_margin + fb_info.var.vsync_len);
   1067
   1068	printk(KERN_INFO "Acornfb: %dkB %cRAM, %s, using %dx%d, %d.%03dkHz, %dHz\n",
   1069		fb_info.fix.smem_len / 1024,
   1070		current_par.using_vram ? 'V' : 'D',
   1071		VIDC_NAME, fb_info.var.xres, fb_info.var.yres,
   1072		h_sync / 1000, h_sync % 1000, v_sync);
   1073
   1074	printk(KERN_INFO "Acornfb: Monitor: %d.%03d-%d.%03dkHz, %d-%dHz%s\n",
   1075		fb_info.monspecs.hfmin / 1000, fb_info.monspecs.hfmin % 1000,
   1076		fb_info.monspecs.hfmax / 1000, fb_info.monspecs.hfmax % 1000,
   1077		fb_info.monspecs.vfmin, fb_info.monspecs.vfmax,
   1078		fb_info.monspecs.dpms ? ", DPMS" : "");
   1079
   1080	if (fb_set_var(&fb_info, &fb_info.var))
   1081		printk(KERN_ERR "Acornfb: unable to set display parameters\n");
   1082
   1083	if (register_framebuffer(&fb_info) < 0)
   1084		return -EINVAL;
   1085	return 0;
   1086}
   1087
   1088static struct platform_driver acornfb_driver = {
   1089	.probe	= acornfb_probe,
   1090	.driver	= {
   1091		.name	= "acornfb",
   1092	},
   1093};
   1094
   1095static int __init acornfb_init(void)
   1096{
   1097	return platform_driver_register(&acornfb_driver);
   1098}
   1099
   1100module_init(acornfb_init);
   1101
   1102MODULE_AUTHOR("Russell King");
   1103MODULE_DESCRIPTION("VIDC 1/1a/20 framebuffer driver");
   1104MODULE_LICENSE("GPL");