brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 6432492 Raw
409 lines · c
1/*2 *	linux/drivers/video/pmagb-b-fb.c3 *4 *	PMAGB-B TURBOchannel Smart Frame Buffer (SFB) card support,5 *	derived from:6 *	"HP300 Topcat framebuffer support (derived from macfb of all things)7 *	Phil Blundell <philb@gnu.org> 1998", the original code can be8 *	found in the file hpfb.c in the same directory.9 *10 *	DECstation related code Copyright (C) 1999, 2000, 2001 by11 *	Michael Engel <engel@unix-ag.org>,12 *	Karsten Merker <merker@linuxtag.org> and13 *	Harald Koerfgen.14 *	Copyright (c) 2005, 2006  Maciej W. Rozycki15 *16 *	This file is subject to the terms and conditions of the GNU General17 *	Public License.  See the file COPYING in the main directory of this18 *	archive for more details.19 */20 21#include <linux/compiler.h>22#include <linux/delay.h>23#include <linux/errno.h>24#include <linux/fb.h>25#include <linux/init.h>26#include <linux/kernel.h>27#include <linux/module.h>28#include <linux/tc.h>29#include <linux/types.h>30 31#include <asm/io.h>32 33#include <video/pmagb-b-fb.h>34 35 36struct pmagbbfb_par {37	volatile void __iomem *mmio;38	volatile void __iomem *smem;39	volatile u32 __iomem *sfb;40	volatile u32 __iomem *dac;41	unsigned int osc0;42	unsigned int osc1;43	int slot;44};45 46 47static const struct fb_var_screeninfo pmagbbfb_defined = {48	.bits_per_pixel	= 8,49	.red.length	= 8,50	.green.length	= 8,51	.blue.length	= 8,52	.activate	= FB_ACTIVATE_NOW,53	.height		= -1,54	.width		= -1,55	.accel_flags	= FB_ACCEL_NONE,56	.sync		= FB_SYNC_ON_GREEN,57	.vmode		= FB_VMODE_NONINTERLACED,58};59 60static const struct fb_fix_screeninfo pmagbbfb_fix = {61	.id		= "PMAGB-BA",62	.smem_len	= (2048 * 1024),63	.type		= FB_TYPE_PACKED_PIXELS,64	.visual		= FB_VISUAL_PSEUDOCOLOR,65	.mmio_len	= PMAGB_B_FBMEM,66};67 68 69static inline void sfb_write(struct pmagbbfb_par *par, unsigned int reg, u32 v)70{71	writel(v, par->sfb + reg / 4);72}73 74static inline u32 sfb_read(struct pmagbbfb_par *par, unsigned int reg)75{76	return readl(par->sfb + reg / 4);77}78 79static inline void dac_write(struct pmagbbfb_par *par, unsigned int reg, u8 v)80{81	writeb(v, par->dac + reg / 4);82}83 84static inline u8 dac_read(struct pmagbbfb_par *par, unsigned int reg)85{86	return readb(par->dac + reg / 4);87}88 89static inline void gp0_write(struct pmagbbfb_par *par, u32 v)90{91	writel(v, par->mmio + PMAGB_B_GP0);92}93 94 95/*96 * Set the palette.97 */98static int pmagbbfb_setcolreg(unsigned int regno, unsigned int red,99			      unsigned int green, unsigned int blue,100			      unsigned int transp, struct fb_info *info)101{102	struct pmagbbfb_par *par = info->par;103 104	if (regno >= info->cmap.len)105		return 1;106 107	red   >>= 8;	/* The cmap fields are 16 bits    */108	green >>= 8;	/* wide, but the hardware colormap */109	blue  >>= 8;	/* registers are only 8 bits wide */110 111	mb();112	dac_write(par, BT459_ADDR_LO, regno);113	dac_write(par, BT459_ADDR_HI, 0x00);114	wmb();115	dac_write(par, BT459_CMAP, red);116	wmb();117	dac_write(par, BT459_CMAP, green);118	wmb();119	dac_write(par, BT459_CMAP, blue);120 121	return 0;122}123 124static const struct fb_ops pmagbbfb_ops = {125	.owner		= THIS_MODULE,126	FB_DEFAULT_IOMEM_OPS,127	.fb_setcolreg	= pmagbbfb_setcolreg,128};129 130 131/*132 * Turn the hardware cursor off.133 */134static void pmagbbfb_erase_cursor(struct fb_info *info)135{136	struct pmagbbfb_par *par = info->par;137 138	mb();139	dac_write(par, BT459_ADDR_LO, 0x00);140	dac_write(par, BT459_ADDR_HI, 0x03);141	wmb();142	dac_write(par, BT459_DATA, 0x00);143}144 145/*146 * Set up screen parameters.147 */148static void pmagbbfb_screen_setup(struct fb_info *info)149{150	struct pmagbbfb_par *par = info->par;151 152	info->var.xres = ((sfb_read(par, SFB_REG_VID_HOR) >>153			   SFB_VID_HOR_PIX_SHIFT) & SFB_VID_HOR_PIX_MASK) * 4;154	info->var.xres_virtual = info->var.xres;155	info->var.yres = (sfb_read(par, SFB_REG_VID_VER) >>156			  SFB_VID_VER_SL_SHIFT) & SFB_VID_VER_SL_MASK;157	info->var.yres_virtual = info->var.yres;158	info->var.left_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>159				  SFB_VID_HOR_BP_SHIFT) &160				 SFB_VID_HOR_BP_MASK) * 4;161	info->var.right_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>162				   SFB_VID_HOR_FP_SHIFT) &163				  SFB_VID_HOR_FP_MASK) * 4;164	info->var.upper_margin = (sfb_read(par, SFB_REG_VID_VER) >>165				  SFB_VID_VER_BP_SHIFT) & SFB_VID_VER_BP_MASK;166	info->var.lower_margin = (sfb_read(par, SFB_REG_VID_VER) >>167				  SFB_VID_VER_FP_SHIFT) & SFB_VID_VER_FP_MASK;168	info->var.hsync_len = ((sfb_read(par, SFB_REG_VID_HOR) >>169				SFB_VID_HOR_SYN_SHIFT) &170			       SFB_VID_HOR_SYN_MASK) * 4;171	info->var.vsync_len = (sfb_read(par, SFB_REG_VID_VER) >>172			       SFB_VID_VER_SYN_SHIFT) & SFB_VID_VER_SYN_MASK;173 174	info->fix.line_length = info->var.xres;175};176 177/*178 * Determine oscillator configuration.179 */180static void pmagbbfb_osc_setup(struct fb_info *info)181{182	static unsigned int pmagbbfb_freqs[] = {183		130808, 119843, 104000, 92980, 74370, 72800,184		69197, 66000, 65000, 50350, 36000, 32000, 25175185	};186	struct pmagbbfb_par *par = info->par;187	struct tc_bus *tbus = to_tc_dev(info->device)->bus;188	u32 count0 = 8, count1 = 8, counttc = 16 * 256 + 8;189	u32 freq0, freq1, freqtc = tc_get_speed(tbus) / 250;190	int i, j;191 192	gp0_write(par, 0);				/* select Osc0 */193	for (j = 0; j < 16; j++) {194		mb();195		sfb_write(par, SFB_REG_TCCLK_COUNT, 0);196		mb();197		for (i = 0; i < 100; i++) {	/* nominally max. 20.5us */198			if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)199				break;200			udelay(1);201		}202		count0 += sfb_read(par, SFB_REG_VIDCLK_COUNT);203	}204 205	gp0_write(par, 1);				/* select Osc1 */206	for (j = 0; j < 16; j++) {207		mb();208		sfb_write(par, SFB_REG_TCCLK_COUNT, 0);209 210		for (i = 0; i < 100; i++) {	/* nominally max. 20.5us */211			if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)212				break;213			udelay(1);214		}215		count1 += sfb_read(par, SFB_REG_VIDCLK_COUNT);216	}217 218	freq0 = (freqtc * count0 + counttc / 2) / counttc;219	par->osc0 = freq0;220	if (freq0 >= pmagbbfb_freqs[0] - (pmagbbfb_freqs[0] + 32) / 64 &&221	    freq0 <= pmagbbfb_freqs[0] + (pmagbbfb_freqs[0] + 32) / 64)222		par->osc0 = pmagbbfb_freqs[0];223 224	freq1 = (par->osc0 * count1 + count0 / 2) / count0;225	par->osc1 = freq1;226	for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++)227		if (freq1 >= pmagbbfb_freqs[i] -228			     (pmagbbfb_freqs[i] + 128) / 256 &&229		    freq1 <= pmagbbfb_freqs[i] +230			     (pmagbbfb_freqs[i] + 128) / 256) {231			par->osc1 = pmagbbfb_freqs[i];232			break;233		}234 235	if (par->osc0 - par->osc1 <= (par->osc0 + par->osc1 + 256) / 512 ||236	    par->osc1 - par->osc0 <= (par->osc0 + par->osc1 + 256) / 512)237		par->osc1 = 0;238 239	gp0_write(par, par->osc1 != 0);			/* reselect OscX */240 241	info->var.pixclock = par->osc1 ?242			     (1000000000 + par->osc1 / 2) / par->osc1 :243			     (1000000000 + par->osc0 / 2) / par->osc0;244};245 246 247static int pmagbbfb_probe(struct device *dev)248{249	struct tc_dev *tdev = to_tc_dev(dev);250	resource_size_t start, len;251	struct fb_info *info;252	struct pmagbbfb_par *par;253	char freq0[12], freq1[12];254	u32 vid_base;255	int err;256 257	info = framebuffer_alloc(sizeof(struct pmagbbfb_par), dev);258	if (!info)259		return -ENOMEM;260 261	par = info->par;262	dev_set_drvdata(dev, info);263 264	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {265		printk(KERN_ERR "%s: Cannot allocate color map\n",266		       dev_name(dev));267		err = -ENOMEM;268		goto err_alloc;269	}270 271	info->fbops = &pmagbbfb_ops;272	info->fix = pmagbbfb_fix;273	info->var = pmagbbfb_defined;274 275	/* Request the I/O MEM resource.  */276	start = tdev->resource.start;277	len = tdev->resource.end - start + 1;278	if (!request_mem_region(start, len, dev_name(dev))) {279		printk(KERN_ERR "%s: Cannot reserve FB region\n",280		       dev_name(dev));281		err = -EBUSY;282		goto err_cmap;283	}284 285	/* MMIO mapping setup.  */286	info->fix.mmio_start = start;287	par->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len);288	if (!par->mmio) {289		printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev));290		err = -ENOMEM;291		goto err_resource;292	}293	par->sfb = par->mmio + PMAGB_B_SFB;294	par->dac = par->mmio + PMAGB_B_BT459;295 296	/* Frame buffer mapping setup.  */297	info->fix.smem_start = start + PMAGB_B_FBMEM;298	par->smem = ioremap(info->fix.smem_start, info->fix.smem_len);299	if (!par->smem) {300		printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev));301		err = -ENOMEM;302		goto err_mmio_map;303	}304	vid_base = sfb_read(par, SFB_REG_VID_BASE);305	info->screen_base = (void __iomem *)par->smem + vid_base * 0x1000;306	info->screen_size = info->fix.smem_len - 2 * vid_base * 0x1000;307 308	pmagbbfb_erase_cursor(info);309	pmagbbfb_screen_setup(info);310	pmagbbfb_osc_setup(info);311 312	err = register_framebuffer(info);313	if (err < 0) {314		printk(KERN_ERR "%s: Cannot register framebuffer\n",315		       dev_name(dev));316		goto err_smem_map;317	}318 319	get_device(dev);320 321	snprintf(freq0, sizeof(freq0), "%u.%03uMHz",322		 par->osc0 / 1000, par->osc0 % 1000);323	snprintf(freq1, sizeof(freq1), "%u.%03uMHz",324		 par->osc1 / 1000, par->osc1 % 1000);325 326	fb_info(info, "%s frame buffer device at %s\n",327		info->fix.id, dev_name(dev));328	fb_info(info, "Osc0: %s, Osc1: %s, Osc%u selected\n",329		freq0, par->osc1 ? freq1 : "disabled", par->osc1 != 0);330 331	return 0;332 333 334err_smem_map:335	iounmap(par->smem);336 337err_mmio_map:338	iounmap(par->mmio);339 340err_resource:341	release_mem_region(start, len);342 343err_cmap:344	fb_dealloc_cmap(&info->cmap);345 346err_alloc:347	framebuffer_release(info);348	return err;349}350 351static int pmagbbfb_remove(struct device *dev)352{353	struct tc_dev *tdev = to_tc_dev(dev);354	struct fb_info *info = dev_get_drvdata(dev);355	struct pmagbbfb_par *par = info->par;356	resource_size_t start, len;357 358	put_device(dev);359	unregister_framebuffer(info);360	iounmap(par->smem);361	iounmap(par->mmio);362	start = tdev->resource.start;363	len = tdev->resource.end - start + 1;364	release_mem_region(start, len);365	fb_dealloc_cmap(&info->cmap);366	framebuffer_release(info);367	return 0;368}369 370 371/*372 * Initialize the framebuffer.373 */374static const struct tc_device_id pmagbbfb_tc_table[] = {375	{ "DEC     ", "PMAGB-BA" },376	{ }377};378MODULE_DEVICE_TABLE(tc, pmagbbfb_tc_table);379 380static struct tc_driver pmagbbfb_driver = {381	.id_table	= pmagbbfb_tc_table,382	.driver		= {383		.name	= "pmagbbfb",384		.bus	= &tc_bus_type,385		.probe	= pmagbbfb_probe,386		.remove	= pmagbbfb_remove,387	},388};389 390static int __init pmagbbfb_init(void)391{392#ifndef MODULE393	if (fb_get_options("pmagbbfb", NULL))394		return -ENXIO;395#endif396	return tc_register_driver(&pmagbbfb_driver);397}398 399static void __exit pmagbbfb_exit(void)400{401	tc_unregister_driver(&pmagbbfb_driver);402}403 404 405module_init(pmagbbfb_init);406module_exit(pmagbbfb_exit);407 408MODULE_LICENSE("GPL");409