1102 lines · 26.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  linux/drivers/video/acornfb.c * *  Copyright (C) 1998-2001 Russell King * * Frame buffer code for Acorn platforms * * NOTE: Most of the modes with X!=640 will disappear shortly. * NOTE: Startup setting of HS & VS polarity not supported. *       (do we need to support it if we're coming up in 640x480?) * * FIXME: (things broken by the "new improved" FBCON API) *  - Blanking 8bpp displays with VIDC */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/fb.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/io.h>#include <linux/gfp.h> #include <mach/hardware.h>#include <asm/irq.h>#include <asm/mach-types.h> #include "acornfb.h" /* * Default resolution. * NOTE that it has to be supported in the table towards * the end of this file. */#define DEFAULT_XRES	640#define DEFAULT_YRES	480#define DEFAULT_BPP	4 /* * define this to debug the video mode selection */#undef DEBUG_MODE_SELECTION /* * Translation from RISC OS monitor types to actual * HSYNC and VSYNC frequency ranges.  These are * probably not right, but they're the best info I * have.  Allow 1% either way on the nominal for TVs. */#define NR_MONTYPES	6static struct fb_monspecs monspecs[NR_MONTYPES] = {	{	/* TV		*/		.hfmin	= 15469,		.hfmax	= 15781,		.vfmin	= 49,		.vfmax	= 51,	}, {	/* Multi Freq	*/		.hfmin	= 0,		.hfmax	= 99999,		.vfmin	= 0,		.vfmax	= 199,	}, {	/* Hi-res mono	*/		.hfmin	= 58608,		.hfmax	= 58608,		.vfmin	= 64,		.vfmax	= 64,	}, {	/* VGA		*/		.hfmin	= 30000,		.hfmax	= 70000,		.vfmin	= 60,		.vfmax	= 60,	}, {	/* SVGA		*/		.hfmin	= 30000,		.hfmax	= 70000,		.vfmin	= 56,		.vfmax	= 75,	}, {		.hfmin	= 30000,		.hfmax	= 70000,		.vfmin	= 60,		.vfmax	= 60,	}}; static struct fb_info fb_info;static struct acornfb_par current_par;static struct vidc_timing current_vidc; extern unsigned int vram_size;	/* set by setup.c */ #ifdef HAS_VIDC20#include <mach/acornfb.h> #define MAX_SIZE	(2*1024*1024) /* VIDC20 has a different set of rules from the VIDC: *  hcr  : must be multiple of 4 *  hswr : must be even *  hdsr : must be even *  hder : must be even *  vcr  : >= 2, (interlace, must be odd) *  vswr : >= 1 *  vdsr : >= 1 *  vder : >= vdsr */static void acornfb_set_timing(struct fb_info *info){	struct fb_var_screeninfo *var = &info->var;	struct vidc_timing vidc;	u_int vcr, fsize;	u_int ext_ctl, dat_ctl;	u_int words_per_line; 	memset(&vidc, 0, sizeof(vidc)); 	vidc.h_sync_width	= var->hsync_len - 8;	vidc.h_border_start	= vidc.h_sync_width + var->left_margin + 8 - 12;	vidc.h_display_start	= vidc.h_border_start + 12 - 18;	vidc.h_display_end	= vidc.h_display_start + var->xres;	vidc.h_border_end	= vidc.h_display_end + 18 - 12;	vidc.h_cycle		= vidc.h_border_end + var->right_margin + 12 - 8;	vidc.h_interlace	= vidc.h_cycle / 2;	vidc.v_sync_width	= var->vsync_len - 1;	vidc.v_border_start	= vidc.v_sync_width + var->upper_margin;	vidc.v_display_start	= vidc.v_border_start;	vidc.v_display_end	= vidc.v_display_start + var->yres;	vidc.v_border_end	= vidc.v_display_end;	vidc.control		= acornfb_default_control(); 	vcr = var->vsync_len + var->upper_margin + var->yres +	      var->lower_margin; 	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {		vidc.v_cycle = (vcr - 3) / 2;		vidc.control |= VIDC20_CTRL_INT;	} else		vidc.v_cycle = vcr - 2; 	switch (var->bits_per_pixel) {	case  1: vidc.control |= VIDC20_CTRL_1BPP;	break;	case  2: vidc.control |= VIDC20_CTRL_2BPP;	break;	case  4: vidc.control |= VIDC20_CTRL_4BPP;	break;	default:	case  8: vidc.control |= VIDC20_CTRL_8BPP;	break;	case 16: vidc.control |= VIDC20_CTRL_16BPP;	break;	case 32: vidc.control |= VIDC20_CTRL_32BPP;	break;	} 	acornfb_vidc20_find_rates(&vidc, var);	fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1; 	if (memcmp(&current_vidc, &vidc, sizeof(vidc))) {		current_vidc = vidc; 		vidc_writel(VIDC20_CTRL | vidc.control);		vidc_writel(0xd0000000 | vidc.pll_ctl);		vidc_writel(0x80000000 | vidc.h_cycle);		vidc_writel(0x81000000 | vidc.h_sync_width);		vidc_writel(0x82000000 | vidc.h_border_start);		vidc_writel(0x83000000 | vidc.h_display_start);		vidc_writel(0x84000000 | vidc.h_display_end);		vidc_writel(0x85000000 | vidc.h_border_end);		vidc_writel(0x86000000);		vidc_writel(0x87000000 | vidc.h_interlace);		vidc_writel(0x90000000 | vidc.v_cycle);		vidc_writel(0x91000000 | vidc.v_sync_width);		vidc_writel(0x92000000 | vidc.v_border_start);		vidc_writel(0x93000000 | vidc.v_display_start);		vidc_writel(0x94000000 | vidc.v_display_end);		vidc_writel(0x95000000 | vidc.v_border_end);		vidc_writel(0x96000000);		vidc_writel(0x97000000);	} 	iomd_writel(fsize, IOMD_FSIZE); 	ext_ctl = acornfb_default_econtrol(); 	if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */		ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC;	else {		if (var->sync & FB_SYNC_HOR_HIGH_ACT)			ext_ctl |= VIDC20_ECTL_HS_HSYNC;		else			ext_ctl |= VIDC20_ECTL_HS_NHSYNC; 		if (var->sync & FB_SYNC_VERT_HIGH_ACT)			ext_ctl |= VIDC20_ECTL_VS_VSYNC;		else			ext_ctl |= VIDC20_ECTL_VS_NVSYNC;	} 	vidc_writel(VIDC20_ECTL | ext_ctl); 	words_per_line = var->xres * var->bits_per_pixel / 32; 	if (current_par.using_vram && info->fix.smem_len == 2048*1024)		words_per_line /= 2; 	/* RiscPC doesn't use the VIDC's VRAM control. */	dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line; 	/* The data bus width is dependent on both the type	 * and amount of video memory.	 *     DRAM	32bit low	 * 1MB VRAM	32bit	 * 2MB VRAM	64bit	 */	if (current_par.using_vram && current_par.vram_half_sam == 2048)		dat_ctl |= VIDC20_DCTL_BUS_D63_0;	else		dat_ctl |= VIDC20_DCTL_BUS_D31_0; 	vidc_writel(VIDC20_DCTL | dat_ctl); #ifdef DEBUG_MODE_SELECTION	printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres,	       var->yres, var->bits_per_pixel);	printk(KERN_DEBUG " H-cycle          : %d\n", vidc.h_cycle);	printk(KERN_DEBUG " H-sync-width     : %d\n", vidc.h_sync_width);	printk(KERN_DEBUG " H-border-start   : %d\n", vidc.h_border_start);	printk(KERN_DEBUG " H-display-start  : %d\n", vidc.h_display_start);	printk(KERN_DEBUG " H-display-end    : %d\n", vidc.h_display_end);	printk(KERN_DEBUG " H-border-end     : %d\n", vidc.h_border_end);	printk(KERN_DEBUG " H-interlace      : %d\n", vidc.h_interlace);	printk(KERN_DEBUG " V-cycle          : %d\n", vidc.v_cycle);	printk(KERN_DEBUG " V-sync-width     : %d\n", vidc.v_sync_width);	printk(KERN_DEBUG " V-border-start   : %d\n", vidc.v_border_start);	printk(KERN_DEBUG " V-display-start  : %d\n", vidc.v_display_start);	printk(KERN_DEBUG " V-display-end    : %d\n", vidc.v_display_end);	printk(KERN_DEBUG " V-border-end     : %d\n", vidc.v_border_end);	printk(KERN_DEBUG " Ext Ctrl  (C)    : 0x%08X\n", ext_ctl);	printk(KERN_DEBUG " PLL Ctrl  (D)    : 0x%08X\n", vidc.pll_ctl);	printk(KERN_DEBUG " Ctrl      (E)    : 0x%08X\n", vidc.control);	printk(KERN_DEBUG " Data Ctrl (F)    : 0x%08X\n", dat_ctl);	printk(KERN_DEBUG " Fsize            : 0x%08X\n", fsize);#endif} /* * We have to take note of the VIDC20's 16-bit palette here. * The VIDC20 looks up a 16 bit pixel as follows: * *   bits   111111 *          5432109876543210 *   red            ++++++++  (8 bits,  7 to 0) *  green       ++++++++      (8 bits, 11 to 4) *   blue   ++++++++          (8 bits, 15 to 8) * * We use a pixel which looks like: * *   bits   111111 *          5432109876543210 *   red               +++++  (5 bits,  4 to  0) *  green         +++++       (5 bits,  9 to  5) *   blue    +++++            (5 bits, 14 to 10) */static intacornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,		  u_int trans, struct fb_info *info){	union palette pal; 	if (regno >= current_par.palette_size)		return 1; 	if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {		u32 pseudo_val; 		pseudo_val  = regno << info->var.red.offset;		pseudo_val |= regno << info->var.green.offset;		pseudo_val |= regno << info->var.blue.offset; 		((u32 *)info->pseudo_palette)[regno] = pseudo_val;	} 	pal.p = 0;	pal.vidc20.red   = red >> 8;	pal.vidc20.green = green >> 8;	pal.vidc20.blue  = blue >> 8; 	current_par.palette[regno] = pal; 	if (info->var.bits_per_pixel == 16) {		int i; 		pal.p = 0;		vidc_writel(0x10000000);		for (i = 0; i < 256; i += 1) {			pal.vidc20.red   = current_par.palette[i       & 31].vidc20.red;			pal.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;			pal.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;			vidc_writel(pal.p);			/* Palette register pointer auto-increments */		}	} else {		vidc_writel(0x10000000 | regno);		vidc_writel(pal.p);	} 	return 0;}#endif /* * Before selecting the timing parameters, adjust * the resolution to fit the rules. */static intacornfb_adjust_timing(struct fb_info *info, struct fb_var_screeninfo *var, u_int fontht){	u_int font_line_len, sam_size, min_size, size, nr_y; 	/* xres must be even */	var->xres = (var->xres + 1) & ~1; 	/*	 * We don't allow xres_virtual to differ from xres	 */	var->xres_virtual = var->xres;	var->xoffset = 0; 	if (current_par.using_vram)		sam_size = current_par.vram_half_sam * 2;	else		sam_size = 16; 	/*	 * Now, find a value for yres_virtual which allows	 * us to do ywrap scrolling.  The value of	 * yres_virtual must be such that the end of the	 * displayable frame buffer must be aligned with	 * the start of a font line.	 */	font_line_len = var->xres * var->bits_per_pixel * fontht / 8;	min_size = var->xres * var->yres * var->bits_per_pixel / 8; 	/*	 * If minimum screen size is greater than that we have	 * available, reject it.	 */	if (min_size > info->fix.smem_len)		return -EINVAL; 	/* Find int 'y', such that y * fll == s * sam < maxsize	 * y = s * sam / fll; s = maxsize / sam	 */	for (size = info->fix.smem_len;	     nr_y = size / font_line_len, min_size <= size;	     size -= sam_size) {		if (nr_y * font_line_len == size)			break;	}	nr_y *= fontht; 	if (var->accel_flags & FB_ACCELF_TEXT) {		if (min_size > size) {			/*			 * failed, use ypan			 */			size = info->fix.smem_len;			var->yres_virtual = size / (font_line_len / fontht);		} else			var->yres_virtual = nr_y;	} else if (var->yres_virtual > nr_y)		var->yres_virtual = nr_y; 	current_par.screen_end = info->fix.smem_start + size; 	/*	 * Fix yres & yoffset if needed.	 */	if (var->yres > var->yres_virtual)		var->yres = var->yres_virtual; 	if (var->vmode & FB_VMODE_YWRAP) {		if (var->yoffset > var->yres_virtual)			var->yoffset = var->yres_virtual;	} else {		if (var->yoffset + var->yres > var->yres_virtual)			var->yoffset = var->yres_virtual - var->yres;	} 	/* hsync_len must be even */	var->hsync_len = (var->hsync_len + 1) & ~1; #if defined(HAS_VIDC20)	/* left_margin must be even */	if (var->left_margin & 1) {		var->left_margin += 1;		var->right_margin -= 1;	} 	/* right_margin must be even */	if (var->right_margin & 1)		var->right_margin += 1;#endif 	if (var->vsync_len < 1)		var->vsync_len = 1; 	return 0;} static intacornfb_validate_timing(struct fb_var_screeninfo *var,			struct fb_monspecs *monspecs){	unsigned long hs, vs; 	/*	 * hs(Hz) = 10^12 / (pixclock * xtotal)	 * vs(Hz) = hs(Hz) / ytotal	 *	 * No need to do long long divisions or anything	 * like that if you factor it correctly	 */	hs = 1953125000 / var->pixclock;	hs = hs * 512 /	     (var->xres + var->left_margin + var->right_margin + var->hsync_len);	vs = hs /	     (var->yres + var->upper_margin + var->lower_margin + var->vsync_len); 	return (vs >= monspecs->vfmin && vs <= monspecs->vfmax &&		hs >= monspecs->hfmin && hs <= monspecs->hfmax) ? 0 : -EINVAL;} static inline voidacornfb_update_dma(struct fb_info *info, struct fb_var_screeninfo *var){	u_int off = var->yoffset * info->fix.line_length; #if defined(HAS_MEMC)	memc_write(VDMA_INIT, off >> 2);#elif defined(HAS_IOMD)	iomd_writel(info->fix.smem_start + off, IOMD_VIDINIT);#endif} static intacornfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){	u_int fontht;	int err; 	/*	 * FIXME: Find the font height	 */	fontht = 8; 	var->red.msb_right = 0;	var->green.msb_right = 0;	var->blue.msb_right = 0;	var->transp.msb_right = 0; 	switch (var->bits_per_pixel) {	case 1:	case 2:	case 4:	case 8:		var->red.offset    = 0;		var->red.length    = var->bits_per_pixel;		var->green         = var->red;		var->blue          = var->red;		var->transp.offset = 0;		var->transp.length = 0;		break; #ifdef HAS_VIDC20	case 16:		var->red.offset    = 0;		var->red.length    = 5;		var->green.offset  = 5;		var->green.length  = 5;		var->blue.offset   = 10;		var->blue.length   = 5;		var->transp.offset = 15;		var->transp.length = 1;		break; 	case 32:		var->red.offset    = 0;		var->red.length    = 8;		var->green.offset  = 8;		var->green.length  = 8;		var->blue.offset   = 16;		var->blue.length   = 8;		var->transp.offset = 24;		var->transp.length = 4;		break;#endif	default:		return -EINVAL;	} 	/*	 * Check to see if the pixel rate is valid.

Lines 1–500 of 1102.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t