Merge tag 'ubifs-for-linus-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / drivers / video / fbdev / bw2.c
CommitLineData
09c434b8 1// SPDX-License-Identifier: GPL-2.0-only
1da177e4
LT
2/* bw2.c: BWTWO frame buffer driver
3 *
50312ce9 4 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
1da177e4
LT
5 * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
6 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
7 * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
8 *
9 * Driver layout based loosely on tgafb.c, see that file for credits.
10 */
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/errno.h>
15#include <linux/string.h>
1da177e4
LT
16#include <linux/delay.h>
17#include <linux/init.h>
18#include <linux/fb.h>
19#include <linux/mm.h>
e8812acb
RH
20#include <linux/of.h>
21#include <linux/platform_device.h>
1da177e4
LT
22
23#include <asm/io.h>
1da177e4
LT
24#include <asm/fbio.h>
25
1da177e4
LT
26#include "sbuslib.h"
27
28/*
29 * Local functions.
30 */
31
32static int bw2_blank(int, struct fb_info *);
33
34093433
TZ
34static int bw2_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
35static int bw2_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg);
1da177e4
LT
36
37/*
38 * Frame buffer operations
39 */
40
8a48ac33 41static const struct fb_ops bw2_ops = {
1da177e4 42 .owner = THIS_MODULE,
34093433 43 FB_DEFAULT_SBUS_OPS(bw2),
1da177e4 44 .fb_blank = bw2_blank,
1da177e4
LT
45};
46
47/* OBio addresses for the bwtwo registers */
48#define BWTWO_REGISTER_OFFSET 0x400000
49
50struct bt_regs {
50312ce9
DM
51 u32 addr;
52 u32 color_map;
53 u32 control;
54 u32 cursor;
1da177e4
LT
55};
56
57struct bw2_regs {
58 struct bt_regs cmap;
50312ce9
DM
59 u8 control;
60 u8 status;
61 u8 cursor_start;
62 u8 cursor_end;
63 u8 h_blank_start;
64 u8 h_blank_end;
65 u8 h_sync_start;
66 u8 h_sync_end;
67 u8 comp_sync_end;
68 u8 v_blank_start_high;
69 u8 v_blank_start_low;
70 u8 v_blank_end;
71 u8 v_sync_start;
72 u8 v_sync_end;
73 u8 xfer_holdoff_start;
74 u8 xfer_holdoff_end;
1da177e4
LT
75};
76
77/* Status Register Constants */
78#define BWTWO_SR_RES_MASK 0x70
79#define BWTWO_SR_1600_1280 0x50
80#define BWTWO_SR_1152_900_76_A 0x40
81#define BWTWO_SR_1152_900_76_B 0x60
82#define BWTWO_SR_ID_MASK 0x0f
83#define BWTWO_SR_ID_MONO 0x02
84#define BWTWO_SR_ID_MONO_ECL 0x03
85#define BWTWO_SR_ID_MSYNC 0x04
86#define BWTWO_SR_ID_NOCONN 0x0a
87
88/* Control Register Constants */
89#define BWTWO_CTL_ENABLE_INTS 0x80
90#define BWTWO_CTL_ENABLE_VIDEO 0x40
91#define BWTWO_CTL_ENABLE_TIMING 0x20
92#define BWTWO_CTL_ENABLE_CURCMP 0x10
93#define BWTWO_CTL_XTAL_MASK 0x0C
94#define BWTWO_CTL_DIVISOR_MASK 0x03
95
96/* Status Register Constants */
97#define BWTWO_STAT_PENDING_INT 0x80
98#define BWTWO_STAT_MSENSE_MASK 0x70
99#define BWTWO_STAT_ID_MASK 0x0f
100
101struct bw2_par {
102 spinlock_t lock;
103 struct bw2_regs __iomem *regs;
104
105 u32 flags;
106#define BW2_FLAG_BLANKED 0x00000001
107
50312ce9 108 unsigned long which_io;
1da177e4
LT
109};
110
111/**
112 * bw2_blank - Optional function. Blanks the display.
3ccdcdf4 113 * @blank: the blank mode we want.
1da177e4
LT
114 * @info: frame buffer structure that represents a single frame buffer
115 */
116static int
117bw2_blank(int blank, struct fb_info *info)
118{
119 struct bw2_par *par = (struct bw2_par *) info->par;
120 struct bw2_regs __iomem *regs = par->regs;
121 unsigned long flags;
122 u8 val;
123
124 spin_lock_irqsave(&par->lock, flags);
125
126 switch (blank) {
127 case FB_BLANK_UNBLANK: /* Unblanking */
128 val = sbus_readb(&regs->control);
129 val |= BWTWO_CTL_ENABLE_VIDEO;
130 sbus_writeb(val, &regs->control);
131 par->flags &= ~BW2_FLAG_BLANKED;
132 break;
133
134 case FB_BLANK_NORMAL: /* Normal blanking */
135 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
136 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
137 case FB_BLANK_POWERDOWN: /* Poweroff */
138 val = sbus_readb(&regs->control);
139 val &= ~BWTWO_CTL_ENABLE_VIDEO;
140 sbus_writeb(val, &regs->control);
141 par->flags |= BW2_FLAG_BLANKED;
142 break;
143 }
144
145 spin_unlock_irqrestore(&par->lock, flags);
146
147 return 0;
148}
149
150static struct sbus_mmap_map bw2_mmap_map[] = {
151 {
152 .size = SBUS_MMAP_FBSIZE(1)
153 },
154 { .size = 0 }
155};
156
34093433 157static int bw2_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
1da177e4
LT
158{
159 struct bw2_par *par = (struct bw2_par *)info->par;
160
161 return sbusfb_mmap_helper(bw2_mmap_map,
3f06cd29 162 info->fix.smem_start, info->fix.smem_len,
50312ce9 163 par->which_io,
1da177e4
LT
164 vma);
165}
166
34093433 167static int bw2_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
1da177e4 168{
1da177e4 169 return sbusfb_ioctl_helper(cmd, arg, info,
3f06cd29 170 FBTYPE_SUN2BW, 1, info->fix.smem_len);
1da177e4
LT
171}
172
173/*
174 * Initialisation
175 */
176
48c68c4f 177static void bw2_init_fix(struct fb_info *info, int linebytes)
1da177e4 178{
8d026858 179 strscpy(info->fix.id, "bwtwo", sizeof(info->fix.id));
1da177e4
LT
180
181 info->fix.type = FB_TYPE_PACKED_PIXELS;
182 info->fix.visual = FB_VISUAL_MONO01;
183
184 info->fix.line_length = linebytes;
185
186 info->fix.accel = FB_ACCEL_SUN_BWTWO;
187}
188
48c68c4f 189static u8 bw2regs_1600[] = {
1da177e4
LT
190 0x14, 0x8b, 0x15, 0x28, 0x16, 0x03, 0x17, 0x13,
191 0x18, 0x7b, 0x19, 0x05, 0x1a, 0x34, 0x1b, 0x2e,
192 0x1c, 0x00, 0x1d, 0x0a, 0x1e, 0xff, 0x1f, 0x01,
193 0x10, 0x21, 0
194};
195
48c68c4f 196static u8 bw2regs_ecl[] = {
1da177e4
LT
197 0x14, 0x65, 0x15, 0x1e, 0x16, 0x04, 0x17, 0x0c,
198 0x18, 0x5e, 0x19, 0x03, 0x1a, 0xa7, 0x1b, 0x23,
199 0x1c, 0x00, 0x1d, 0x08, 0x1e, 0xff, 0x1f, 0x01,
200 0x10, 0x20, 0
201};
202
48c68c4f 203static u8 bw2regs_analog[] = {
1da177e4
LT
204 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x03, 0x17, 0x13,
205 0x18, 0xb0, 0x19, 0x03, 0x1a, 0xa6, 0x1b, 0x22,
206 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
207 0x10, 0x20, 0
208};
209
48c68c4f 210static u8 bw2regs_76hz[] = {
1da177e4
LT
211 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
212 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
213 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
214 0x10, 0x24, 0
215};
216
48c68c4f 217static u8 bw2regs_66hz[] = {
1da177e4
LT
218 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
219 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
220 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
221 0x10, 0x20, 0
222};
223
48c68c4f
GKH
224static int bw2_do_default_mode(struct bw2_par *par, struct fb_info *info,
225 int *linebytes)
1da177e4
LT
226{
227 u8 status, mon;
228 u8 *p;
229
230 status = sbus_readb(&par->regs->status);
231 mon = status & BWTWO_SR_RES_MASK;
232 switch (status & BWTWO_SR_ID_MASK) {
233 case BWTWO_SR_ID_MONO_ECL:
234 if (mon == BWTWO_SR_1600_1280) {
235 p = bw2regs_1600;
236 info->var.xres = info->var.xres_virtual = 1600;
237 info->var.yres = info->var.yres_virtual = 1280;
238 *linebytes = 1600 / 8;
239 } else
240 p = bw2regs_ecl;
241 break;
242
243 case BWTWO_SR_ID_MONO:
244 p = bw2regs_analog;
245 break;
246
247 case BWTWO_SR_ID_MSYNC:
248 if (mon == BWTWO_SR_1152_900_76_A ||
249 mon == BWTWO_SR_1152_900_76_B)
250 p = bw2regs_76hz;
251 else
252 p = bw2regs_66hz;
253 break;
254
255 case BWTWO_SR_ID_NOCONN:
6c8f5b90 256 return 0;
1da177e4
LT
257
258 default:
6c8f5b90
DM
259 printk(KERN_ERR "bw2: can't handle SR %02x\n",
260 status);
261 return -EINVAL;
1da177e4
LT
262 }
263 for ( ; *p; p += 2) {
264 u8 __iomem *regp = &((u8 __iomem *)par->regs)[p[0]];
265 sbus_writeb(p[1], regp);
266 }
6c8f5b90 267 return 0;
1da177e4
LT
268}
269
48c68c4f 270static int bw2_probe(struct platform_device *op)
1da177e4 271{
d4b8b2c2 272 struct device_node *dp = op->dev.of_node;
c7f439b9
DM
273 struct fb_info *info;
274 struct bw2_par *par;
50312ce9 275 int linebytes, err;
1da177e4 276
c7f439b9 277 info = framebuffer_alloc(sizeof(struct bw2_par), &op->dev);
1da177e4 278
c7f439b9
DM
279 err = -ENOMEM;
280 if (!info)
281 goto out_err;
282 par = info->par;
50312ce9 283
c7f439b9 284 spin_lock_init(&par->lock);
50312ce9 285
3f06cd29 286 info->fix.smem_start = op->resource[0].start;
c7f439b9
DM
287 par->which_io = op->resource[0].flags & IORESOURCE_BITS;
288
6cd5a86b 289 sbusfb_fill_var(&info->var, dp, 1);
50312ce9 290 linebytes = of_getintprop_default(dp, "linebytes",
c7f439b9 291 info->var.xres);
50312ce9 292
c7f439b9
DM
293 info->var.red.length = info->var.green.length =
294 info->var.blue.length = info->var.bits_per_pixel;
295 info->var.red.offset = info->var.green.offset =
296 info->var.blue.offset = 0;
1da177e4 297
c7f439b9
DM
298 par->regs = of_ioremap(&op->resource[0], BWTWO_REGISTER_OFFSET,
299 sizeof(struct bw2_regs), "bw2 regs");
300 if (!par->regs)
301 goto out_release_fb;
1da177e4 302
29413f05 303 if (!of_property_present(dp, "width")) {
6c8f5b90
DM
304 err = bw2_do_default_mode(par, info, &linebytes);
305 if (err)
306 goto out_unmap_regs;
307 }
1da177e4 308
3f06cd29 309 info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
1da177e4 310
c7f439b9 311 info->fbops = &bw2_ops;
50312ce9 312
c7f439b9 313 info->screen_base = of_ioremap(&op->resource[0], 0,
3f06cd29 314 info->fix.smem_len, "bw2 ram");
0ca30b1b
PST
315 if (!info->screen_base) {
316 err = -ENOMEM;
c7f439b9 317 goto out_unmap_regs;
0ca30b1b 318 }
1da177e4 319
59f7137a 320 bw2_blank(FB_BLANK_UNBLANK, info);
1da177e4 321
c7f439b9 322 bw2_init_fix(info, linebytes);
1da177e4 323
c7f439b9
DM
324 err = register_framebuffer(info);
325 if (err < 0)
326 goto out_unmap_screen;
1da177e4 327
c7f439b9 328 dev_set_drvdata(&op->dev, info);
50312ce9 329
6d7e6533
RH
330 printk(KERN_INFO "%pOF: bwtwo at %lx:%lx\n",
331 dp, par->which_io, info->fix.smem_start);
1da177e4 332
50312ce9 333 return 0;
1da177e4 334
c7f439b9 335out_unmap_screen:
3f06cd29 336 of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
c7f439b9
DM
337
338out_unmap_regs:
339 of_iounmap(&op->resource[0], par->regs, sizeof(struct bw2_regs));
340
341out_release_fb:
342 framebuffer_release(info);
1da177e4 343
c7f439b9
DM
344out_err:
345 return err;
50312ce9 346}
1da177e4 347
d19663ed 348static void bw2_remove(struct platform_device *op)
50312ce9 349{
c7f439b9
DM
350 struct fb_info *info = dev_get_drvdata(&op->dev);
351 struct bw2_par *par = info->par;
50312ce9 352
c7f439b9 353 unregister_framebuffer(info);
50312ce9 354
c7f439b9 355 of_iounmap(&op->resource[0], par->regs, sizeof(struct bw2_regs));
3f06cd29 356 of_iounmap(&op->resource[0], info->screen_base, info->fix.smem_len);
50312ce9 357
c7f439b9 358 framebuffer_release(info);
1da177e4
LT
359}
360
fd098316 361static const struct of_device_id bw2_match[] = {
50312ce9
DM
362 {
363 .name = "bwtwo",
364 },
365 {},
366};
367MODULE_DEVICE_TABLE(of, bw2_match);
1da177e4 368
28541d0f 369static struct platform_driver bw2_driver = {
4018294b
GL
370 .driver = {
371 .name = "bw2",
4018294b
GL
372 .of_match_table = bw2_match,
373 },
50312ce9 374 .probe = bw2_probe,
d19663ed 375 .remove_new = bw2_remove,
50312ce9 376};
1da177e4 377
50312ce9
DM
378static int __init bw2_init(void)
379{
380 if (fb_get_options("bw2fb", NULL))
381 return -ENODEV;
382
28541d0f 383 return platform_driver_register(&bw2_driver);
1da177e4
LT
384}
385
50312ce9 386static void __exit bw2_exit(void)
1da177e4 387{
28541d0f 388 platform_driver_unregister(&bw2_driver);
1da177e4
LT
389}
390
50312ce9 391module_init(bw2_init);
1da177e4 392module_exit(bw2_exit);
1da177e4
LT
393
394MODULE_DESCRIPTION("framebuffer driver for BWTWO chipsets");
50312ce9
DM
395MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
396MODULE_VERSION("2.0");
1da177e4 397MODULE_LICENSE("GPL");