pmagb-b-fb.c 10.2 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 *	linux/drivers/video/pmagb-b-fb.c
L
Linus Torvalds 已提交
3
 *
4 5
 *	PMAGB-B TURBOchannel Smart Frame Buffer (SFB) card support,
 *	derived from:
L
Linus Torvalds 已提交
6 7
 *	"HP300 Topcat framebuffer support (derived from macfb of all things)
 *	Phil Blundell <philb@gnu.org> 1998", the original code can be
8
 *	found in the file hpfb.c in the same directory.
L
Linus Torvalds 已提交
9
 *
10 11 12
 *	DECstation related code Copyright (C) 1999, 2000, 2001 by
 *	Michael Engel <engel@unix-ag.org>,
 *	Karsten Merker <merker@linuxtag.org> and
L
Linus Torvalds 已提交
13
 *	Harald Koerfgen.
14
 *	Copyright (c) 2005, 2006  Maciej W. Rozycki
L
Linus Torvalds 已提交
15
 *
16 17 18
 *	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.
L
Linus Torvalds 已提交
19 20
 */

21
#include <linux/compiler.h>
L
Linus Torvalds 已提交
22
#include <linux/delay.h>
23
#include <linux/errno.h>
L
Linus Torvalds 已提交
24
#include <linux/fb.h>
25 26 27
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
28
#include <linux/tc.h>
29 30 31 32 33
#include <linux/types.h>

#include <asm/io.h>
#include <asm/system.h>

L
Linus Torvalds 已提交
34 35
#include <video/pmagb-b-fb.h>

36 37 38 39 40 41 42 43 44

struct pmagbbfb_par {
	volatile void __iomem *mmio;
	volatile void __iomem *smem;
	volatile u32 __iomem *sfb;
	volatile u32 __iomem *dac;
	unsigned int osc0;
	unsigned int osc1;
	int slot;
L
Linus Torvalds 已提交
45 46 47
};


48
static struct fb_var_screeninfo pmagbbfb_defined __initdata = {
L
Linus Torvalds 已提交
49 50 51 52 53
	.bits_per_pixel	= 8,
	.red.length	= 8,
	.green.length	= 8,
	.blue.length	= 8,
	.activate	= FB_ACTIVATE_NOW,
54 55
	.height		= -1,
	.width		= -1,
L
Linus Torvalds 已提交
56
	.accel_flags	= FB_ACCEL_NONE,
57
	.sync		= FB_SYNC_ON_GREEN,
L
Linus Torvalds 已提交
58 59 60
	.vmode		= FB_VMODE_NONINTERLACED,
};

61
static struct fb_fix_screeninfo pmagbbfb_fix __initdata = {
L
Linus Torvalds 已提交
62
	.id		= "PMAGB-BA",
63
	.smem_len	= (2048 * 1024),
L
Linus Torvalds 已提交
64 65
	.type		= FB_TYPE_PACKED_PIXELS,
	.visual		= FB_VISUAL_PSEUDOCOLOR,
66 67 68 69 70 71 72
	.mmio_len	= PMAGB_B_FBMEM,
};


static inline void sfb_write(struct pmagbbfb_par *par, unsigned int reg, u32 v)
{
	writel(v, par->sfb + reg / 4);
L
Linus Torvalds 已提交
73 74
}

75 76 77 78 79 80
static inline u32 sfb_read(struct pmagbbfb_par *par, unsigned int reg)
{
	return readl(par->sfb + reg / 4);
}

static inline void dac_write(struct pmagbbfb_par *par, unsigned int reg, u8 v)
L
Linus Torvalds 已提交
81
{
82
	writeb(v, par->dac + reg / 4);
L
Linus Torvalds 已提交
83 84
}

85 86 87 88 89 90 91 92 93 94 95
static inline u8 dac_read(struct pmagbbfb_par *par, unsigned int reg)
{
	return readb(par->dac + reg / 4);
}

static inline void gp0_write(struct pmagbbfb_par *par, u32 v)
{
	writel(v, par->mmio + PMAGB_B_GP0);
}


L
Linus Torvalds 已提交
96
/*
97
 * Set the palette.
L
Linus Torvalds 已提交
98
 */
