162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* tcx.c: TCX frame buffer driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net)
562306a36Sopenharmony_ci * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
662306a36Sopenharmony_ci * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
762306a36Sopenharmony_ci * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Driver layout based loosely on tgafb.c, see that file for credits.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/fb.h>
1962306a36Sopenharmony_ci#include <linux/mm.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/io.h>
2462306a36Sopenharmony_ci#include <asm/fbio.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "sbuslib.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * Local functions.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int tcx_setcolreg(unsigned, unsigned, unsigned, unsigned,
3362306a36Sopenharmony_ci			 unsigned, struct fb_info *);
3462306a36Sopenharmony_cistatic int tcx_blank(int, struct fb_info *);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int tcx_mmap(struct fb_info *, struct vm_area_struct *);
3762306a36Sopenharmony_cistatic int tcx_ioctl(struct fb_info *, unsigned int, unsigned long);
3862306a36Sopenharmony_cistatic int tcx_pan_display(struct fb_var_screeninfo *, struct fb_info *);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci *  Frame buffer operations
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct fb_ops tcx_ops = {
4562306a36Sopenharmony_ci	.owner			= THIS_MODULE,
4662306a36Sopenharmony_ci	.fb_setcolreg		= tcx_setcolreg,
4762306a36Sopenharmony_ci	.fb_blank		= tcx_blank,
4862306a36Sopenharmony_ci	.fb_pan_display		= tcx_pan_display,
4962306a36Sopenharmony_ci	.fb_fillrect		= cfb_fillrect,
5062306a36Sopenharmony_ci	.fb_copyarea		= cfb_copyarea,
5162306a36Sopenharmony_ci	.fb_imageblit		= cfb_imageblit,
5262306a36Sopenharmony_ci	.fb_mmap		= tcx_mmap,
5362306a36Sopenharmony_ci	.fb_ioctl		= tcx_ioctl,
5462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
5562306a36Sopenharmony_ci	.fb_compat_ioctl	= sbusfb_compat_ioctl,
5662306a36Sopenharmony_ci#endif
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* THC definitions */
6062306a36Sopenharmony_ci#define TCX_THC_MISC_REV_SHIFT       16
6162306a36Sopenharmony_ci#define TCX_THC_MISC_REV_MASK        15
6262306a36Sopenharmony_ci#define TCX_THC_MISC_VSYNC_DIS       (1 << 25)
6362306a36Sopenharmony_ci#define TCX_THC_MISC_HSYNC_DIS       (1 << 24)
6462306a36Sopenharmony_ci#define TCX_THC_MISC_RESET           (1 << 12)
6562306a36Sopenharmony_ci#define TCX_THC_MISC_VIDEO           (1 << 10)
6662306a36Sopenharmony_ci#define TCX_THC_MISC_SYNC            (1 << 9)
6762306a36Sopenharmony_ci#define TCX_THC_MISC_VSYNC           (1 << 8)
6862306a36Sopenharmony_ci#define TCX_THC_MISC_SYNC_ENAB       (1 << 7)
6962306a36Sopenharmony_ci#define TCX_THC_MISC_CURS_RES        (1 << 6)
7062306a36Sopenharmony_ci#define TCX_THC_MISC_INT_ENAB        (1 << 5)
7162306a36Sopenharmony_ci#define TCX_THC_MISC_INT             (1 << 4)
7262306a36Sopenharmony_ci#define TCX_THC_MISC_INIT            0x9f
7362306a36Sopenharmony_ci#define TCX_THC_REV_REV_SHIFT        20
7462306a36Sopenharmony_ci#define TCX_THC_REV_REV_MASK         15
7562306a36Sopenharmony_ci#define TCX_THC_REV_MINREV_SHIFT     28
7662306a36Sopenharmony_ci#define TCX_THC_REV_MINREV_MASK      15
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* The contents are unknown */
7962306a36Sopenharmony_cistruct tcx_tec {
8062306a36Sopenharmony_ci	u32 tec_matrix;
8162306a36Sopenharmony_ci	u32 tec_clip;
8262306a36Sopenharmony_ci	u32 tec_vdc;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct tcx_thc {
8662306a36Sopenharmony_ci	u32 thc_rev;
8762306a36Sopenharmony_ci	u32 thc_pad0[511];
8862306a36Sopenharmony_ci	u32 thc_hs;		/* hsync timing */
8962306a36Sopenharmony_ci	u32 thc_hsdvs;
9062306a36Sopenharmony_ci	u32 thc_hd;
9162306a36Sopenharmony_ci	u32 thc_vs;		/* vsync timing */
9262306a36Sopenharmony_ci	u32 thc_vd;
9362306a36Sopenharmony_ci	u32 thc_refresh;
9462306a36Sopenharmony_ci	u32 thc_misc;
9562306a36Sopenharmony_ci	u32 thc_pad1[56];
9662306a36Sopenharmony_ci	u32 thc_cursxy;	/* cursor x,y position (16 bits each) */
9762306a36Sopenharmony_ci	u32 thc_cursmask[32];	/* cursor mask bits */
9862306a36Sopenharmony_ci	u32 thc_cursbits[32];	/* what to show where mask enabled */
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct bt_regs {
10262306a36Sopenharmony_ci	u32 addr;
10362306a36Sopenharmony_ci	u32 color_map;
10462306a36Sopenharmony_ci	u32 control;
10562306a36Sopenharmony_ci	u32 cursor;
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define TCX_MMAP_ENTRIES 14
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct tcx_par {
11162306a36Sopenharmony_ci	spinlock_t		lock;
11262306a36Sopenharmony_ci	struct bt_regs		__iomem *bt;
11362306a36Sopenharmony_ci	struct tcx_thc		__iomem *thc;
11462306a36Sopenharmony_ci	struct tcx_tec		__iomem *tec;
11562306a36Sopenharmony_ci	u32			__iomem *cplane;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	u32			flags;
11862306a36Sopenharmony_ci#define TCX_FLAG_BLANKED	0x00000001
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	unsigned long		which_io;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	struct sbus_mmap_map	mmap_map[TCX_MMAP_ENTRIES];
12362306a36Sopenharmony_ci	int			lowdepth;
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* Reset control plane so that WID is 8-bit plane. */
12762306a36Sopenharmony_cistatic void __tcx_set_control_plane(struct fb_info *info)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct tcx_par *par = info->par;
13062306a36Sopenharmony_ci	u32 __iomem *p, *pend;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (par->lowdepth)
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	p = par->cplane;
13662306a36Sopenharmony_ci	if (p == NULL)
13762306a36Sopenharmony_ci		return;
13862306a36Sopenharmony_ci	for (pend = p + info->fix.smem_len; p < pend; p++) {
13962306a36Sopenharmony_ci		u32 tmp = sbus_readl(p);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		tmp &= 0xffffff;
14262306a36Sopenharmony_ci		sbus_writel(tmp, p);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void tcx_reset(struct fb_info *info)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct tcx_par *par = (struct tcx_par *) info->par;
14962306a36Sopenharmony_ci	unsigned long flags;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	spin_lock_irqsave(&par->lock, flags);
15262306a36Sopenharmony_ci	__tcx_set_control_plane(info);
15362306a36Sopenharmony_ci	spin_unlock_irqrestore(&par->lock, flags);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int tcx_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	tcx_reset(info);
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/**
16362306a36Sopenharmony_ci *      tcx_setcolreg - Optional function. Sets a color register.
16462306a36Sopenharmony_ci *      @regno: boolean, 0 copy local, 1 get_user() function
16562306a36Sopenharmony_ci *      @red: frame buffer colormap structure
16662306a36Sopenharmony_ci *      @green: The green value which can be up to 16 bits wide
16762306a36Sopenharmony_ci *      @blue:  The blue value which can be up to 16 bits wide.
16862306a36Sopenharmony_ci *      @transp: If supported the alpha value which can be up to 16 bits wide.
16962306a36Sopenharmony_ci *      @info: frame buffer info structure
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_cistatic int tcx_setcolreg(unsigned regno,
17262306a36Sopenharmony_ci			 unsigned red, unsigned green, unsigned blue,
17362306a36Sopenharmony_ci			 unsigned transp, struct fb_info *info)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct tcx_par *par = (struct tcx_par *) info->par;
17662306a36Sopenharmony_ci	struct bt_regs __iomem *bt = par->bt;
17762306a36Sopenharmony_ci	unsigned long flags;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (regno >= 256)
18062306a36Sopenharmony_ci		return 1;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	red >>= 8;
18362306a36Sopenharmony_ci	green >>= 8;
18462306a36Sopenharmony_ci	blue >>= 8;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	spin_lock_irqsave(&par->lock, flags);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	sbus_writel(regno << 24, &bt->addr);
18962306a36Sopenharmony_ci	sbus_writel(red << 24, &bt->color_map);
19062306a36Sopenharmony_ci	sbus_writel(green << 24, &bt->color_map);
19162306a36Sopenharmony_ci	sbus_writel(blue << 24, &bt->color_map);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_unlock_irqrestore(&par->lock, flags);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/**
19962306a36Sopenharmony_ci *      tcx_blank - Optional function.  Blanks the display.
20062306a36Sopenharmony_ci *      @blank: the blank mode we want.
20162306a36Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_cistatic int
20462306a36Sopenharmony_citcx_blank(int blank, struct fb_info *info)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct tcx_par *par = (struct tcx_par *) info->par;
20762306a36Sopenharmony_ci	struct tcx_thc __iomem *thc = par->thc;
20862306a36Sopenharmony_ci	unsigned long flags;
20962306a36Sopenharmony_ci	u32 val;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	spin_lock_irqsave(&par->lock, flags);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	val = sbus_readl(&thc->thc_misc);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	switch (blank) {
21662306a36Sopenharmony_ci	case FB_BLANK_UNBLANK: /* Unblanking */
21762306a36Sopenharmony_ci		val &= ~(TCX_THC_MISC_VSYNC_DIS |
21862306a36Sopenharmony_ci			 TCX_THC_MISC_HSYNC_DIS);
21962306a36Sopenharmony_ci		val |= TCX_THC_MISC_VIDEO;
22062306a36Sopenharmony_ci		par->flags &= ~TCX_FLAG_BLANKED;
22162306a36Sopenharmony_ci		break;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	case FB_BLANK_NORMAL: /* Normal blanking */
22462306a36Sopenharmony_ci		val &= ~TCX_THC_MISC_VIDEO;
22562306a36Sopenharmony_ci		par->flags |= TCX_FLAG_BLANKED;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
22962306a36Sopenharmony_ci		val |= TCX_THC_MISC_VSYNC_DIS;
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
23262306a36Sopenharmony_ci		val |= TCX_THC_MISC_HSYNC_DIS;
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN: /* Poweroff */
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	sbus_writel(val, &thc->thc_misc);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	spin_unlock_irqrestore(&par->lock, flags);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic struct sbus_mmap_map __tcx_mmap_map[TCX_MMAP_ENTRIES] = {
24762306a36Sopenharmony_ci	{
24862306a36Sopenharmony_ci		.voff	= TCX_RAM8BIT,
24962306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(1)
25062306a36Sopenharmony_ci	},
25162306a36Sopenharmony_ci	{
25262306a36Sopenharmony_ci		.voff	= TCX_RAM24BIT,
25362306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(4)
25462306a36Sopenharmony_ci	},
25562306a36Sopenharmony_ci	{
25662306a36Sopenharmony_ci		.voff	= TCX_UNK3,
25762306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(8)
25862306a36Sopenharmony_ci	},
25962306a36Sopenharmony_ci	{
26062306a36Sopenharmony_ci		.voff	= TCX_UNK4,
26162306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(8)
26262306a36Sopenharmony_ci	},
26362306a36Sopenharmony_ci	{
26462306a36Sopenharmony_ci		.voff	= TCX_CONTROLPLANE,
26562306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(4)
26662306a36Sopenharmony_ci	},
26762306a36Sopenharmony_ci	{
26862306a36Sopenharmony_ci		.voff	= TCX_UNK6,
26962306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(8)
27062306a36Sopenharmony_ci	},
27162306a36Sopenharmony_ci	{
27262306a36Sopenharmony_ci		.voff	= TCX_UNK7,
27362306a36Sopenharmony_ci		.size	= SBUS_MMAP_FBSIZE(8)
27462306a36Sopenharmony_ci	},
27562306a36Sopenharmony_ci	{
27662306a36Sopenharmony_ci		.voff	= TCX_TEC,
27762306a36Sopenharmony_ci		.size	= PAGE_SIZE
27862306a36Sopenharmony_ci	},
27962306a36Sopenharmony_ci	{
28062306a36Sopenharmony_ci		.voff	= TCX_BTREGS,
28162306a36Sopenharmony_ci		.size	= PAGE_SIZE
28262306a36Sopenharmony_ci	},
28362306a36Sopenharmony_ci	{
28462306a36Sopenharmony_ci		.voff	= TCX_THC,
28562306a36Sopenharmony_ci		.size	= PAGE_SIZE
28662306a36Sopenharmony_ci	},
28762306a36Sopenharmony_ci	{
28862306a36Sopenharmony_ci		.voff	= TCX_DHC,
28962306a36Sopenharmony_ci		.size	= PAGE_SIZE
29062306a36Sopenharmony_ci	},
29162306a36Sopenharmony_ci	{
29262306a36Sopenharmony_ci		.voff	= TCX_ALT,
29362306a36Sopenharmony_ci		.size	= PAGE_SIZE
29462306a36Sopenharmony_ci	},
29562306a36Sopenharmony_ci	{
29662306a36Sopenharmony_ci		.voff	= TCX_UNK2,
29762306a36Sopenharmony_ci		.size	= 0x20000
29862306a36Sopenharmony_ci	},
29962306a36Sopenharmony_ci	{ .size = 0 }
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int tcx_mmap(struct fb_info *info, struct vm_area_struct *vma)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct tcx_par *par = (struct tcx_par *)info->par;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return sbusfb_mmap_helper(par->mmap_map,
30762306a36Sopenharmony_ci				  info->fix.smem_start, info->fix.smem_len,
30862306a36Sopenharmony_ci				  par->which_io, vma);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int tcx_ioctl(struct fb_info *info, unsigned int cmd,
31262306a36Sopenharmony_ci		     unsigned long arg)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct tcx_par *par = (struct tcx_par *) info->par;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return sbusfb_ioctl_helper(cmd, arg, info,
31762306a36Sopenharmony_ci				   FBTYPE_TCXCOLOR,
31862306a36Sopenharmony_ci				   (par->lowdepth ? 8 : 24),
31962306a36Sopenharmony_ci				   info->fix.smem_len);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/*
32362306a36Sopenharmony_ci *  Initialisation
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic void
32762306a36Sopenharmony_citcx_init_fix(struct fb_info *info, int linebytes)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct tcx_par *par = (struct tcx_par *)info->par;
33062306a36Sopenharmony_ci	const char *tcx_name;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (par->lowdepth)
33362306a36Sopenharmony_ci		tcx_name = "TCX8";
33462306a36Sopenharmony_ci	else
33562306a36Sopenharmony_ci		tcx_name = "TCX24";
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	strscpy(info->fix.id, tcx_name, sizeof(info->fix.id));
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
34062306a36Sopenharmony_ci	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	info->fix.line_length = linebytes;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	info->fix.accel = FB_ACCEL_SUN_TCX;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void tcx_unmap_regs(struct platform_device *op, struct fb_info *info,
34862306a36Sopenharmony_ci			   struct tcx_par *par)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	if (par->tec)
35162306a36Sopenharmony_ci		of_iounmap(&op->resource[7],
35262306a36Sopenharmony_ci			   par->tec, sizeof(struct tcx_tec));
35362306a36Sopenharmony_ci	if (par->thc)
35462306a36Sopenharmony_ci		of_iounmap(&op->resource[9],
35562306a36Sopenharmony_ci			   par->thc, sizeof(struct tcx_thc));
35662306a36Sopenharmony_ci	if (par->bt)
35762306a36Sopenharmony_ci		of_iounmap(&op->resource[8],
35862306a36Sopenharmony_ci			   par->bt, sizeof(struct bt_regs));
35962306a36Sopenharmony_ci	if (par->cplane)
36062306a36Sopenharmony_ci		of_iounmap(&op->resource[4],
36162306a36Sopenharmony_ci			   par->cplane, info->fix.smem_len * sizeof(u32));
36262306a36Sopenharmony_ci	if (info->screen_base)
36362306a36Sopenharmony_ci		of_iounmap(&op->resource[0],
36462306a36Sopenharmony_ci			   info->screen_base, info->fix.smem_len);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int tcx_probe(struct platform_device *op)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct device_node *dp = op->dev.of_node;
37062306a36Sopenharmony_ci	struct fb_info *info;
37162306a36Sopenharmony_ci	struct tcx_par *par;
37262306a36Sopenharmony_ci	int linebytes, i, err;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct tcx_par), &op->dev);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	err = -ENOMEM;
37762306a36Sopenharmony_ci	if (!info)
37862306a36Sopenharmony_ci		goto out_err;
37962306a36Sopenharmony_ci	par = info->par;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	spin_lock_init(&par->lock);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	par->lowdepth = of_property_read_bool(dp, "tcx-8-bit");
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	sbusfb_fill_var(&info->var, dp, 8);
38662306a36Sopenharmony_ci	info->var.red.length = 8;
38762306a36Sopenharmony_ci	info->var.green.length = 8;
38862306a36Sopenharmony_ci	info->var.blue.length = 8;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	linebytes = of_getintprop_default(dp, "linebytes",
39162306a36Sopenharmony_ci					  info->var.xres);
39262306a36Sopenharmony_ci	info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	par->tec = of_ioremap(&op->resource[7], 0,
39562306a36Sopenharmony_ci				  sizeof(struct tcx_tec), "tcx tec");
39662306a36Sopenharmony_ci	par->thc = of_ioremap(&op->resource[9], 0,
39762306a36Sopenharmony_ci				  sizeof(struct tcx_thc), "tcx thc");
39862306a36Sopenharmony_ci	par->bt = of_ioremap(&op->resource[8], 0,
39962306a36Sopenharmony_ci				 sizeof(struct bt_regs), "tcx dac");
40062306a36Sopenharmony_ci	info->screen_base = of_ioremap(&op->resource[0], 0,
40162306a36Sopenharmony_ci					   info->fix.smem_len, "tcx ram");
40262306a36Sopenharmony_ci	if (!par->tec || !par->thc ||
40362306a36Sopenharmony_ci	    !par->bt || !info->screen_base)
40462306a36Sopenharmony_ci		goto out_unmap_regs;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	memcpy(&par->mmap_map, &__tcx_mmap_map, sizeof(par->mmap_map));
40762306a36Sopenharmony_ci	if (!par->lowdepth) {
40862306a36Sopenharmony_ci		par->cplane = of_ioremap(&op->resource[4], 0,
40962306a36Sopenharmony_ci					     info->fix.smem_len * sizeof(u32),
41062306a36Sopenharmony_ci					     "tcx cplane");
41162306a36Sopenharmony_ci		if (!par->cplane)
41262306a36Sopenharmony_ci			goto out_unmap_regs;
41362306a36Sopenharmony_ci	} else {
41462306a36Sopenharmony_ci		par->mmap_map[1].size = SBUS_MMAP_EMPTY;
41562306a36Sopenharmony_ci		par->mmap_map[4].size = SBUS_MMAP_EMPTY;
41662306a36Sopenharmony_ci		par->mmap_map[5].size = SBUS_MMAP_EMPTY;
41762306a36Sopenharmony_ci		par->mmap_map[6].size = SBUS_MMAP_EMPTY;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	info->fix.smem_start = op->resource[0].start;
42162306a36Sopenharmony_ci	par->which_io = op->resource[0].flags & IORESOURCE_BITS;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	for (i = 0; i < TCX_MMAP_ENTRIES; i++) {
42462306a36Sopenharmony_ci		int j;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		switch (i) {
42762306a36Sopenharmony_ci		case 10:
42862306a36Sopenharmony_ci			j = 12;
42962306a36Sopenharmony_ci			break;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		case 11: case 12:
43262306a36Sopenharmony_ci			j = i - 1;
43362306a36Sopenharmony_ci			break;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		default:
43662306a36Sopenharmony_ci			j = i;
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci		par->mmap_map[i].poff = op->resource[j].start;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	info->fbops = &tcx_ops;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* Initialize brooktree DAC. */
44562306a36Sopenharmony_ci	sbus_writel(0x04 << 24, &par->bt->addr);         /* color planes */
44662306a36Sopenharmony_ci	sbus_writel(0xff << 24, &par->bt->control);
44762306a36Sopenharmony_ci	sbus_writel(0x05 << 24, &par->bt->addr);
44862306a36Sopenharmony_ci	sbus_writel(0x00 << 24, &par->bt->control);
44962306a36Sopenharmony_ci	sbus_writel(0x06 << 24, &par->bt->addr);         /* overlay plane */
45062306a36Sopenharmony_ci	sbus_writel(0x73 << 24, &par->bt->control);
45162306a36Sopenharmony_ci	sbus_writel(0x07 << 24, &par->bt->addr);
45262306a36Sopenharmony_ci	sbus_writel(0x00 << 24, &par->bt->control);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	tcx_reset(info);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	tcx_blank(FB_BLANK_UNBLANK, info);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0))
45962306a36Sopenharmony_ci		goto out_unmap_regs;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	fb_set_cmap(&info->cmap, info);
46262306a36Sopenharmony_ci	tcx_init_fix(info, linebytes);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	err = register_framebuffer(info);
46562306a36Sopenharmony_ci	if (err < 0)
46662306a36Sopenharmony_ci		goto out_dealloc_cmap;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	dev_set_drvdata(&op->dev, info);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	printk(KERN_INFO "%pOF: TCX at %lx:%lx, %s\n",
47162306a36Sopenharmony_ci	       dp,
47262306a36Sopenharmony_ci	       par->which_io,
47362306a36Sopenharmony_ci	       info->fix.smem_start,
47462306a36Sopenharmony_ci	       par->lowdepth ? "8-bit only" : "24-bit depth");
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ciout_dealloc_cmap:
47962306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ciout_unmap_regs:
48262306a36Sopenharmony_ci	tcx_unmap_regs(op, info, par);
48362306a36Sopenharmony_ci	framebuffer_release(info);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciout_err:
48662306a36Sopenharmony_ci	return err;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void tcx_remove(struct platform_device *op)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(&op->dev);
49262306a36Sopenharmony_ci	struct tcx_par *par = info->par;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	unregister_framebuffer(info);
49562306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	tcx_unmap_regs(op, info, par);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	framebuffer_release(info);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic const struct of_device_id tcx_match[] = {
50362306a36Sopenharmony_ci	{
50462306a36Sopenharmony_ci		.name = "SUNW,tcx",
50562306a36Sopenharmony_ci	},
50662306a36Sopenharmony_ci	{},
50762306a36Sopenharmony_ci};
50862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tcx_match);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic struct platform_driver tcx_driver = {
51162306a36Sopenharmony_ci	.driver = {
51262306a36Sopenharmony_ci		.name = "tcx",
51362306a36Sopenharmony_ci		.of_match_table = tcx_match,
51462306a36Sopenharmony_ci	},
51562306a36Sopenharmony_ci	.probe		= tcx_probe,
51662306a36Sopenharmony_ci	.remove_new	= tcx_remove,
51762306a36Sopenharmony_ci};
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int __init tcx_init(void)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	if (fb_get_options("tcxfb", NULL))
52262306a36Sopenharmony_ci		return -ENODEV;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return platform_driver_register(&tcx_driver);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic void __exit tcx_exit(void)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	platform_driver_unregister(&tcx_driver);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cimodule_init(tcx_init);
53362306a36Sopenharmony_cimodule_exit(tcx_exit);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ciMODULE_DESCRIPTION("framebuffer driver for TCX chipsets");
53662306a36Sopenharmony_ciMODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
53762306a36Sopenharmony_ciMODULE_VERSION("2.0");
53862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
539