brintos

brintos / linux-shallow public Read only

0
0
Text · 15.2 KiB · b9fb059 Raw
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