683 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* leo.c: LEO frame buffer driver3 *4 * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)5 * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz)6 * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz)7 *8 * Driver layout based loosely on tgafb.c, see that file for credits.9 */10 11#include <linux/module.h>12#include <linux/kernel.h>13#include <linux/errno.h>14#include <linux/string.h>15#include <linux/delay.h>16#include <linux/init.h>17#include <linux/fb.h>18#include <linux/mm.h>19#include <linux/io.h>20#include <linux/of.h>21#include <linux/platform_device.h>22 23#include <asm/fbio.h>24 25#include "sbuslib.h"26 27/*28 * Local functions.29 */30 31static int leo_setcolreg(unsigned, unsigned, unsigned, unsigned,32 unsigned, struct fb_info *);33static int leo_blank(int, struct fb_info *);34static int leo_pan_display(struct fb_var_screeninfo *, struct fb_info *);35 36static int leo_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma);37static int leo_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg);38 39/*40 * Frame buffer operations41 */42 43static const struct fb_ops leo_ops = {44 .owner = THIS_MODULE,45 FB_DEFAULT_SBUS_OPS(leo),46 .fb_setcolreg = leo_setcolreg,47 .fb_blank = leo_blank,48 .fb_pan_display = leo_pan_display,49};50 51#define LEO_OFF_LC_SS0_KRN 0x00200000UL52#define LEO_OFF_LC_SS0_USR 0x00201000UL53#define LEO_OFF_LC_SS1_KRN 0x01200000UL54#define LEO_OFF_LC_SS1_USR 0x01201000UL55#define LEO_OFF_LD_SS0 0x00400000UL56#define LEO_OFF_LD_SS1 0x01400000UL57#define LEO_OFF_LD_GBL 0x00401000UL58#define LEO_OFF_LX_KRN 0x00600000UL59#define LEO_OFF_LX_CURSOR 0x00601000UL60#define LEO_OFF_SS0 0x00800000UL61#define LEO_OFF_SS1 0x01800000UL62#define LEO_OFF_UNK 0x00602000UL63#define LEO_OFF_UNK2 0x00000000UL64 65#define LEO_CUR_ENABLE 0x0000008066#define LEO_CUR_UPDATE 0x0000003067#define LEO_CUR_PROGRESS 0x0000000668#define LEO_CUR_UPDATECMAP 0x0000000369 70#define LEO_CUR_TYPE_MASK 0x0000000071#define LEO_CUR_TYPE_IMAGE 0x0000002072#define LEO_CUR_TYPE_CMAP 0x0000005073 74struct leo_cursor {75 u8 xxx0[16];76 u32 cur_type;77 u32 cur_misc;78 u32 cur_cursxy;79 u32 cur_data;80};81 82#define LEO_KRN_TYPE_CLUT0 0x0000100083#define LEO_KRN_TYPE_CLUT1 0x0000100184#define LEO_KRN_TYPE_CLUT2 0x0000100285#define LEO_KRN_TYPE_WID 0x0000100386#define LEO_KRN_TYPE_UNK 0x0000100687#define LEO_KRN_TYPE_VIDEO 0x0000200388#define LEO_KRN_TYPE_CLUTDATA 0x0000400089#define LEO_KRN_CSR_ENABLE 0x0000000890#define LEO_KRN_CSR_PROGRESS 0x0000000491#define LEO_KRN_CSR_UNK 0x0000000292#define LEO_KRN_CSR_UNK2 0x0000000193 94struct leo_lx_krn {95 u32 krn_type;96 u32 krn_csr;97 u32 krn_value;98};99 100struct leo_lc_ss0_krn {101 u32 misc;102 u8 xxx0[0x800-4];103 u32 rev;104};105 106struct leo_lc_ss0_usr {107 u32 csr;108 u32 addrspace;109 u32 fontmsk;110 u32 fontt;111 u32 extent;112 u32 src;113 u32 dst;114 u32 copy;115 u32 fill;116};117 118struct leo_lc_ss1_krn {119 u8 unknown;120};121 122struct leo_lc_ss1_usr {123 u8 unknown;124};125 126struct leo_ld_ss0 {127 u8 xxx0[0xe00];128 u32 csr;129 u32 wid;130 u32 wmask;131 u32 widclip;132 u32 vclipmin;133 u32 vclipmax;134 u32 pickmin; /* SS1 only */135 u32 pickmax; /* SS1 only */136 u32 fg;137 u32 bg;138 u32 src; /* Copy/Scroll (SS0 only) */139 u32 dst; /* Copy/Scroll/Fill (SS0 only) */140 u32 extent; /* Copy/Scroll/Fill size (SS0 only) */141 u32 xxx1[3];142 u32 setsem; /* SS1 only */143 u32 clrsem; /* SS1 only */144 u32 clrpick; /* SS1 only */145 u32 clrdat; /* SS1 only */146 u32 alpha; /* SS1 only */147 u8 xxx2[0x2c];148 u32 winbg;149 u32 planemask;150 u32 rop;151 u32 z;152 u32 dczf; /* SS1 only */153 u32 dczb; /* SS1 only */154 u32 dcs; /* SS1 only */155 u32 dczs; /* SS1 only */156 u32 pickfb; /* SS1 only */157 u32 pickbb; /* SS1 only */158 u32 dcfc; /* SS1 only */159 u32 forcecol; /* SS1 only */160 u32 door[8]; /* SS1 only */161 u32 pick[5]; /* SS1 only */162};163 164#define LEO_SS1_MISC_ENABLE 0x00000001165#define LEO_SS1_MISC_STEREO 0x00000002166struct leo_ld_ss1 {167 u8 xxx0[0xef4];168 u32 ss1_misc;169};170 171struct leo_ld_gbl {172 u8 unknown;173};174 175struct leo_par {176 spinlock_t lock;177 struct leo_lx_krn __iomem *lx_krn;178 struct leo_lc_ss0_usr __iomem *lc_ss0_usr;179 struct leo_ld_ss0 __iomem *ld_ss0;180 struct leo_ld_ss1 __iomem *ld_ss1;181 struct leo_cursor __iomem *cursor;182 u32 extent;183 u32 clut_data[256];184 185 u32 flags;186#define LEO_FLAG_BLANKED 0x00000001187 188 unsigned long which_io;189};190 191static void leo_wait(struct leo_lx_krn __iomem *lx_krn)192{193 int i;194 195 for (i = 0;196 (sbus_readl(&lx_krn->krn_csr) & LEO_KRN_CSR_PROGRESS) &&197 i < 300000;198 i++)199 udelay(1); /* Busy wait at most 0.3 sec */200 return;201}202 203static void leo_switch_from_graph(struct fb_info *info)204{205 struct leo_par *par = (struct leo_par *) info->par;206 struct leo_ld_ss0 __iomem *ss = par->ld_ss0;207 struct leo_cursor __iomem *cursor = par->cursor;208 unsigned long flags;209 u32 val;210 211 spin_lock_irqsave(&par->lock, flags);212 213 par->extent = ((info->var.xres - 1) |214 ((info->var.yres - 1) << 16));215 216 sbus_writel(0xffffffff, &ss->wid);217 sbus_writel(0xffff, &ss->wmask);218 sbus_writel(0, &ss->vclipmin);219 sbus_writel(par->extent, &ss->vclipmax);220 sbus_writel(0, &ss->fg);221 sbus_writel(0xff000000, &ss->planemask);222 sbus_writel(0x310850, &ss->rop);223 sbus_writel(0, &ss->widclip);224 sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11),225 &par->lc_ss0_usr->extent);226 sbus_writel(4, &par->lc_ss0_usr->addrspace);227 sbus_writel(0x80000000, &par->lc_ss0_usr->fill);228 sbus_writel(0, &par->lc_ss0_usr->fontt);229 do {230 val = sbus_readl(&par->lc_ss0_usr->csr);231 } while (val & 0x20000000);232 233 /* setup screen buffer for cfb_* functions */234 sbus_writel(1, &ss->wid);235 sbus_writel(0x00ffffff, &ss->planemask);236 sbus_writel(0x310b90, &ss->rop);237 sbus_writel(0, &par->lc_ss0_usr->addrspace);238 239 /* hide cursor */240 sbus_writel(sbus_readl(&cursor->cur_misc) & ~LEO_CUR_ENABLE, &cursor->cur_misc);241 242 spin_unlock_irqrestore(&par->lock, flags);243}244 245static int leo_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)246{247 /* We just use this to catch switches out of248 * graphics mode.249 */250 leo_switch_from_graph(info);251 252 if (var->xoffset || var->yoffset || var->vmode)253 return -EINVAL;254 return 0;255}256 257/**258 * leo_setcolreg - Optional function. Sets a color register.259 * @regno: boolean, 0 copy local, 1 get_user() function260 * @red: frame buffer colormap structure261 * @green: The green value which can be up to 16 bits wide262 * @blue: The blue value which can be up to 16 bits wide.263 * @transp: If supported the alpha value which can be up to 16 bits wide.264 * @info: frame buffer info structure265 */266static int leo_setcolreg(unsigned regno,267 unsigned red, unsigned green, unsigned blue,268 unsigned transp, struct fb_info *info)269{270 struct leo_par *par = (struct leo_par *) info->par;271 struct leo_lx_krn __iomem *lx_krn = par->lx_krn;272 unsigned long flags;273 u32 val;274 int i;275 276 if (regno >= 256)277 return 1;278 279 red >>= 8;280 green >>= 8;281 blue >>= 8;282 283 par->clut_data[regno] = red | (green << 8) | (blue << 16);284 285 spin_lock_irqsave(&par->lock, flags);286 287 leo_wait(lx_krn);288 289 sbus_writel(LEO_KRN_TYPE_CLUTDATA, &lx_krn->krn_type);290 for (i = 0; i < 256; i++)291 sbus_writel(par->clut_data[i], &lx_krn->krn_value);292 sbus_writel(LEO_KRN_TYPE_CLUT0, &lx_krn->krn_type);293 294 val = sbus_readl(&lx_krn->krn_csr);295 val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);296 sbus_writel(val, &lx_krn->krn_csr);297 298 spin_unlock_irqrestore(&par->lock, flags);299 300 return 0;301}302 303/**304 * leo_blank - Optional function. Blanks the display.305 * @blank: the blank mode we want.306 * @info: frame buffer structure that represents a single frame buffer307 */308static int leo_blank(int blank, struct fb_info *info)309{310 struct leo_par *par = (struct leo_par *) info->par;311 struct leo_lx_krn __iomem *lx_krn = par->lx_krn;312 unsigned long flags;313 u32 val;314 315 spin_lock_irqsave(&par->lock, flags);316 317 switch (blank) {318 case FB_BLANK_UNBLANK: /* Unblanking */319 val = sbus_readl(&lx_krn->krn_csr);320 val |= LEO_KRN_CSR_ENABLE;321 sbus_writel(val, &lx_krn->krn_csr);322 par->flags &= ~LEO_FLAG_BLANKED;323 break;324 325 case FB_BLANK_NORMAL: /* Normal blanking */326 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */327 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */328 case FB_BLANK_POWERDOWN: /* Poweroff */329 val = sbus_readl(&lx_krn->krn_csr);330 val &= ~LEO_KRN_CSR_ENABLE;331 sbus_writel(val, &lx_krn->krn_csr);332 par->flags |= LEO_FLAG_BLANKED;333 break;334 }335 336 spin_unlock_irqrestore(&par->lock, flags);337 338 return 0;339}340 341static const struct sbus_mmap_map leo_mmap_map[] = {342 {343 .voff = LEO_SS0_MAP,344 .poff = LEO_OFF_SS0,345 .size = 0x800000346 },347 {348 .voff = LEO_LC_SS0_USR_MAP,349 .poff = LEO_OFF_LC_SS0_USR,350 .size = 0x1000351 },352 {353 .voff = LEO_LD_SS0_MAP,354 .poff = LEO_OFF_LD_SS0,355 .size = 0x1000356 },357 {358 .voff = LEO_LX_CURSOR_MAP,359 .poff = LEO_OFF_LX_CURSOR,360 .size = 0x1000361 },362 {363 .voff = LEO_SS1_MAP,364 .poff = LEO_OFF_SS1,365 .size = 0x800000366 },367 {368 .voff = LEO_LC_SS1_USR_MAP,369 .poff = LEO_OFF_LC_SS1_USR,370 .size = 0x1000371 },372 {373 .voff = LEO_LD_SS1_MAP,374 .poff = LEO_OFF_LD_SS1,375 .size = 0x1000376 },377 {378 .voff = LEO_UNK_MAP,379 .poff = LEO_OFF_UNK,380 .size = 0x1000381 },382 {383 .voff = LEO_LX_KRN_MAP,384 .poff = LEO_OFF_LX_KRN,385 .size = 0x1000386 },387 {388 .voff = LEO_LC_SS0_KRN_MAP,389 .poff = LEO_OFF_LC_SS0_KRN,390 .size = 0x1000391 },392 {393 .voff = LEO_LC_SS1_KRN_MAP,394 .poff = LEO_OFF_LC_SS1_KRN,395 .size = 0x1000396 },397 {398 .voff = LEO_LD_GBL_MAP,399 .poff = LEO_OFF_LD_GBL,400 .size = 0x1000401 },402 {403 .voff = LEO_UNK2_MAP,404 .poff = LEO_OFF_UNK2,405 .size = 0x100000406 },407 { .size = 0 }408};409 410static int leo_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma)411{412 struct leo_par *par = (struct leo_par *)info->par;413 414 return sbusfb_mmap_helper(leo_mmap_map,415 info->fix.smem_start, info->fix.smem_len,416 par->which_io, vma);417}418 419static int leo_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)420{421 return sbusfb_ioctl_helper(cmd, arg, info,422 FBTYPE_SUNLEO, 32, info->fix.smem_len);423}424 425/*426 * Initialisation427 */428 429static void430leo_init_fix(struct fb_info *info, struct device_node *dp)431{432 snprintf(info->fix.id, sizeof(info->fix.id), "%pOFn", dp);433 434 info->fix.type = FB_TYPE_PACKED_PIXELS;435 info->fix.visual = FB_VISUAL_TRUECOLOR;436 437 info->fix.line_length = 8192;438 439 info->fix.accel = FB_ACCEL_SUN_LEO;440}441 442static void leo_wid_put(struct fb_info *info, struct fb_wid_list *wl)443{444 struct leo_par *par = (struct leo_par *) info->par;445 struct leo_lx_krn __iomem *lx_krn = par->lx_krn;446 struct fb_wid_item *wi;447 unsigned long flags;448 u32 val;449 int i, j;450 451 spin_lock_irqsave(&par->lock, flags);452 453 leo_wait(lx_krn);454 455 for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) {456 switch (wi->wi_type) {457 case FB_WID_DBL_8:458 j = (wi->wi_index & 0xf) + 0x40;459 break;460 461 case FB_WID_DBL_24:462 j = wi->wi_index & 0x3f;463 break;464 465 default:466 continue;467 }468 sbus_writel(0x5800 + j, &lx_krn->krn_type);469 sbus_writel(wi->wi_values[0], &lx_krn->krn_value);470 }471 sbus_writel(LEO_KRN_TYPE_WID, &lx_krn->krn_type);472 473 val = sbus_readl(&lx_krn->krn_csr);474 val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2);475 sbus_writel(val, &lx_krn->krn_csr);476 477 spin_unlock_irqrestore(&par->lock, flags);478}479 480static void leo_init_wids(struct fb_info *info)481{482 struct fb_wid_item wi;483 struct fb_wid_list wl;484 485 wl.wl_count = 1;486 wl.wl_list = &wi;487 wi.wi_type = FB_WID_DBL_8;488 wi.wi_index = 0;489 wi.wi_values [0] = 0x2c0;490 leo_wid_put(info, &wl);491 wi.wi_index = 1;492 wi.wi_values [0] = 0x30;493 leo_wid_put(info, &wl);494 wi.wi_index = 2;495 wi.wi_values [0] = 0x20;496 leo_wid_put(info, &wl);497 wi.wi_type = FB_WID_DBL_24;498 wi.wi_index = 1;499 wi.wi_values [0] = 0x30;500 leo_wid_put(info, &wl);501}502 503static void leo_init_hw(struct fb_info *info)504{505 struct leo_par *par = (struct leo_par *) info->par;506 u32 val;507 508 val = sbus_readl(&par->ld_ss1->ss1_misc);509 val |= LEO_SS1_MISC_ENABLE;510 sbus_writel(val, &par->ld_ss1->ss1_misc);511 512 leo_switch_from_graph(info);513}514 515static void leo_fixup_var_rgb(struct fb_var_screeninfo *var)516{517 var->red.offset = 0;518 var->red.length = 8;519 var->green.offset = 8;520 var->green.length = 8;521 var->blue.offset = 16;522 var->blue.length = 8;523 var->transp.offset = 0;524 var->transp.length = 0;525}526 527static void leo_unmap_regs(struct platform_device *op, struct fb_info *info,528 struct leo_par *par)529{530 if (par->lc_ss0_usr)531 of_iounmap(&op->resource[0], par->lc_ss0_usr, 0x1000);532 if (par->ld_ss0)533 of_iounmap(&op->resource[0], par->ld_ss0, 0x1000);534 if (par->ld_ss1)535 of_iounmap(&op->resource[0], par->ld_ss1, 0x1000);536 if (par->lx_krn)537 of_iounmap(&op->resource[0], par->lx_krn, 0x1000);538 if (par->cursor)539 of_iounmap(&op->resource[0],540 par->cursor, sizeof(struct leo_cursor));541 if (info->screen_base)542 of_iounmap(&op->resource[0], info->screen_base, 0x800000);543}544 545static int leo_probe(struct platform_device *op)546{547 struct device_node *dp = op->dev.of_node;548 struct fb_info *info;549 struct leo_par *par;550 int linebytes, err;551 552 info = framebuffer_alloc(sizeof(struct leo_par), &op->dev);553 554 err = -ENOMEM;555 if (!info)556 goto out_err;557 par = info->par;558 559 spin_lock_init(&par->lock);560 561 info->fix.smem_start = op->resource[0].start;562 par->which_io = op->resource[0].flags & IORESOURCE_BITS;563 564 sbusfb_fill_var(&info->var, dp, 32);565 leo_fixup_var_rgb(&info->var);566 567 linebytes = of_getintprop_default(dp, "linebytes",568 info->var.xres);569 info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);570 571 par->lc_ss0_usr =572 of_ioremap(&op->resource[0], LEO_OFF_LC_SS0_USR,573 0x1000, "leolc ss0usr");574 par->ld_ss0 =575 of_ioremap(&op->resource[0], LEO_OFF_LD_SS0,576 0x1000, "leold ss0");577 par->ld_ss1 =578 of_ioremap(&op->resource[0], LEO_OFF_LD_SS1,579 0x1000, "leold ss1");580 par->lx_krn =581 of_ioremap(&op->resource[0], LEO_OFF_LX_KRN,582 0x1000, "leolx krn");583 par->cursor =584 of_ioremap(&op->resource[0], LEO_OFF_LX_CURSOR,585 sizeof(struct leo_cursor), "leolx cursor");586 info->screen_base =587 of_ioremap(&op->resource[0], LEO_OFF_SS0,588 0x800000, "leo ram");589 if (!par->lc_ss0_usr ||590 !par->ld_ss0 ||591 !par->ld_ss1 ||592 !par->lx_krn ||593 !par->cursor ||594 !info->screen_base)595 goto out_unmap_regs;596 597 info->fbops = &leo_ops;598 info->pseudo_palette = par->clut_data;599 600 leo_init_wids(info);601 leo_init_hw(info);602 603 leo_blank(FB_BLANK_UNBLANK, info);604 605 if (fb_alloc_cmap(&info->cmap, 256, 0))606 goto out_unmap_regs;607 608 leo_init_fix(info, dp);609 610 err = register_framebuffer(info);611 if (err < 0)612 goto out_dealloc_cmap;613 614 dev_set_drvdata(&op->dev, info);615 616 printk(KERN_INFO "%pOF: leo at %lx:%lx\n",617 dp,618 par->which_io, info->fix.smem_start);619 620 return 0;621 622out_dealloc_cmap:623 fb_dealloc_cmap(&info->cmap);624 625out_unmap_regs:626 leo_unmap_regs(op, info, par);627 framebuffer_release(info);628 629out_err:630 return err;631}632 633static void leo_remove(struct platform_device *op)634{635 struct fb_info *info = dev_get_drvdata(&op->dev);636 struct leo_par *par = info->par;637 638 unregister_framebuffer(info);639 fb_dealloc_cmap(&info->cmap);640 641 leo_unmap_regs(op, info, par);642 643 framebuffer_release(info);644}645 646static const struct of_device_id leo_match[] = {647 {648 .name = "SUNW,leo",649 },650 {},651};652MODULE_DEVICE_TABLE(of, leo_match);653 654static struct platform_driver leo_driver = {655 .driver = {656 .name = "leo",657 .of_match_table = leo_match,658 },659 .probe = leo_probe,660 .remove = leo_remove,661};662 663static int __init leo_init(void)664{665 if (fb_get_options("leofb", NULL))666 return -ENODEV;667 668 return platform_driver_register(&leo_driver);669}670 671static void __exit leo_exit(void)672{673 platform_driver_unregister(&leo_driver);674}675 676module_init(leo_init);677module_exit(leo_exit);678 679MODULE_DESCRIPTION("framebuffer driver for LEO chipsets");680MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");681MODULE_VERSION("2.0");682MODULE_LICENSE("GPL");683