574 lines · 14.7 KB · C
RawDownload
/* *  linux/drivers/video/offb.c -- Open Firmware based frame buffer device * *	Copyright (C) 1997 Geert Uytterhoeven * *  This driver is partly based on the PowerMac console driver: * *	Copyright (C) 1996 Paul Mackerras * *  This file is subject to the terms and conditions of the GNU General Public *  License. See the file COPYING in the main directory of this archive for *  more details. */ #include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/pci.h>#include <asm/io.h>#include <asm/prom.h> #ifdef CONFIG_PPC64#include <asm/pci-bridge.h>#endif #ifdef CONFIG_PPC32#include <asm/bootx.h>#endif #include "macmodes.h" /* Supported palette hacks */enum {	cmap_unknown,	cmap_m64,		/* ATI Mach64 */	cmap_r128,		/* ATI Rage128 */	cmap_M3A,		/* ATI Rage Mobility M3 Head A */	cmap_M3B,		/* ATI Rage Mobility M3 Head B */	cmap_radeon,		/* ATI Radeon */	cmap_gxt2000,		/* IBM GXT2000 */}; struct offb_par {	volatile void __iomem *cmap_adr;	volatile void __iomem *cmap_data;	int cmap_type;	int blanked;}; struct offb_par default_par;     /*     *  Interface used by the world     */ int offb_init(void); static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,			  u_int transp, struct fb_info *info);static int offb_blank(int blank, struct fb_info *info); #ifdef CONFIG_PPC32extern boot_infos_t *boot_infos;#endif static void offb_init_nodriver(struct device_node *);static void offb_init_fb(const char *name, const char *full_name,			 int width, int height, int depth, int pitch,			 unsigned long address, struct device_node *dp); static struct fb_ops offb_ops = {	.owner		= THIS_MODULE,	.fb_setcolreg	= offb_setcolreg,	.fb_blank	= offb_blank,	.fb_fillrect	= cfb_fillrect,	.fb_copyarea	= cfb_copyarea,	.fb_imageblit	= cfb_imageblit,};     /*     *  Set a single color register. The values supplied are already     *  rounded down to the hardware's capabilities (according to the     *  entries in the var structure). Return != 0 for invalid regno.     */ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,			  u_int transp, struct fb_info *info){	struct offb_par *par = (struct offb_par *) info->par; 	if (!par->cmap_adr || regno > 255)		return 1; 	red >>= 8;	green >>= 8;	blue >>= 8; 	switch (par->cmap_type) {	case cmap_m64:		writeb(regno, par->cmap_adr);		writeb(red, par->cmap_data);		writeb(green, par->cmap_data);		writeb(blue, par->cmap_data);		break;	case cmap_M3A:		/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */		out_le32(par->cmap_adr + 0x58,			 in_le32(par->cmap_adr + 0x58) & ~0x20);	case cmap_r128:		/* Set palette index & data */		out_8(par->cmap_adr + 0xb0, regno);		out_le32(par->cmap_adr + 0xb4,			 (red << 16 | green << 8 | blue));		break;	case cmap_M3B:		/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */		out_le32(par->cmap_adr + 0x58,			 in_le32(par->cmap_adr + 0x58) | 0x20);		/* Set palette index & data */		out_8(par->cmap_adr + 0xb0, regno);		out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));		break;	case cmap_radeon:		/* Set palette index & data (could be smarter) */		out_8(par->cmap_adr + 0xb0, regno);		out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));		break;	case cmap_gxt2000:		out_le32((unsigned __iomem *) par->cmap_adr + regno,			 (red << 16 | green << 8 | blue));		break;	} 	if (regno < 16)		switch (info->var.bits_per_pixel) {		case 16:			((u16 *) (info->pseudo_palette))[regno] =			    (regno << 10) | (regno << 5) | regno;			break;		case 32:			{				int i = (regno << 8) | regno;				((u32 *) (info->pseudo_palette))[regno] =				    (i << 16) | i;				break;			}		}	return 0;}     /*     *  Blank the display.     */ static int offb_blank(int blank, struct fb_info *info){	struct offb_par *par = (struct offb_par *) info->par;	int i, j; 	if (!par->cmap_adr)		return 0; 	if (!par->blanked)		if (!blank)			return 0; 	par->blanked = blank; 	if (blank)		for (i = 0; i < 256; i++) {			switch (par->cmap_type) {			case cmap_m64:				writeb(i, par->cmap_adr);				for (j = 0; j < 3; j++)					writeb(0, par->cmap_data);				break;			case cmap_M3A:				/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */				out_le32(par->cmap_adr + 0x58,					 in_le32(par->cmap_adr + 0x58) & ~0x20);			case cmap_r128:				/* Set palette index & data */				out_8(par->cmap_adr + 0xb0, i);				out_le32(par->cmap_adr + 0xb4, 0);				break;			case cmap_M3B:				/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */				out_le32(par->cmap_adr + 0x58,					 in_le32(par->cmap_adr + 0x58) | 0x20);				/* Set palette index & data */				out_8(par->cmap_adr + 0xb0, i);				out_le32(par->cmap_adr + 0xb4, 0);				break;			case cmap_radeon:				out_8(par->cmap_adr + 0xb0, i);				out_le32(par->cmap_adr + 0xb4, 0);				break;			case cmap_gxt2000:				out_le32((unsigned __iomem *) par->cmap_adr + i,					 0);				break;			}	} else		fb_set_cmap(&info->cmap, info);	return 0;}     /*     *  Initialisation     */ int __init offb_init(void){	struct device_node *dp = NULL, *boot_disp = NULL; #if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32)	struct device_node *macos_display = NULL;#endif	if (fb_get_options("offb", NULL))		return -ENODEV; #if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32)	/* If we're booted from BootX... */	if (boot_infos != 0) {		unsigned long addr =		    (unsigned long) boot_infos->dispDeviceBase;		u32 *addrp;		u64 daddr, dsize;		unsigned int flags; 		/* find the device node corresponding to the macos display */		while ((dp = of_find_node_by_type(dp, "display"))) {			int i; 			/*			 * Look for an AAPL,address property first.			 */			unsigned int na;			unsigned int *ap =				(unsigned int *)get_property(dp, "AAPL,address",							     &na);			if (ap != 0) {				for (na /= sizeof(unsigned int); na > 0;				     --na, ++ap)					if (*ap <= addr &&					    addr < *ap + 0x1000000) {						macos_display = dp;						goto foundit;					}			} 			/*			 * See if the display address is in one of the address			 * ranges for this display.			 */			i = 0;			for (;;) {				addrp = of_get_address(dp, i++, &dsize, &flags);				if (addrp == NULL)					break;				if (!(flags & IORESOURCE_MEM))					continue;				daddr = of_translate_address(dp, addrp);				if (daddr == OF_BAD_ADDR)					continue;				if (daddr <= addr && addr < (daddr + dsize)) {					macos_display = dp;					goto foundit;				}			}		foundit:			if (macos_display) {				printk(KERN_INFO "MacOS display is %s\n",				       dp->full_name);				break;			}		} 		/* initialize it */		offb_init_fb(macos_display ? macos_display->			     name : "MacOS display",			     macos_display ? macos_display->			     full_name : "MacOS display",			     boot_infos->dispDeviceRect[2],			     boot_infos->dispDeviceRect[3],			     boot_infos->dispDeviceDepth,			     boot_infos->dispDeviceRowBytes, addr, NULL);	}#endif /* defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32) */ 	for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) {		if (get_property(dp, "linux,opened", NULL) &&		    get_property(dp, "linux,boot-display", NULL)) {			boot_disp = dp;			offb_init_nodriver(dp);		}	}	for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) {		if (get_property(dp, "linux,opened", NULL) &&		    dp != boot_disp)			offb_init_nodriver(dp);	} 	return 0;}  static void __init offb_init_nodriver(struct device_node *dp){	int *pp, i;	unsigned int len;	int width = 640, height = 480, depth = 8, pitch;	unsigned int flags, rsize, *up;	u64 address = OF_BAD_ADDR;	u32 *addrp;	u64 asize; 	if ((pp = (int *) get_property(dp, "depth", &len)) != NULL	    && len == sizeof(int))		depth = *pp;	if ((pp = (int *) get_property(dp, "width", &len)) != NULL	    && len == sizeof(int))		width = *pp;	if ((pp = (int *) get_property(dp, "height", &len)) != NULL	    && len == sizeof(int))		height = *pp;	if ((pp = (int *) get_property(dp, "linebytes", &len)) != NULL	    && len == sizeof(int)) {		pitch = *pp;		if (pitch == 1)			pitch = 0x1000;	} else		pitch = width;        rsize = (unsigned long)pitch * (unsigned long)height *               (unsigned long)(depth / 8);        /* Try to match device to a PCI device in order to get a properly	* translated address rather then trying to decode the open firmware	* stuff in various incorrect ways	*/#ifdef CONFIG_PCI       /* First try to locate the PCI device if any */       {               struct pci_dev *pdev = NULL; 	       for_each_pci_dev(pdev) {                       if (dp == pci_device_to_OF_node(pdev))                               break;	       }               if (pdev) {                       for (i = 0; i < 6 && address == OF_BAD_ADDR; i++) {                               if ((pci_resource_flags(pdev, i) &				    IORESOURCE_MEM) &&				   (pci_resource_len(pdev, i) >= rsize))                                       address = pci_resource_start(pdev, i);                       }		       pci_dev_put(pdev);               }        }#endif /* CONFIG_PCI */        /* This one is dodgy, we may drop it ... */       if (address == OF_BAD_ADDR &&	   (up = (unsigned *) get_property(dp, "address", &len)) != NULL &&	   len == sizeof(unsigned int))	       address = (u64) * up;        if (address == OF_BAD_ADDR) {	       for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))			    != NULL; i++) {		       if (!(flags & IORESOURCE_MEM))			       continue;		       if (asize >= pitch * height * depth / 8)			       break;	       }		if (addrp == NULL) {			printk(KERN_ERR			       "no framebuffer address found for %s\n",			       dp->full_name);			return;		}		address = of_translate_address(dp, addrp);		if (address == OF_BAD_ADDR) {			printk(KERN_ERR			       "can't translate framebuffer address for %s\n",			       dp->full_name);			return;		} 		/* kludge for valkyrie */		if (strcmp(dp->name, "valkyrie") == 0)			address += 0x1000;	}	offb_init_fb(dp->name, dp->full_name, width, height, depth,		     pitch, address, dp); } static void __init offb_init_fb(const char *name, const char *full_name,				int width, int height, int depth,				int pitch, unsigned long address,				struct device_node *dp){	unsigned long res_size = pitch * height * depth / 8;	struct offb_par *par = &default_par;	unsigned long res_start = address;	struct fb_fix_screeninfo *fix;	struct fb_var_screeninfo *var;	struct fb_info *info;	int size; 	if (!request_mem_region(res_start, res_size, "offb"))		return; 	printk(KERN_INFO	       "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",	       width, height, name, address, depth, pitch);	if (depth != 8 && depth != 16 && depth != 32) {		printk(KERN_ERR "%s: can't use depth = %d\n", full_name,		       depth);		release_mem_region(res_start, res_size);		return;	} 	size = sizeof(struct fb_info) + sizeof(u32) * 17; 	info = kmalloc(size, GFP_ATOMIC);		if (info == 0) {		release_mem_region(res_start, res_size);		return;	}	memset(info, 0, size); 	fix = &info->fix;	var = &info->var; 	strcpy(fix->id, "OFfb ");	strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));	fix->id[sizeof(fix->id) - 1] = '\0'; 	var->xres = var->xres_virtual = width;	var->yres = var->yres_virtual = height;	fix->line_length = pitch; 	fix->smem_start = address;	fix->smem_len = pitch * height;	fix->type = FB_TYPE_PACKED_PIXELS;	fix->type_aux = 0; 	par->cmap_type = cmap_unknown;	if (depth == 8) { 		/* Palette hacks disabled for now */#if 0		if (dp && !strncmp(name, "ATY,Rage128", 11)) {			unsigned long regbase = dp->addrs[2].address;			par->cmap_adr = ioremap(regbase, 0x1FFF);			par->cmap_type = cmap_r128;		} else if (dp && (!strncmp(name, "ATY,RageM3pA", 12)				  || !strncmp(name, "ATY,RageM3p12A", 14))) {			unsigned long regbase =			    dp->parent->addrs[2].address;			par->cmap_adr = ioremap(regbase, 0x1FFF);			par->cmap_type = cmap_M3A;		} else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) {			unsigned long regbase =			    dp->parent->addrs[2].address;			par->cmap_adr = ioremap(regbase, 0x1FFF);			par->cmap_type = cmap_M3B;		} else if (dp && !strncmp(name, "ATY,Rage6", 9)) {			unsigned long regbase = dp->addrs[1].address;			par->cmap_adr = ioremap(regbase, 0x1FFF);			par->cmap_type = cmap_radeon;		} else if (!strncmp(name, "ATY,", 4)) {			unsigned long base = address & 0xff000000UL;			par->cmap_adr =			    ioremap(base + 0x7ff000, 0x1000) + 0xcc0;			par->cmap_data = par->cmap_adr + 1;			par->cmap_type = cmap_m64;		} else if (device_is_compatible(dp, "pci1014,b7")) {			unsigned long regbase = dp->addrs[0].address;			par->cmap_adr = ioremap(regbase + 0x6000, 0x1000);			par->cmap_type = cmap_gxt2000;		}#endif		fix->visual = par->cmap_adr ? FB_VISUAL_PSEUDOCOLOR		    : FB_VISUAL_STATIC_PSEUDOCOLOR;

Lines 1–500 of 574.

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