99 100 101
static int pmagbbfb_setcolreg(unsigned int regno, unsigned int red,
			      unsigned int green, unsigned int blue,
			      unsigned int transp, struct fb_info *info)
L
Linus Torvalds 已提交
102
{
103 104 105
	struct pmagbbfb_par *par = info->par;

	BUG_ON(regno >= info->cmap.len);
L
Linus Torvalds 已提交
106 107

	red   >>= 8;	/* The cmap fields are 16 bits    */
108
	green >>= 8;	/* wide, but the hardware colormap */
L
Linus Torvalds 已提交
109 110
	blue  >>= 8;	/* registers are only 8 bits wide */

111 112 113 114 115 116 117 118 119 120
	mb();
	dac_write(par, BT459_ADDR_LO, regno);
	dac_write(par, BT459_ADDR_HI, 0x00);
	wmb();
	dac_write(par, BT459_CMAP, red);
	wmb();
	dac_write(par, BT459_CMAP, green);
	wmb();
	dac_write(par, BT459_CMAP, blue);

L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131
	return 0;
}

static struct fb_ops pmagbbfb_ops = {
	.owner		= THIS_MODULE,
	.fb_setcolreg	= pmagbbfb_setcolreg,
	.fb_fillrect	= cfb_fillrect,
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
};

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182

/*
 * Turn the hardware cursor off.
 */
static void __init pmagbbfb_erase_cursor(struct fb_info *info)
{
	struct pmagbbfb_par *par = info->par;

	mb();
	dac_write(par, BT459_ADDR_LO, 0x00);
	dac_write(par, BT459_ADDR_HI, 0x03);
	wmb();
	dac_write(par, BT459_DATA, 0x00);
}

/*
 * Set up screen parameters.
 */
static void __init pmagbbfb_screen_setup(struct fb_info *info)
{
	struct pmagbbfb_par *par = info->par;

	info->var.xres = ((sfb_read(par, SFB_REG_VID_HOR) >>
			   SFB_VID_HOR_PIX_SHIFT) & SFB_VID_HOR_PIX_MASK) * 4;
	info->var.xres_virtual = info->var.xres;
	info->var.yres = (sfb_read(par, SFB_REG_VID_VER) >>
			  SFB_VID_VER_SL_SHIFT) & SFB_VID_VER_SL_MASK;
	info->var.yres_virtual = info->var.yres;
	info->var.left_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
				  SFB_VID_HOR_BP_SHIFT) &
				 SFB_VID_HOR_BP_MASK) * 4;
	info->var.right_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
				   SFB_VID_HOR_FP_SHIFT) &
				  SFB_VID_HOR_FP_MASK) * 4;
	info->var.upper_margin = (sfb_read(par, SFB_REG_VID_VER) >>
				  SFB_VID_VER_BP_SHIFT) & SFB_VID_VER_BP_MASK;
	info->var.lower_margin = (sfb_read(par, SFB_REG_VID_VER) >>
				  SFB_VID_VER_FP_SHIFT) & SFB_VID_VER_FP_MASK;
	info->var.hsync_len = ((sfb_read(par, SFB_REG_VID_HOR) >>
				SFB_VID_HOR_SYN_SHIFT) &
			       SFB_VID_HOR_SYN_MASK) * 4;
	info->var.vsync_len = (sfb_read(par, SFB_REG_VID_VER) >>
			       SFB_VID_VER_SYN_SHIFT) & SFB_VID_VER_SYN_MASK;

	info->fix.line_length = info->var.xres;
};

/*
 * Determine oscillator configuration.
 */
