root/drivers/video/fbdev/pmagb-b-fb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sfb_write
  2. sfb_read
  3. dac_write
  4. dac_read
  5. gp0_write
  6. pmagbbfb_setcolreg
  7. pmagbbfb_erase_cursor
  8. pmagbbfb_screen_setup
  9. pmagbbfb_osc_setup
  10. pmagbbfb_probe
  11. pmagbbfb_remove
  12. pmagbbfb_init
  13. pmagbbfb_exit

   1 /*
   2  *      linux/drivers/video/pmagb-b-fb.c
   3  *
   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 be
   8  *      found in the file hpfb.c in the same directory.
   9  *
  10  *      DECstation related code Copyright (C) 1999, 2000, 2001 by
  11  *      Michael Engel <engel@unix-ag.org>,
  12  *      Karsten Merker <merker@linuxtag.org> and
  13  *      Harald Koerfgen.
  14  *      Copyright (c) 2005, 2006  Maciej W. Rozycki
  15  *
  16  *      This file is subject to the terms and conditions of the GNU General
  17  *      Public License.  See the file COPYING in the main directory of this
  18  *      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 
  36 struct 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 
  47 static 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 
  60 static 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 
  69 static inline void sfb_write(struct pmagbbfb_par *par, unsigned int reg, u32 v)
  70 {
  71         writel(v, par->sfb + reg / 4);
  72 }
  73 
  74 static inline u32 sfb_read(struct pmagbbfb_par *par, unsigned int reg)
  75 {
  76         return readl(par->sfb + reg / 4);
  77 }
  78 
  79 static inline void dac_write(struct pmagbbfb_par *par, unsigned int reg, u8 v)
  80 {
  81         writeb(v, par->dac + reg / 4);
  82 }
  83 
  84 static inline u8 dac_read(struct pmagbbfb_par *par, unsigned int reg)
  85 {
  86         return readb(par->dac + reg / 4);
  87 }
  88 
  89 static 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  */
  98 static 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 
 124 static struct fb_ops pmagbbfb_ops = {
 125         .owner          = THIS_MODULE,
 126         .fb_setcolreg   = pmagbbfb_setcolreg,
 127         .fb_fillrect    = cfb_fillrect,
 128         .fb_copyarea    = cfb_copyarea,
 129         .fb_imageblit   = cfb_imageblit,
 130 };
 131 
 132 
 133 /*
 134  * Turn the hardware cursor off.
 135  */
 136 static void pmagbbfb_erase_cursor(struct fb_info *info)
 137 {
 138         struct pmagbbfb_par *par = info->par;
 139 
 140         mb();
 141         dac_write(par, BT459_ADDR_LO, 0x00);
 142         dac_write(par, BT459_ADDR_HI, 0x03);
 143         wmb();
 144         dac_write(par, BT459_DATA, 0x00);
 145 }
 146 
 147 /*
 148  * Set up screen parameters.
 149  */
 150 static void pmagbbfb_screen_setup(struct fb_info *info)
 151 {
 152         struct pmagbbfb_par *par = info->par;
 153 
 154         info->var.xres = ((sfb_read(par, SFB_REG_VID_HOR) >>
 155                            SFB_VID_HOR_PIX_SHIFT) & SFB_VID_HOR_PIX_MASK) * 4;
 156         info->var.xres_virtual = info->var.xres;
 157         info->var.yres = (sfb_read(par, SFB_REG_VID_VER) >>
 158                           SFB_VID_VER_SL_SHIFT) & SFB_VID_VER_SL_MASK;
 159         info->var.yres_virtual = info->var.yres;
 160         info->var.left_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
 161                                   SFB_VID_HOR_BP_SHIFT) &
 162                                  SFB_VID_HOR_BP_MASK) * 4;
 163         info->var.right_margin = ((sfb_read(par, SFB_REG_VID_HOR) >>
 164                                    SFB_VID_HOR_FP_SHIFT) &
 165                                   SFB_VID_HOR_FP_MASK) * 4;
 166         info->var.upper_margin = (sfb_read(par, SFB_REG_VID_VER) >>
 167                                   SFB_VID_VER_BP_SHIFT) & SFB_VID_VER_BP_MASK;
 168         info->var.lower_margin = (sfb_read(par, SFB_REG_VID_VER) >>
 169                                   SFB_VID_VER_FP_SHIFT) & SFB_VID_VER_FP_MASK;
 170         info->var.hsync_len = ((sfb_read(par, SFB_REG_VID_HOR) >>
 171                                 SFB_VID_HOR_SYN_SHIFT) &
 172                                SFB_VID_HOR_SYN_MASK) * 4;
 173         info->var.vsync_len = (sfb_read(par, SFB_REG_VID_VER) >>
 174                                SFB_VID_VER_SYN_SHIFT) & SFB_VID_VER_SYN_MASK;
 175 
 176         info->fix.line_length = info->var.xres;
 177 };
 178 
 179 /*
 180  * Determine oscillator configuration.
 181  */
 182 static void pmagbbfb_osc_setup(struct fb_info *info)
 183 {
 184         static unsigned int pmagbbfb_freqs[] = {
 185                 130808, 119843, 104000, 92980, 74370, 72800,
 186                 69197, 66000, 65000, 50350, 36000, 32000, 25175
 187         };
 188         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         int i, j;
 193 
 194         gp0_write(par, 0);                              /* select Osc0 */
 195         for (j = 0; j < 16; j++) {
 196                 mb();
 197                 sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
 198                 mb();
 199                 for (i = 0; i < 100; i++) {     /* nominally max. 20.5us */
 200                         if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
 201                                 break;
 202                         udelay(1);
 203                 }
 204                 count0 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
 205         }
 206 
 207         gp0_write(par, 1);                              /* select Osc1 */
 208         for (j = 0; j < 16; j++) {
 209                 mb();
 210                 sfb_write(par, SFB_REG_TCCLK_COUNT, 0);
 211 
 212                 for (i = 0; i < 100; i++) {     /* nominally max. 20.5us */
 213                         if (sfb_read(par, SFB_REG_TCCLK_COUNT) == 0)
 214                                 break;
 215                         udelay(1);
 216                 }
 217                 count1 += sfb_read(par, SFB_REG_VIDCLK_COUNT);
 218         }
 219 
 220         freq0 = (freqtc * count0 + counttc / 2) / counttc;
 221         par->osc0 = freq0;
 222         if (freq0 >= pmagbbfb_freqs[0] - (pmagbbfb_freqs[0] + 32) / 64 &&
 223             freq0 <= pmagbbfb_freqs[0] + (pmagbbfb_freqs[0] + 32) / 64)
 224                 par->osc0 = pmagbbfb_freqs[0];
 225 
 226         freq1 = (par->osc0 * count1 + count0 / 2) / count0;
 227         par->osc1 = freq1;
 228         for (i = 0; i < ARRAY_SIZE(pmagbbfb_freqs); i++)
 229                 if (freq1 >= pmagbbfb_freqs[i] -
 230                              (pmagbbfb_freqs[i] + 128) / 256 &&
 231                     freq1 <= pmagbbfb_freqs[i] +
 232                              (pmagbbfb_freqs[i] + 128) / 256) {
 233                         par->osc1 = pmagbbfb_freqs[i];
 234                         break;
 235                 }
 236 
 237         if (par->osc0 - par->osc1 <= (par->osc0 + par->osc1 + 256) / 512 ||
 238             par->osc1 - par->osc0 <= (par->osc0 + par->osc1 + 256) / 512)
 239                 par->osc1 = 0;
 240 
 241         gp0_write(par, par->osc1 != 0);                 /* reselect OscX */
 242 
 243         info->var.pixclock = par->osc1 ?
 244                              (1000000000 + par->osc1 / 2) / par->osc1 :
 245                              (1000000000 + par->osc0 / 2) / par->osc0;
 246 };
 247 
 248 
 249 static int pmagbbfb_probe(struct device *dev)
 250 {
 251         struct tc_dev *tdev = to_tc_dev(dev);
 252         resource_size_t start, len;
 253         struct fb_info *info;
 254         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                 return -ENOMEM;
 262 
 263         par = info->par;
 264         dev_set_drvdata(dev, info);
 265 
 266         if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
 267                 printk(KERN_ERR "%s: Cannot allocate color map\n",
 268                        dev_name(dev));
 269                 err = -ENOMEM;
 270                 goto err_alloc;
 271         }
 272 
 273         info->fbops = &pmagbbfb_ops;
 274         info->fix = pmagbbfb_fix;
 275         info->var = pmagbbfb_defined;
 276         info->flags = FBINFO_DEFAULT;
 277 
 278         /* Request the I/O MEM resource.  */
 279         start = tdev->resource.start;
 280         len = tdev->resource.end - start + 1;
 281         if (!request_mem_region(start, len, dev_name(dev))) {
 282                 printk(KERN_ERR "%s: Cannot reserve FB region\n",
 283                        dev_name(dev));
 284                 err = -EBUSY;
 285                 goto err_cmap;
 286         }
 287 
 288         /* MMIO mapping setup.  */
 289         info->fix.mmio_start = start;
 290         par->mmio = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
 291         if (!par->mmio) {
 292                 printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev));
 293                 err = -ENOMEM;
 294                 goto err_resource;
 295         }
 296         par->sfb = par->mmio + PMAGB_B_SFB;
 297         par->dac = par->mmio + PMAGB_B_BT459;
 298 
 299         /* Frame buffer mapping setup.  */
 300         info->fix.smem_start = start + PMAGB_B_FBMEM;
 301         par->smem = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
 302         if (!par->smem) {
 303                 printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev));
 304                 err = -ENOMEM;
 305                 goto err_mmio_map;
 306         }
 307         vid_base = sfb_read(par, SFB_REG_VID_BASE);
 308         info->screen_base = (void __iomem *)par->smem + vid_base * 0x1000;
 309         info->screen_size = info->fix.smem_len - 2 * vid_base * 0x1000;
 310 
 311         pmagbbfb_erase_cursor(info);
 312         pmagbbfb_screen_setup(info);
 313         pmagbbfb_osc_setup(info);
 314 
 315         err = register_framebuffer(info);
 316         if (err < 0) {
 317                 printk(KERN_ERR "%s: Cannot register framebuffer\n",
 318                        dev_name(dev));
 319                 goto err_smem_map;
 320         }
 321 
 322         get_device(dev);
 323 
 324         snprintf(freq0, sizeof(freq0), "%u.%03uMHz",
 325                  par->osc0 / 1000, par->osc0 % 1000);
 326         snprintf(freq1, sizeof(freq1), "%u.%03uMHz",
 327                  par->osc1 / 1000, par->osc1 % 1000);
 328 
 329         fb_info(info, "%s frame buffer device at %s\n",
 330                 info->fix.id, dev_name(dev));
 331         fb_info(info, "Osc0: %s, Osc1: %s, Osc%u selected\n",
 332                 freq0, par->osc1 ? freq1 : "disabled", par->osc1 != 0);
 333 
 334         return 0;
 335 
 336 
 337 err_smem_map:
 338         iounmap(par->smem);
 339 
 340 err_mmio_map:
 341         iounmap(par->mmio);
 342 
 343 err_resource:
 344         release_mem_region(start, len);
 345 
 346 err_cmap:
 347         fb_dealloc_cmap(&info->cmap);
 348 
 349 err_alloc:
 350         framebuffer_release(info);
 351         return err;
 352 }
 353 
 354 static int pmagbbfb_remove(struct device *dev)
 355 {
 356         struct tc_dev *tdev = to_tc_dev(dev);
 357         struct fb_info *info = dev_get_drvdata(dev);
 358         struct pmagbbfb_par *par = info->par;
 359         resource_size_t start, len;
 360 
 361         put_device(dev);
 362         unregister_framebuffer(info);
 363         iounmap(par->smem);
 364         iounmap(par->mmio);
 365         start = tdev->resource.start;
 366         len = tdev->resource.end - start + 1;
 367         release_mem_region(start, len);
 368         fb_dealloc_cmap(&info->cmap);
 369         framebuffer_release(info);
 370         return 0;
 371 }
 372 
 373 
 374 /*
 375  * Initialize the framebuffer.
 376  */
 377 static const struct tc_device_id pmagbbfb_tc_table[] = {
 378         { "DEC     ", "PMAGB-BA" },
 379         { }
 380 };
 381 MODULE_DEVICE_TABLE(tc, pmagbbfb_tc_table);
 382 
 383 static struct tc_driver pmagbbfb_driver = {
 384         .id_table       = pmagbbfb_tc_table,
 385         .driver         = {
 386                 .name   = "pmagbbfb",
 387                 .bus    = &tc_bus_type,
 388                 .probe  = pmagbbfb_probe,
 389                 .remove = pmagbbfb_remove,
 390         },
 391 };
 392 
 393 static int __init pmagbbfb_init(void)
 394 {
 395 #ifndef MODULE
 396         if (fb_get_options("pmagbbfb", NULL))
 397                 return -ENXIO;
 398 #endif
 399         return tc_register_driver(&pmagbbfb_driver);
 400 }
 401 
 402 static void __exit pmagbbfb_exit(void)
 403 {
 404         tc_unregister_driver(&pmagbbfb_driver);
 405 }
 406 
 407 
 408 module_init(pmagbbfb_init);
 409 module_exit(pmagbbfb_exit);
 410 
 411 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */