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
	if (!info) {
261
		printk(KERN_ERR "%s: Cannot allocate memory\n", dev_name(dev));
262
		return -ENOMEM;
263
	}
264 265

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

268 269
	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
		printk(KERN_ERR "%s: Cannot allocate color map\n",
270
		       dev_name(dev));
271
		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_name(dev))) {
		printk(KERN_ERR "%s: Cannot reserve FB region\n",
		       dev_name(dev));
286
		err = -EBUSY;
287
		goto err_cmap;
288
	}
289

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

	/* Frame buffer mapping setup.  */
302
	info->fix.smem_start = start + PMAGB_B_FBMEM;
303
	par->smem = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
304
	if (!par->smem) {
305
		printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev));
306
		err = -ENOMEM;
307
		goto err_mmio_map;
308
	}
309 310 311 312 313 314 315
	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 已提交
316

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

324 325
	get_device(dev);

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

331
	pr_info("fb%d: %s frame buffer device at %s\n",
332
		info->node, info->fix.id, dev_name(dev));
333 334 335 336
	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 已提交
337
	return 0;
338 339 340 341 342 343 344 345


err_smem_map:
	iounmap(par->smem);

err_mmio_map:
	iounmap(par->mmio);

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

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

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

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

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

376 377

/*
378
 * Initialize the framebuffer.
379
 */
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
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),
	},
};

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

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

410

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

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