Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  linux/drivers/video/pmagb-b-fb.c
0003  *
0004  *  PMAGB-B TURBOchannel Smart Frame Buffer (SFB) card support,
0005  *  derived from:
0006  *  "HP300 Topcat framebuffer support (derived from macfb of all things)
0007  *  Phil Blundell <philb@gnu.org> 1998", the original code can be
0008  *  found in the file hpfb.c in the same directory.
0009  *
0010  *  DECstation related code Copyright (C) 1999, 2000, 2001 by
0011  *  Michael Engel <engel@unix-ag.org>,
0012  *  Karsten Merker <merker@linuxtag.org> and
0013  *  Harald Koerfgen.
0014  *  Copyright (c) 2005, 2006  Maciej W. Rozycki
0015  *
0016  *  This file is subject to the terms and conditions of the GNU General
0017  *  Public License.  See the file COPYING in the main directory of this
0018  *  archive for more details.
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  * Set the palette.
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;    /* The cmap fields are 16 bits    */
0108     green >>= 8;    /* wide, but the hardware colormap */
0109     blue  >>= 8;    /* registers are only 8 bits wide */
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  * Turn the hardware cursor off.
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  * Set up screen parameters.
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  * Determine oscillator configuration.
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);              /* select Osc0 */
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++) { /* nominally max. 20.5us */
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);              /* select Osc1 */
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++) { /* nominally max. 20.5us */
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);         /* reselect OscX */
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     /* Request the I/O MEM resource.  */
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     /* MMIO mapping setup.  */
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     /* Frame buffer mapping setup.  */
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  * Initialize the framebuffer.
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");