static void __init pmagbbfb_osc_setup(struct fb_info *info)
L
Linus Torvalds 已提交
183
{
184
	static unsigned int pmagbbfb_freqs[] __initdata = {
185
		130808, 119843, 104000, 92980, 74370, 72800,
186 187 188
		69197, 66000, 65000, 50350, 36000, 32000, 25175
	};
	struct pmagbbfb_par *par = info->par;
189
	struct tc_bus *tbus = to_tc_dev(info->device)->bus;
190
	u32 count0 = 8, count1 = 8, counttc = 16 * 256 + 8;
191
	u32 freq0, freq1, freqtc = tc_get_speed(tbus) / 250;
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	int i, j;

	gp0_write(par, 0);				/* select Osc0 */
	for (j = 0; j < 16; j++) {
		mb();
		sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
		mb();
		for (i = 0; i < 100; i++) {	/* nominally max. 20.5us */
			if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
				break;
			udelay(1);
		}
		count0 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
	}

	gp0_write(par, 1);				/* select Osc1 */
	for (j = 0; j < 16; j++) {
		mb();
		sfb_write(par, SFB_REG_TCCLK_COUNT, 0);

		for (i = 0; i < 100; i++) {	/* nominally max. 20.5us */
			if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
				break;
			udelay(1);
		}
		count1 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
	}

	freq0 = (freqtc * count0 + counttc / 2) / counttc;
	par->osc0 = freq0;
	if (freq0 >= pmagbbfb_freqs[0] - (pmagbbfb_freqs[0] + 32) / 64 &&
	    freq0 <= pmagbbfb_freqs[0] + (pmagbbfb_freqs[0] + 32) / 64)
		par->osc0 = pmagbbfb_freqs[0];

	freq1 = (par->osc0 * count1 + count0 / 2) / count0;
	par->osc1 = freq1;
228
	for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++)
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
		if (freq1 >= pmagbbfb_freqs[i] -
			     (pmagbbfb_freqs[i] + 128) / 256 &&
		    freq1 <= pmagbbfb_freqs[i] +
			     (pmagbbfb_freqs[i] + 128) / 256) {
			par->osc1 = pmagbbfb_freqs[i];
			break;
		}

	if (par->osc0 - par->osc1 <= (par->osc0 + par->osc1 + 256) / 512 ||
	    par->osc1 - par->osc0 <= (par->osc0 + par->osc1 + 256) / 512)
		par->osc1 = 0;

	gp0_write(par, par->osc1 != 0);			/* reselect OscX */

	info->var.pixclock = par->osc1 ?
			     (1000000000 + par->osc1 / 2) / par->osc1 :
			     (1000000000 + par->osc0 / 2) / par->osc0;
};


249
static int __init pmagbbfb_probe(struct device *dev)
250
{
251 252
	struct tc_dev *tdev = to_tc_dev(dev);
	resource_size_t start, len;
253 254
	struct fb_info *info;
	struct pmagbbfb_par *par;
255
	char freq0[12], freq1[12];
256
	u32 vid_base;
257
	int err;
258

259
	info = framebuffer_alloc(sizeof(struct pmagbbfb_par), dev);
260 261
	if (!info) {
		printk(KERN_ERR "%s: Cannot allocate memory\n", dev->bus_id);
262
		return -ENOMEM;
263
	}
264 265

	par = info->par;
266
	dev_set_drvdata(dev, info);
267

268 269 270 271
	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
		printk(KERN_ERR "%s: Cannot allocate color map\n",
		       dev->bus_id);
		err = -ENOMEM;
272
		goto err_alloc;
273
	}
274

L
Linus Torvalds 已提交
275 276
	info->fbops = &pmagbbfb_ops;
	info->fix = pmagbbfb_fix;
277
	info->var = pmagbbfb_defined;
L
Linus Torvalds 已提交
278 279
	info->flags = FBINFO_DEFAULT;

280 281 282
	/* Request the I/O MEM resource.  */
	start = tdev->resource.start;
	len = tdev->resource.end - start + 1;
283 284 285
	if (!request_mem_region(start, len, dev->bus_id)) {
		printk(KERN_ERR "%s: Cannot reserve FB region\n", dev->bus_id);
		err = -EBUSY;
286
		goto err_cmap;
287
	}
288

289
	/* MMIO mapping setup.  */
290
	info->fix.mmio_start = start;
291
	par->mmio = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
292 293 294
	if (!par->mmio) {
		printk(KERN_ERR "%s: Cannot map MMIO\n", dev->bus_id);
		err = -ENOMEM;
295
		goto err_resource;
296
	}
297 298 299 300
	par->sfb = par->mmio + PMAGB_B_SFB;
	par->dac = par->mmio + PMAGB_B_BT459;

	/* Frame buffer mapping setup.  */
301
	info->fix.smem_start = start + PMAGB_B_FBMEM;
