0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 #include <linux/compiler.h>
0022 #include <linux/delay.h>
0023 #include <linux/errno.h>
0024 #include <linux/fb.h>
0025 #include <linux/init.h>
0026 #include <linux/kernel.h>
0027 #include <linux/module.h>
0028 #include <linux/tc.h>
0029 #include <linux/types.h>
0030
0031 #include <asm/io.h>
0032
0033 #include <video/pmagb-b-fb.h>
0034
0035
0036 struct pmagbbfb_par {
0037 volatile void __iomem *mmio;
0038 volatile void __iomem *smem;
0039 volatile u32 __iomem *sfb;
0040 volatile u32 __iomem *dac;
0041 unsigned int osc0;
0042 unsigned int osc1;
0043 int slot;
0044 };
0045
0046
0047 static const struct fb_var_screeninfo pmagbbfb_defined = {
0048 .bits_per_pixel = 8,
0049 .red.length = 8,
0050 .green.length = 8,
0051 .blue.length = 8,
0052 .activate = FB_ACTIVATE_NOW,
0053 .height = -1,
0054 .width = -1,
0055 .accel_flags = FB_ACCEL_NONE,
0056 .sync = FB_SYNC_ON_GREEN,
0057 .vmode = FB_VMODE_NONINTERLACED,
0058 };
0059
0060 static const struct fb_fix_screeninfo pmagbbfb_fix = {
0061 .id = "PMAGB-BA",
0062 .smem_len = (2048 * 1024),
0063 .type = FB_TYPE_PACKED_PIXELS,
0064 .visual = FB_VISUAL_PSEUDOCOLOR,
0065 .mmio_len = PMAGB_B_FBMEM,
0066 };
0067
0068
0069 static inline void sfb_write(struct pmagbbfb_par *par, unsigned int reg, u32 v)
0070 {
0071 writel(v, par->sfb + reg / 4);
0072 }
0073
0074 static inline u32 sfb_read(struct pmagbbfb_par *par, unsigned int reg)
0075 {
0076 return readl(par->sfb + reg / 4);
0077 }
0078
0079 static inline void dac_write(struct pmagbbfb_par *par, unsigned int reg, u8 v)
0080 {
0081 writeb(v, par->dac + reg / 4);
0082 }
0083
0084 static inline u8 dac_read(struct pmagbbfb_par *par, unsigned int reg)
0085 {
0086 return readb(par->dac + reg / 4);
0087 }
0088
0089 static inline void gp0_write(struct pmagbbfb_par *par, u32 v)
0090 {
0091 writel(v, par->mmio + PMAGB_B_GP0);
0092 }
0093
0094
0095
0096
0097
0098 static int pmagbbfb_setcolreg(unsigned int regno, unsigned int red,
0099 unsigned int green, unsigned int blue,
0100 unsigned int transp, struct fb_info *info)
0101 {
0102 struct pmagbbfb_par *par = info->par;
0103
0104 if (regno >= info->cmap.len)
0105 return 1;
0106
0107 red >>= 8;
0108 green >>= 8;
0109 blue >>= 8;
0110
0111 mb();
0112 dac_write(par, BT459_ADDR_LO, regno);
0113 dac_write(par, BT459_ADDR_HI, 0x00);
0114 wmb();
0115 dac_write(par, BT459_CMAP, red);
0116 wmb();
0117 dac_write(par, BT459_CMAP, green);
0118 wmb();
0119 dac_write(par, BT459_CMAP, blue);
0120
0121 return 0;
0122 }
0123
0124 static const struct fb_ops pmagbbfb_ops = {
0125 .owner = THIS_MODULE,
0126 .fb_setcolreg = pmagbbfb_setcolreg,
0127 .fb_fillrect = cfb_fillrect,
0128 .fb_copyarea = cfb_copyarea,
0129 .fb_imageblit = cfb_imageblit,
0130 };
0131
0132
0133
0134
0135
0136 static void pmagbbfb_erase_cursor(struct fb_info *info)
0137 {
0138 struct pmagbbfb_par *par = info->par;
0139
0140 mb();
0141 dac_write(par, BT459_ADDR_LO, 0x00);
0142 dac_write(par, BT459_ADDR_HI, 0x03);
0143 wmb();
0144 dac_write(par, BT459_DATA, 0x00);
0145 }
0146
0147
0148
0149
0150 static void pmagbbfb_screen_setup(struct fb_info *info)
0151 {
0152 struct pmagbbfb_par *par = info->par;
0153
0154 info->var.xres = ((sfb_read(par, SFB_REG_VID_HOR) >>
0155 SFB_VID_HOR_PIX_SHIFT) & SFB_VID_HOR_PIX_MASK) * 4;
0156 info->var.xres_virtual = info->var.xres;
0157 info->var.yres = (sfb_read(par, SFB_REG_VID_VER) >>
0158 SFB_VID_VER_SL_SHIFT) & SFB_VID_VER_SL_MASK;
0159 info->var.yres_virtual = info->var.yres;
0160 info->var.left_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
0161 SFB_VID_HOR_BP_SHIFT) &
0162 SFB_VID_HOR_BP_MASK) * 4;
0163 info->var.right_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
0164 SFB_VID_HOR_FP_SHIFT) &
0165 SFB_VID_HOR_FP_MASK) * 4;
0166 info->var.upper_margin = (sfb_read(par, SFB_REG_VID_VER) >>
0167 SFB_VID_VER_BP_SHIFT) & SFB_VID_VER_BP_MASK;
0168 info->var.lower_margin = (sfb_read(par, SFB_REG_VID_VER) >>
0169 SFB_VID_VER_FP_SHIFT) & SFB_VID_VER_FP_MASK;
0170 info->var.hsync_len = ((sfb_read(par, SFB_REG_VID_HOR) >>
0171 SFB_VID_HOR_SYN_SHIFT) &
0172 SFB_VID_HOR_SYN_MASK) * 4;
0173 info->var.vsync_len = (sfb_read(par, SFB_REG_VID_VER) >>
0174 SFB_VID_VER_SYN_SHIFT) & SFB_VID_VER_SYN_MASK;
0175
0176 info->fix.line_length = info->var.xres;
0177 };
0178
0179
0180
0181
0182 static void pmagbbfb_osc_setup(struct fb_info *info)
0183 {
0184 static unsigned int pmagbbfb_freqs[] = {
0185 130808, 119843, 104000, 92980, 74370, 72800,
0186 69197, 66000, 65000, 50350, 36000, 32000, 25175
0187 };
0188 struct pmagbbfb_par *par = info->par;
0189 struct tc_bus *tbus = to_tc_dev(info->device)->bus;
0190 u32 count0 = 8, count1 = 8, counttc = 16 * 256 + 8;
0191 u32 freq0, freq1, freqtc = tc_get_speed(tbus) / 250;
0192 int i, j;
0193
0194 gp0_write(par, 0);
0195 for (j = 0; j < 16; j++) {
0196 mb();
0197 sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
0198 mb();
0199 for (i = 0; i < 100; i++) {
0200 if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
0201 break;
0202 udelay(1);
0203 }
0204 count0 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
0205 }
0206
0207 gp0_write(par, 1);
0208 for (j = 0; j < 16; j++) {
0209 mb();
0210 sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
0211
0212 for (i = 0; i < 100; i++) {
0213 if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
0214 break;
0215 udelay(1);
0216 }
0217 count1 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
0218 }
0219
0220 freq0 = (freqtc * count0 + counttc / 2) / counttc;
0221 par->osc0 = freq0;
0222 if (freq0 >= pmagbbfb_freqs[0] - (pmagbbfb_freqs[0] + 32) / 64 &&
0223 freq0 <= pmagbbfb_freqs[0] + (pmagbbfb_freqs[0] + 32) / 64)
0224 par->osc0 = pmagbbfb_freqs[0];
0225
0226 freq1 = (par->osc0 * count1 + count0 / 2) / count0;
0227 par->osc1 = freq1;
0228 for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++)
0229 if (freq1 >= pmagbbfb_freqs[i] -
0230 (pmagbbfb_freqs[i] + 128) / 256 &&
0231 freq1 <= pmagbbfb_freqs[i] +
0232 (pmagbbfb_freqs[i] + 128) / 256) {
0233 par->osc1 = pmagbbfb_freqs[i];
0234 break;
0235 }
0236
0237 if (par->osc0 - par->osc1 <= (par->osc0 + par->osc1 + 256) / 512 ||
0238 par->osc1 - par->osc0 <= (par->osc0 + par->osc1 + 256) / 512)
0239 par->osc1 = 0;
0240
0241 gp0_write(par, par->osc1 != 0);
0242
0243 info->var.pixclock = par->osc1 ?
0244 (1000000000 + par->osc1 / 2) / par->osc1 :
0245 (1000000000 + par->osc0 / 2) / par->osc0;
0246 };
0247
0248
0249 static int pmagbbfb_probe(struct device *dev)
0250 {
0251 struct tc_dev *tdev = to_tc_dev(dev);
0252 resource_size_t start, len;
0253 struct fb_info *info;
0254 struct pmagbbfb_par *par;
0255 char freq0[12], freq1[12];
0256 u32 vid_base;
0257 int err;
0258
0259 info = framebuffer_alloc(sizeof(struct pmagbbfb_par), dev);
0260 if (!info)
0261 return -ENOMEM;
0262
0263 par = info->par;
0264 dev_set_drvdata(dev, info);
0265
0266 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
0267 printk(KERN_ERR "%s: Cannot allocate color map\n",
0268 dev_name(dev));
0269 err = -ENOMEM;
0270 goto err_alloc;
0271 }
0272
0273 info->fbops = &pmagbbfb_ops;
0274 info->fix = pmagbbfb_fix;
0275 info->var = pmagbbfb_defined;
0276 info->flags = FBINFO_DEFAULT;
0277
0278
0279 start = tdev->resource.start;
0280 len = tdev->resource.end - start + 1;
0281 if (!request_mem_region(start, len, dev_name(dev))) {
0282 printk(KERN_ERR "%s: Cannot reserve FB region\n",
0283 dev_name(dev));
0284 err = -EBUSY;
0285 goto err_cmap;
0286 }
0287
0288
0289 info->fix.mmio_start = start;
0290 par->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len);
0291 if (!par->mmio) {
0292 printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev));
0293 err = -ENOMEM;
0294 goto err_resource;
0295 }
0296 par->sfb = par->mmio + PMAGB_B_SFB;
0297 par->dac = par->mmio + PMAGB_B_BT459;
0298
0299
0300 info->fix.smem_start = start + PMAGB_B_FBMEM;
0301 par->smem = ioremap(info->fix.smem_start, info->fix.smem_len);
0302 if (!par->smem) {
0303 printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev));
0304 err = -ENOMEM;
0305 goto err_mmio_map;
0306 }
0307 vid_base = sfb_read(par, SFB_REG_VID_BASE);
0308 info->screen_base = (void __iomem *)par->smem + vid_base * 0x1000;
0309 info->screen_size = info->fix.smem_len - 2 * vid_base * 0x1000;
0310
0311 pmagbbfb_erase_cursor(info);
0312 pmagbbfb_screen_setup(info);
0313 pmagbbfb_osc_setup(info);
0314
0315 err = register_framebuffer(info);
0316 if (err < 0) {
0317 printk(KERN_ERR "%s: Cannot register framebuffer\n",
0318 dev_name(dev));
0319 goto err_smem_map;
0320 }
0321
0322 get_device(dev);
0323
0324 snprintf(freq0, sizeof(freq0), "%u.%03uMHz",
0325 par->osc0 / 1000, par->osc0 % 1000);
0326 snprintf(freq1, sizeof(freq1), "%u.%03uMHz",
0327 par->osc1 / 1000, par->osc1 % 1000);
0328
0329 fb_info(info, "%s frame buffer device at %s\n",
0330 info->fix.id, dev_name(dev));
0331 fb_info(info, "Osc0: %s, Osc1: %s, Osc%u selected\n",
0332 freq0, par->osc1 ? freq1 : "disabled", par->osc1 != 0);
0333
0334 return 0;
0335
0336
0337 err_smem_map:
0338 iounmap(par->smem);
0339
0340 err_mmio_map:
0341 iounmap(par->mmio);
0342
0343 err_resource:
0344 release_mem_region(start, len);
0345
0346 err_cmap:
0347 fb_dealloc_cmap(&info->cmap);
0348
0349 err_alloc:
0350 framebuffer_release(info);
0351 return err;
0352 }
0353
0354 static int pmagbbfb_remove(struct device *dev)
0355 {
0356 struct tc_dev *tdev = to_tc_dev(dev);
0357 struct fb_info *info = dev_get_drvdata(dev);
0358 struct pmagbbfb_par *par = info->par;
0359 resource_size_t start, len;
0360
0361 put_device(dev);
0362 unregister_framebuffer(info);
0363 iounmap(par->smem);
0364 iounmap(par->mmio);
0365 start = tdev->resource.start;
0366 len = tdev->resource.end - start + 1;
0367 release_mem_region(start, len);
0368 fb_dealloc_cmap(&info->cmap);
0369 framebuffer_release(info);
0370 return 0;
0371 }
0372
0373
0374
0375
0376
0377 static const struct tc_device_id pmagbbfb_tc_table[] = {
0378 { "DEC ", "PMAGB-BA" },
0379 { }
0380 };
0381 MODULE_DEVICE_TABLE(tc, pmagbbfb_tc_table);
0382
0383 static struct tc_driver pmagbbfb_driver = {
0384 .id_table = pmagbbfb_tc_table,
0385 .driver = {
0386 .name = "pmagbbfb",
0387 .bus = &tc_bus_type,
0388 .probe = pmagbbfb_probe,
0389 .remove = pmagbbfb_remove,
0390 },
0391 };
0392
0393 static int __init pmagbbfb_init(void)
0394 {
0395 #ifndef MODULE
0396 if (fb_get_options("pmagbbfb", NULL))
0397 return -ENXIO;
0398 #endif
0399 return tc_register_driver(&pmagbbfb_driver);
0400 }
0401
0402 static void __exit pmagbbfb_exit(void)
0403 {
0404 tc_unregister_driver(&pmagbbfb_driver);
0405 }
0406
0407
0408 module_init(pmagbbfb_init);
0409 module_exit(pmagbbfb_exit);
0410
0411 MODULE_LICENSE("GPL");