0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/module.h>
0013 #include <linux/kernel.h>
0014 #include <linux/errno.h>
0015 #include <linux/string.h>
0016 #include <linux/delay.h>
0017 #include <linux/init.h>
0018 #include <linux/fb.h>
0019 #include <linux/mm.h>
0020 #include <linux/of_device.h>
0021
0022 #include <asm/io.h>
0023 #include <asm/fbio.h>
0024
0025 #include "sbuslib.h"
0026
0027
0028
0029
0030
0031 static int bw2_blank(int, struct fb_info *);
0032
0033 static int bw2_mmap(struct fb_info *, struct vm_area_struct *);
0034 static int bw2_ioctl(struct fb_info *, unsigned int, unsigned long);
0035
0036
0037
0038
0039
0040 static const struct fb_ops bw2_ops = {
0041 .owner = THIS_MODULE,
0042 .fb_blank = bw2_blank,
0043 .fb_fillrect = cfb_fillrect,
0044 .fb_copyarea = cfb_copyarea,
0045 .fb_imageblit = cfb_imageblit,
0046 .fb_mmap = bw2_mmap,
0047 .fb_ioctl = bw2_ioctl,
0048 #ifdef CONFIG_COMPAT
0049 .fb_compat_ioctl = sbusfb_compat_ioctl,
0050 #endif
0051 };
0052
0053
0054 #define BWTWO_REGISTER_OFFSET 0x400000
0055
0056 struct bt_regs {
0057 u32 addr;
0058 u32 color_map;
0059 u32 control;
0060 u32 cursor;
0061 };
0062
0063 struct bw2_regs {
0064 struct bt_regs cmap;
0065 u8 control;
0066 u8 status;
0067 u8 cursor_start;
0068 u8 cursor_end;
0069 u8 h_blank_start;
0070 u8 h_blank_end;
0071 u8 h_sync_start;
0072 u8 h_sync_end;
0073 u8 comp_sync_end;
0074 u8 v_blank_start_high;
0075 u8 v_blank_start_low;
0076 u8 v_blank_end;
0077 u8 v_sync_start;
0078 u8 v_sync_end;
0079 u8 xfer_holdoff_start;
0080 u8 xfer_holdoff_end;
0081 };
0082
0083
0084 #define BWTWO_SR_RES_MASK 0x70
0085 #define BWTWO_SR_1600_1280 0x50
0086 #define BWTWO_SR_1152_900_76_A 0x40
0087 #define BWTWO_SR_1152_900_76_B 0x60
0088 #define BWTWO_SR_ID_MASK 0x0f
0089 #define BWTWO_SR_ID_MONO 0x02
0090 #define BWTWO_SR_ID_MONO_ECL 0x03
0091 #define BWTWO_SR_ID_MSYNC 0x04
0092 #define BWTWO_SR_ID_NOCONN 0x0a
0093
0094
0095 #define BWTWO_CTL_ENABLE_INTS 0x80
0096 #define BWTWO_CTL_ENABLE_VIDEO 0x40
0097 #define BWTWO_CTL_ENABLE_TIMING 0x20
0098 #define BWTWO_CTL_ENABLE_CURCMP 0x10
0099 #define BWTWO_CTL_XTAL_MASK 0x0C
0100 #define BWTWO_CTL_DIVISOR_MASK 0x03
0101
0102
0103 #define BWTWO_STAT_PENDING_INT 0x80
0104 #define BWTWO_STAT_MSENSE_MASK 0x70
0105 #define BWTWO_STAT_ID_MASK 0x0f
0106
0107 struct bw2_par {
0108 spinlock_t lock;
0109 struct bw2_regs __iomem *regs;
0110
0111 u32 flags;
0112 #define BW2_FLAG_BLANKED 0x00000001
0113
0114 unsigned long which_io;
0115 };
0116
0117
0118
0119
0120
0121
0122 static int
0123 bw2_blank(int blank, struct fb_info *info)
0124 {
0125 struct bw2_par *par = (struct bw2_par *) info->par;
0126 struct bw2_regs __iomem *regs = par->regs;
0127 unsigned long flags;
0128 u8 val;
0129
0130 spin_lock_irqsave(&par->lock, flags);
0131
0132 switch (blank) {
0133 case FB_BLANK_UNBLANK:
0134 val = sbus_readb(®s->control);
0135 val |= BWTWO_CTL_ENABLE_VIDEO;
0136 sbus_writeb(val, ®s->control);
0137 par->flags &= ~BW2_FLAG_BLANKED;
0138 break;
0139
0140 case FB_BLANK_NORMAL:
0141 case FB_BLANK_VSYNC_SUSPEND:
0142 case FB_BLANK_HSYNC_SUSPEND:
0143 case FB_BLANK_POWERDOWN:
0144 val = sbus_readb(®s->control);
0145 val &= ~BWTWO_CTL_ENABLE_VIDEO;
0146 sbus_writeb(val, ®s->control);
0147 par->flags |= BW2_FLAG_BLANKED;
0148 break;
0149 }
0150
0151 spin_unlock_irqrestore(&par->lock, flags);
0152
0153 return 0;
0154 }
0155
0156 static struct sbus_mmap_map bw2_mmap_map[] = {
0157 {
0158 .size = SBUS_MMAP_FBSIZE(1)
0159 },
0160 { .size = 0 }
0161 };
0162
0163 static int bw2_mmap(struct fb_info *info, struct vm_area_struct *vma)
0164 {
0165 struct bw2_par *par = (struct bw2_par *)info->par;
0166
0167 return sbusfb_mmap_helper(bw2_mmap_map,
0168 info->fix.smem_start, info->fix.smem_len,
0169 par->which_io,
0170 vma);
0171 }
0172
0173 static int bw2_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
0174 {
0175 return sbusfb_ioctl_helper(cmd, arg, info,
0176 FBTYPE_SUN2BW, 1, info->fix.smem_len);
0177 }
0178
0179
0180
0181
0182
0183 static void bw2_init_fix(struct fb_info *info, int linebytes)
0184 {
0185 strscpy(info->fix.id, "bwtwo", sizeof(info->fix.id));
0186
0187 info->fix.type = FB_TYPE_PACKED_PIXELS;
0188 info->fix.visual = FB_VISUAL_MONO01;
0189
0190 info->fix.line_length = linebytes;
0191
0192 info->fix.accel = FB_ACCEL_SUN_BWTWO;
0193 }
0194
0195 static u8 bw2regs_1600[] = {
0196 0x14, 0x8b, 0x15, 0x28, 0x16, 0x03, 0x17, 0x13,
0197 0x18, 0x7b, 0x19, 0x05, 0x1a, 0x34, 0x1b, 0x2e,
0198 0x1c, 0x00, 0x1d, 0x0a, 0x1e, 0xff, 0x1f, 0x01,
0199 0x10, 0x21, 0
0200 };
0201
0202 static u8 bw2regs_ecl[] = {
0203 0x14, 0x65, 0x15, 0x1e, 0x16, 0x04, 0x17, 0x0c,
0204 0x18, 0x5e, 0x19, 0x03, 0x1a, 0xa7, 0x1b, 0x23,
0205 0x1c, 0x00, 0x1d, 0x08, 0x1e, 0xff, 0x1f, 0x01,
0206 0x10, 0x20, 0
0207 };
0208
0209 static u8 bw2regs_analog[] = {
0210 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x03, 0x17, 0x13,
0211 0x18, 0xb0, 0x19, 0x03, 0x1a, 0xa6, 0x1b, 0x22,
0212 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0213 0x10, 0x20, 0
0214 };
0215
0216 static u8 bw2regs_76hz[] = {
0217 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
0218 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
0219 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
0220 0x10, 0x24, 0
0221 };
0222
0223 static u8 bw2regs_66hz[] = {
0224 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
0225 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
0226 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
0227 0x10, 0x20, 0
0228 };
0229
0230 static int bw2_do_default_mode(struct bw2_par *par, struct fb_info *info,
0231 int *linebytes)
0232 {
0233 u8 status, mon;
0234 u8 *p;
0235
0236 status = sbus_readb(&par->regs->status);
0237 mon = status & BWTWO_SR_RES_MASK;
0238 switch (status & BWTWO_SR_ID_MASK) {
0239 case BWTWO_SR_ID_MONO_ECL:
0240 if (mon == BWTWO_SR_1600_1280) {
0241 p = bw2regs_1600;
0242 info->var.xres = info->var.xres_virtual = 1600;
0243 info->var.yres = info->var.yres_virtual = 1280;
0244 *linebytes = 1600 / 8;
0245 } else
0246 p = bw2regs_ecl;
0247 break;
0248
0249 case BWTWO_SR_ID_MONO:
0250 p = bw2regs_analog;
0251 break;
0252
0253 case BWTWO_SR_ID_MSYNC:
0254 if (mon == BWTWO_SR_1152_900_76_A ||
0255 mon == BWTWO_SR_1152_900_76_B)
0256 p = bw2regs_76hz;
0257 else
0258 p = bw2regs_66hz;
0259 break;
0260
0261 case BWTWO_SR_ID_NOCONN:
0262 return 0;
0263
0264 default:
0265 printk(KERN_ERR "bw2: can't handle SR %02x\n",
0266 status);
0267 return -EINVAL;
0268 }
0269 for ( ; *p; p += 2) {
0270 u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
0271 sbus_writeb(p[1], regp);
0272 }
0273 return 0;
0274 }
0275
0276 static int bw2_probe(struct platform_device *op)
0277 {
0278 struct device_node *dp = op->dev.of_node;
0279 struct fb_info *info;
0280 struct bw2_par *par;
0281 int linebytes, err;
0282
0283 info = framebuffer_alloc(sizeof(struct bw2_par), &op->dev);
0284
0285 err = -ENOMEM;
0286 if (!info)
0287 goto out_err;
0288 par = info->par;
0289
0290 spin_lock_init(&par->lock);
0291
0292 info->fix.smem_start = op->resource[0].start;
0293 par->which_io = op->resource[0].flags & IORESOURCE_BITS;
0294
0295 sbusfb_fill_var(&info->var, dp, 1);
0296 linebytes = of_getintprop_default(dp, "linebytes",
0297 info->var.xres);
0298
0299 info->var.red.length = info->var.green.length =
0300 info->var.blue.length = info->var.bits_per_pixel;
0301 info->var.red.offset = info->var.green.offset =
0302 info->var.blue.offset = 0;
0303
0304 par->regs = of_ioremap(&op->resource[0], BWTWO_REGISTER_OFFSET,
0305 sizeof(struct bw2_regs), "bw2 regs");
0306 if (!par->regs)
0307 goto out_release_fb;
0308
0309 if (!of_find_property(dp, "width", NULL)) {
0310 err = bw2_do_default_mode(par, info, &linebytes);
0311 if (err)
0312 goto out_unmap_regs;
0313 }
0314
0315 info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
0316
0317 info->flags = FBINFO_DEFAULT;
0318 info->fbops = &bw2_ops;
0319
0320 info->screen_base = of_ioremap(&op->resource[0], 0,
0321 info->fix.smem_len, "bw2 ram");
0322 if (!info->screen_base) {
0323 err = -ENOMEM;
0324 goto out_unmap_regs;
0325 }
0326
0327 bw2_blank(FB_BLANK_UNBLANK, info);
0328
0329 bw2_init_fix(info, linebytes);
0330
0331 err = register_framebuffer(info);
0332 if (err < 0)
0333 goto out_unmap_screen;
0334
0335 dev_set_drvdata(&op->dev, info);
0336
0337 printk(KERN_INFO "%pOF: bwtwo at %lx:%lx\n",
0338 dp, par->which_io, info->fix.smem_start);
0339
0340 return 0;
0341
0342 out_unmap_screen:
0343 of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
0344
0345 out_unmap_regs:
0346 of_iounmap(&op->resource[0], par->regs, sizeof(struct bw2_regs));
0347
0348 out_release_fb:
0349 framebuffer_release(info);
0350
0351 out_err:
0352 return err;
0353 }
0354
0355 static int bw2_remove(struct platform_device *op)
0356 {
0357 struct fb_info *info = dev_get_drvdata(&op->dev);
0358 struct bw2_par *par = info->par;
0359
0360 unregister_framebuffer(info);
0361
0362 of_iounmap(&op->resource[0], par->regs, sizeof(struct bw2_regs));
0363 of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
0364
0365 framebuffer_release(info);
0366
0367 return 0;
0368 }
0369
0370 static const struct of_device_id bw2_match[] = {
0371 {
0372 .name = "bwtwo",
0373 },
0374 {},
0375 };
0376 MODULE_DEVICE_TABLE(of, bw2_match);
0377
0378 static struct platform_driver bw2_driver = {
0379 .driver = {
0380 .name = "bw2",
0381 .of_match_table = bw2_match,
0382 },
0383 .probe = bw2_probe,
0384 .remove = bw2_remove,
0385 };
0386
0387 static int __init bw2_init(void)
0388 {
0389 if (fb_get_options("bw2fb", NULL))
0390 return -ENODEV;
0391
0392 return platform_driver_register(&bw2_driver);
0393 }
0394
0395 static void __exit bw2_exit(void)
0396 {
0397 platform_driver_unregister(&bw2_driver);
0398 }
0399
0400 module_init(bw2_init);
0401 module_exit(bw2_exit);
0402
0403 MODULE_DESCRIPTION("framebuffer driver for BWTWO chipsets");
0404 MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
0405 MODULE_VERSION("2.0");
0406 MODULE_LICENSE("GPL");