302
	par->smem = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
303 304 305
	if (!par->smem) {
		printk(KERN_ERR "%s: Cannot map FB\n", dev->bus_id);
		err = -ENOMEM;
306
		goto err_mmio_map;
307
	}
308 309 310 311 312 313 314
	vid_base = sfb_read(par, SFB_REG_VID_BASE);
	info->screen_base = (void __iomem *)par->smem + vid_base * 0x1000;
	info->screen_size = info->fix.smem_len - 2 * vid_base * 0x1000;

	pmagbbfb_erase_cursor(info);
	pmagbbfb_screen_setup(info);
	pmagbbfb_osc_setup(info);
L
Linus Torvalds 已提交
315

316 317 318 319
	err = register_framebuffer(info);
	if (err < 0) {
		printk(KERN_ERR "%s: Cannot register framebuffer\n",
		       dev->bus_id);
320
		goto err_smem_map;
321
	}
322

323 324
	get_device(dev);

325 326 327 328 329
	snprintf(freq0, sizeof(freq0), "%u.%03uMHz",
		 par->osc0 / 1000, par->osc0 % 1000);
	snprintf(freq1, sizeof(freq1), "%u.%03uMHz",
		 par->osc1 / 1000, par->osc1 % 1000);

330 331
	pr_info("fb%d: %s frame buffer device at %s\n",
		info->node, info->fix.id, dev->bus_id);
332 333 334 335
	pr_info("fb%d: Osc0: %s, Osc1: %s, Osc%u selected\n",
		info->node, freq0, par->osc1 ? freq1 : "disabled",
		par->osc1 != 0);

L
Linus Torvalds 已提交
336
	return 0;
337 338 339 340 341 342 343 344


err_smem_map:
	iounmap(par->smem);

err_mmio_map:
	iounmap(par->mmio);

345 346 347
err_resource:
	release_mem_region(start, len);

348 349 350 351 352
err_cmap:
	fb_dealloc_cmap(&info->cmap);

err_alloc:
	framebuffer_release(info);
353
	return err;
L
Linus Torvalds 已提交
354 355
}

356
static int __exit pmagbbfb_remove(struct device *dev)
357
{
358 359
	struct tc_dev *tdev = to_tc_dev(dev);
	struct fb_info *info = dev_get_drvdata(dev);
360
	struct pmagbbfb_par *par = info->par;
361
	resource_size_t start, len;
362

363
	put_device(dev);
364 365 366
	unregister_framebuffer(info);
	iounmap(par->smem);
	iounmap(par->mmio);
367 368 369
	start = tdev->resource.start;
	len = tdev->resource.end - start + 1;
	release_mem_region(start, len);
370 371
	fb_dealloc_cmap(&info->cmap);
	framebuffer_release(info);
372
	return 0;
373
}
L
Linus Torvalds 已提交
374

375 376

/*
377
 * Initialize the framebuffer.
378
 */
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
static const struct tc_device_id pmagbbfb_tc_table[] = {
	{ "DEC     ", "PMAGB-BA" },
	{ }
};
MODULE_DEVICE_TABLE(tc, pmagbbfb_tc_table);

static struct tc_driver pmagbbfb_driver = {
	.id_table	= pmagbbfb_tc_table,
	.driver		= {
		.name	= "pmagbbfb",
		.bus	= &tc_bus_type,
		.probe	= pmagbbfb_probe,
		.remove	= __exit_p(pmagbbfb_remove),
	},
};

395
static int __init pmagbbfb_init(void)
L
Linus Torvalds 已提交
396
{
397
#ifndef MODULE
L
Linus Torvalds 已提交
398
	if (fb_get_options("pmagbbfb", NULL))
399
		return -ENXIO;
400 401
#endif
	return tc_register_driver(&pmagbbfb_driver);
402 403 404 405
}

static void __exit pmagbbfb_exit(void)
{
406
	tc_unregister_driver(&pmagbbfb_driver);
L
Linus Torvalds 已提交
407 408
}

409

L
Linus Torvalds 已提交
410
module_init(pmagbbfb_init);
411 412
module_exit(pmagbbfb_exit);

L
Linus Torvalds 已提交
413
MODULE_LICENSE("GPL");