⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mb862xxfb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * drivers/mb862xx/mb862xxfb.c * * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver * * (C) 2008 Anatolij Gustschin <agust@denx.de> * DENX Software Engineering * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#undef DEBUG#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#if defined(CONFIG_PPC_OF)#include <linux/of_platform.h>#endif#include "mb862xxfb.h"#include "mb862xx_reg.h"#define NR_PALETTE		256#define MB862XX_MEM_SIZE	0x1000000#define CORALP_MEM_SIZE		0x4000000#define CARMINE_MEM_SIZE	0x8000000#define DRV_NAME		"mb862xxfb"#if defined(CONFIG_LWMON5)static struct mb862xx_gc_mode lwmon5_gc_mode = {	/* Mode for Sharp LQ104V1DG61 TFT LCD Panel */	{ "640x480", 60, 640, 480, 40000, 48, 16, 32, 11, 96, 2, 0, 0, 0 },	/* 16 bits/pixel, 32MB, 100MHz, SDRAM memory mode value */	16, 0x2000000, GC_CCF_COT_100, 0x414fb7f2};#endif#if defined(CONFIG_SOCRATES)static struct mb862xx_gc_mode socrates_gc_mode = {	/* Mode for Prime View PM070WL4 TFT LCD Panel */	{ "800x480", 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 },	/* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */	16, 0x1000000, GC_CCF_COT_133, 0x4157ba63};#endif/* Helpers */static inline int h_total(struct fb_var_screeninfo *var){	return var->xres + var->left_margin +		var->right_margin + var->hsync_len;}static inline int v_total(struct fb_var_screeninfo *var){	return var->yres + var->upper_margin +		var->lower_margin + var->vsync_len;}static inline int hsp(struct fb_var_screeninfo *var){	return var->xres + var->right_margin - 1;}static inline int vsp(struct fb_var_screeninfo *var){	return var->yres + var->lower_margin - 1;}static inline int d_pitch(struct fb_var_screeninfo *var){	return var->xres * var->bits_per_pixel / 8;}static inline unsigned int chan_to_field(unsigned int chan,					 struct fb_bitfield *bf){	chan &= 0xffff;	chan >>= 16 - bf->length;	return chan << bf->offset;}static int mb862xxfb_setcolreg(unsigned regno,			       unsigned red, unsigned green, unsigned blue,			       unsigned transp, struct fb_info *info){	struct mb862xxfb_par *par = info->par;	unsigned int val;	switch (info->fix.visual) {	case FB_VISUAL_TRUECOLOR:		if (regno < 16) {			val  = chan_to_field(red,   &info->var.red);			val |= chan_to_field(green, &info->var.green);			val |= chan_to_field(blue,  &info->var.blue);			par->pseudo_palette[regno] = val;		}		break;	case FB_VISUAL_PSEUDOCOLOR:		if (regno < 256) {			val = (red >> 8) << 16;			val |= (green >> 8) << 8;			val |= blue >> 8;			outreg(disp, GC_L0PAL0 + (regno * 4), val);		}		break;	default:		return 1;   /* unsupported type */	}	return 0;}static int mb862xxfb_check_var(struct fb_var_screeninfo *var,			       struct fb_info *fbi){	unsigned long tmp;	if (fbi->dev)		dev_dbg(fbi->dev, "%s\n", __func__);	/* check if these values fit into the registers */	if (var->hsync_len > 255 || var->vsync_len > 255)		return -EINVAL;	if ((var->xres + var->right_margin) >= 4096)		return -EINVAL;	if ((var->yres + var->lower_margin) > 4096)		return -EINVAL;	if (h_total(var) > 4096 || v_total(var) > 4096)		return -EINVAL;	if (var->xres_virtual > 4096 || var->yres_virtual > 4096)		return -EINVAL;	if (var->bits_per_pixel <= 8)		var->bits_per_pixel = 8;	else if (var->bits_per_pixel <= 16)		var->bits_per_pixel = 16;	else if (var->bits_per_pixel <= 32)		var->bits_per_pixel = 32;	/*	 * can cope with 8,16 or 24/32bpp if resulting	 * pitch is divisible by 64 without remainder	 */	if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) {		int r;		var->bits_per_pixel = 0;		do {			var->bits_per_pixel += 8;			r = d_pitch(&fbi->var) % GC_L0M_L0W_UNIT;		} while (r && var->bits_per_pixel <= 32);		if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT)			return -EINVAL;	}	/* line length is going to be 128 bit aligned */	tmp = (var->xres * var->bits_per_pixel) / 8;	if ((tmp & 15) != 0)		return -EINVAL;	/* set r/g/b positions and validate bpp */	switch (var->bits_per_pixel) {	case 8:		var->red.length		= var->bits_per_pixel;		var->green.length	= var->bits_per_pixel;		var->blue.length	= var->bits_per_pixel;		var->red.offset		= 0;		var->green.offset	= 0;		var->blue.offset	= 0;		var->transp.length	= 0;		break;	case 16:		var->red.length		= 5;		var->green.length	= 5;		var->blue.length	= 5;		var->red.offset		= 10;		var->green.offset	= 5;		var->blue.offset	= 0;		var->transp.length	= 0;		break;	case 24:	case 32:		var->transp.length	= 8;		var->red.length		= 8;		var->green.length	= 8;		var->blue.length	= 8;		var->transp.offset	= 24;		var->red.offset		= 16;		var->green.offset	= 8;		var->blue.offset	= 0;		break;	default:		return -EINVAL;	}	return 0;}/* * set display parameters */static int mb862xxfb_set_par(struct fb_info *fbi){	struct mb862xxfb_par *par = fbi->par;	unsigned long reg, sc;	dev_dbg(par->dev, "%s\n", __func__);	if (par->pre_init)		return 0;	/* disp off */	reg = inreg(disp, GC_DCM1);	reg &= ~GC_DCM01_DEN;	outreg(disp, GC_DCM1, reg);	/* set display reference clock div. */	sc = par->refclk / (1000000 / fbi->var.pixclock) - 1;	reg = inreg(disp, GC_DCM1);	reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC);	reg |= sc << 8;	outreg(disp, GC_DCM1, reg);	dev_dbg(par->dev, "SC 0x%lx\n", sc);	/* disp dimension, format */	reg =  pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT,		    (fbi->var.yres - 1));	if (fbi->var.bits_per_pixel == 16)		reg |= GC_L0M_L0C_16;	outreg(disp, GC_L0M, reg);	if (fbi->var.bits_per_pixel == 32) {		reg = inreg(disp, GC_L0EM);		outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24);	}	outreg(disp, GC_WY_WX, 0);	reg = pack(fbi->var.yres - 1, fbi->var.xres);	outreg(disp, GC_WH_WW, reg);	outreg(disp, GC_L0OA0, 0);	outreg(disp, GC_L0DA0, 0);	outreg(disp, GC_L0DY_L0DX, 0);	outreg(disp, GC_L0WY_L0WX, 0);	outreg(disp, GC_L0WH_L0WW, reg);	/* both HW-cursors off */	reg = inreg(disp, GC_CPM_CUTC);	reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1);	outreg(disp, GC_CPM_CUTC, reg);	/* timings */	reg = pack(fbi->var.xres - 1, fbi->var.xres - 1);	outreg(disp, GC_HDB_HDP, reg);	reg = pack((fbi->var.yres - 1), vsp(&fbi->var));	outreg(disp, GC_VDP_VSP, reg);	reg = ((fbi->var.vsync_len - 1) << 24) |	      pack((fbi->var.hsync_len - 1), hsp(&fbi->var));	outreg(disp, GC_VSW_HSW_HSP, reg);	outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0));	outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0));	/* display on */	reg = inreg(disp, GC_DCM1);	reg |= GC_DCM01_DEN | GC_DCM01_L0E;	reg &= ~GC_DCM01_ESY;	outreg(disp, GC_DCM1, reg);	return 0;}static int mb862xxfb_pan(struct fb_var_screeninfo *var,			 struct fb_info *info){	struct mb862xxfb_par *par = info->par;	unsigned long reg;	reg = pack(var->yoffset, var->xoffset);	outreg(disp, GC_L0WY_L0WX, reg);	reg = pack(var->yres_virtual, var->xres_virtual);	outreg(disp, GC_L0WH_L0WW, reg);	return 0;}static int mb862xxfb_blank(int mode, struct fb_info *fbi){	struct mb862xxfb_par  *par = fbi->par;	unsigned long reg;	dev_dbg(fbi->dev, "blank mode=%d\n", mode);	switch (mode) {	case FB_BLANK_POWERDOWN:		reg = inreg(disp, GC_DCM1);		reg &= ~GC_DCM01_DEN;		outreg(disp, GC_DCM1, reg);		break;	case FB_BLANK_UNBLANK:		reg = inreg(disp, GC_DCM1);		reg |= GC_DCM01_DEN;		outreg(disp, GC_DCM1, reg);		break;	case FB_BLANK_NORMAL:	case FB_BLANK_VSYNC_SUSPEND:	case FB_BLANK_HSYNC_SUSPEND:	default:		return 1;	}	return 0;}/* framebuffer ops */static struct fb_ops mb862xxfb_ops = {	.owner		= THIS_MODULE,	.fb_check_var	= mb862xxfb_check_var,	.fb_set_par	= mb862xxfb_set_par,	.fb_setcolreg	= mb862xxfb_setcolreg,	.fb_blank	= mb862xxfb_blank,	.fb_pan_display	= mb862xxfb_pan,	.fb_fillrect	= cfb_fillrect,	.fb_copyarea	= cfb_copyarea,	.fb_imageblit	= cfb_imageblit,};/* initialize fb_info data */static int mb862xxfb_init_fbinfo(struct fb_info *fbi){	struct mb862xxfb_par *par = fbi->par;	struct mb862xx_gc_mode *mode = par->gc_mode;	unsigned long reg;	fbi->fbops = &mb862xxfb_ops;	fbi->pseudo_palette = par->pseudo_palette;	fbi->screen_base = par->fb_base;	fbi->screen_size = par->mapped_vram;	strcpy(fbi->fix.id, DRV_NAME);	fbi->fix.smem_start = (unsigned long)par->fb_base_phys;	fbi->fix.smem_len = par->mapped_vram;	fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys;	fbi->fix.mmio_len = par->mmio_len;	fbi->fix.accel = FB_ACCEL_NONE;	fbi->fix.type = FB_TYPE_PACKED_PIXELS;	fbi->fix.type_aux = 0;	fbi->fix.xpanstep = 1;	fbi->fix.ypanstep = 1;	fbi->fix.ywrapstep = 0;	reg = inreg(disp, GC_DCM1);	if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) {		/* get the disp mode from active display cfg */		unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1;		unsigned long hsp, vsp, ht, vt;		dev_dbg(par->dev, "using bootloader's disp. mode\n");		fbi->var.pixclock = (sc * 1000000) / par->refclk;		fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1;		reg = inreg(disp, GC_VDP_VSP);		fbi->var.yres = ((reg >> 16) & 0x0fff) + 1;		vsp = (reg & 0x0fff) + 1;		fbi->var.xres_virtual = fbi->var.xres;		fbi->var.yres_virtual = fbi->var.yres;		reg = inreg(disp, GC_L0EM);		if (reg & GC_L0EM_L0EC_24) {			fbi->var.bits_per_pixel = 32;		} else {			reg = inreg(disp, GC_L0M);			if (reg & GC_L0M_L0C_16)				fbi->var.bits_per_pixel = 16;			else				fbi->var.bits_per_pixel = 8;		}		reg = inreg(disp, GC_VSW_HSW_HSP);		fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1;		fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1;		hsp = (reg & 0xffff) + 1;		ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1;		fbi->var.right_margin = hsp - fbi->var.xres;		fbi->var.left_margin = ht - hsp - fbi->var.hsync_len;		vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1;		fbi->var.lower_margin = vsp - fbi->var.yres;		fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len;	} else if (mode) {		dev_dbg(par->dev, "using supplied mode\n");		fb_videomode_to_var(&fbi->var, (struct fb_videomode *)mode);		fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8;	} else {		int ret;		ret = fb_find_mode(&fbi->var, fbi, "640x480-16@60",				   NULL, 0, NULL, 16);		if (ret == 0 || ret == 4) {			dev_err(par->dev,				"failed to get initial mode\n");			return -EINVAL;		}	}	fbi->var.xoffset = 0;	fbi->var.yoffset = 0;	fbi->var.grayscale = 0;	fbi->var.nonstd = 0;	fbi->var.height = -1;	fbi->var.width = -1;	fbi->var.accel_flags = 0;	fbi->var.vmode = FB_VMODE_NONINTERLACED;	fbi->var.activate = FB_ACTIVATE_NOW;	fbi->flags = FBINFO_DEFAULT |#ifdef __BIG_ENDIAN		     FBINFO_FOREIGN_ENDIAN |#endif		     FBINFO_HWACCEL_XPAN |		     FBINFO_HWACCEL_YPAN;	/* check and possibly fix bpp */	if ((fbi->fbops->fb_check_var)(&fbi->var, fbi))		dev_err(par->dev, "check_var() failed on initial setup?\n");	fbi->fix.visual = fbi->var.bits_per_pixel == 8 ?			 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;	fbi->fix.line_length = (fbi->var.xres_virtual *				fbi->var.bits_per_pixel) / 8;	return 0;}/* * show some display controller and cursor registers */static ssize_t mb862xxfb_show_dispregs(struct device *dev,				       struct device_attribute *attr, char *buf){	struct fb_info *fbi = dev_get_drvdata(dev);	struct mb862xxfb_par *par = fbi->par;	char *ptr = buf;	unsigned int reg;	for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4)		ptr += sprintf(ptr, "%08x = %08x\n",			       reg, inreg(disp, reg));	for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4)		ptr += sprintf(ptr, "%08x = %08x\n",			       reg, inreg(disp, reg));	for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4)		ptr += sprintf(ptr, "%08x = %08x\n",			       reg, inreg(disp, reg));	return ptr - buf;}static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL);irqreturn_t mb862xx_intr(int irq, void *dev_id){	struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id;	unsigned long reg_ist, mask;	if (!par)		return IRQ_NONE;	if (par->type == BT_CARMINE) {		/* Get Interrupt Status */		reg_ist = inreg(ctrl, GC_CTRL_STATUS);		mask = inreg(ctrl, GC_CTRL_INT_MASK);		if (reg_ist == 0)			return IRQ_HANDLED;		reg_ist &= mask;		if (reg_ist == 0)			return IRQ_HANDLED;		/* Clear interrupt status */		outreg(ctrl, 0x0, reg_ist);	} else {		/* Get status */		reg_ist = inreg(host, GC_IST);		mask = inreg(host, GC_IMASK);		reg_ist &= mask;		if (reg_ist == 0)			return IRQ_HANDLED;		/* Clear status */		outreg(host, GC_IST, ~reg_ist);	}	return IRQ_HANDLED;}#if defined(CONFIG_FB_MB862XX_LIME)/* * GDC (Lime, Coral(B/Q), Mint, ...) on host bus */static int mb862xx_gdc_init(struct mb862xxfb_par *par){	unsigned long ccf, mmr;	unsigned long ver, rev;	if (!par)		return -ENODEV;#if defined(CONFIG_FB_PRE_INIT_FB)	par->pre_init = 1;#endif	par->host = par->mmio_base;	par->i2c = par->mmio_base + MB862XX_I2C_BASE;	par->disp = par->mmio_base + MB862XX_DISP_BASE;	par->cap = par->mmio_base + MB862XX_CAP_BASE;	par->draw = par->mmio_base + MB862XX_DRAW_BASE;	par->geo = par->mmio_base + MB862XX_GEO_BASE;	par->pio = par->mmio_base + MB862XX_PIO_BASE;	par->refclk = GC_DISP_REFCLK_400;	ver = inreg(host, GC_CID);	rev = inreg(pio, GC_REVISION);	if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) {		dev_info(par->dev, "Fujitsu Lime v1.%d found\n",			 (int)rev & 0xff);		par->type = BT_LIME;		ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100;		mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2;	} else {		dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n", ver, rev);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -