18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Frame buffer driver for the Carmine GPU.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The driver configures the GPU as follows
68c2ecf20Sopenharmony_ci * - FB0 is display 0 with unique memory area
78c2ecf20Sopenharmony_ci * - FB1 is display 1 with unique memory area
88c2ecf20Sopenharmony_ci * - both display use 32 bit colors
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/fb.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "carminefb.h"
198c2ecf20Sopenharmony_ci#include "carminefb_regs.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
228c2ecf20Sopenharmony_ci#error  "The endianness of the target host has not been defined."
238c2ecf20Sopenharmony_ci#endif
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * The initial video mode can be supplied via two different ways:
278c2ecf20Sopenharmony_ci * - as a string that is passed to fb_find_mode() (module option fb_mode_str)
288c2ecf20Sopenharmony_ci * - as an integer that picks the video mode from carmine_modedb[] (module
298c2ecf20Sopenharmony_ci *   option fb_mode)
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * If nothing is used than the initial video mode will be the
328c2ecf20Sopenharmony_ci * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[].
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci#define CARMINEFB_DEFAULT_VIDEO_MODE	1
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
378c2ecf20Sopenharmony_cimodule_param(fb_mode, uint, 0444);
388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_mode, "Initial video mode as integer.");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic char *fb_mode_str;
418c2ecf20Sopenharmony_cimodule_param(fb_mode_str, charp, 0444);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * Carminefb displays:
468c2ecf20Sopenharmony_ci * 0b000 None
478c2ecf20Sopenharmony_ci * 0b001 Display 0
488c2ecf20Sopenharmony_ci * 0b010 Display 1
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistatic int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1;
518c2ecf20Sopenharmony_cimodule_param(fb_displays, int, 0444);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct carmine_hw {
558c2ecf20Sopenharmony_ci	void __iomem *v_regs;
568c2ecf20Sopenharmony_ci	void __iomem *screen_mem;
578c2ecf20Sopenharmony_ci	struct fb_info *fb[MAX_DISPLAY];
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct carmine_resolution {
618c2ecf20Sopenharmony_ci	u32 htp;
628c2ecf20Sopenharmony_ci	u32 hsp;
638c2ecf20Sopenharmony_ci	u32 hsw;
648c2ecf20Sopenharmony_ci	u32 hdp;
658c2ecf20Sopenharmony_ci	u32 vtr;
668c2ecf20Sopenharmony_ci	u32 vsp;
678c2ecf20Sopenharmony_ci	u32 vsw;
688c2ecf20Sopenharmony_ci	u32 vdp;
698c2ecf20Sopenharmony_ci	u32 disp_mode;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct carmine_fb {
738c2ecf20Sopenharmony_ci	void __iomem *display_reg;
748c2ecf20Sopenharmony_ci	void __iomem *screen_base;
758c2ecf20Sopenharmony_ci	u32 smem_offset;
768c2ecf20Sopenharmony_ci	u32 cur_mode;
778c2ecf20Sopenharmony_ci	u32 new_mode;
788c2ecf20Sopenharmony_ci	struct carmine_resolution *res;
798c2ecf20Sopenharmony_ci	u32 pseudo_palette[16];
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo carminefb_fix = {
838c2ecf20Sopenharmony_ci	.id = "Carmine",
848c2ecf20Sopenharmony_ci	.type = FB_TYPE_PACKED_PIXELS,
858c2ecf20Sopenharmony_ci	.visual = FB_VISUAL_TRUECOLOR,
868c2ecf20Sopenharmony_ci	.accel = FB_ACCEL_NONE,
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic const struct fb_videomode carmine_modedb[] = {
908c2ecf20Sopenharmony_ci	{
918c2ecf20Sopenharmony_ci		.name		= "640x480",
928c2ecf20Sopenharmony_ci		.xres		= 640,
938c2ecf20Sopenharmony_ci		.yres		= 480,
948c2ecf20Sopenharmony_ci	}, {
958c2ecf20Sopenharmony_ci		.name		= "800x600",
968c2ecf20Sopenharmony_ci		.xres		= 800,
978c2ecf20Sopenharmony_ci		.yres		= 600,
988c2ecf20Sopenharmony_ci	},
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic struct carmine_resolution car_modes[] = {
1028c2ecf20Sopenharmony_ci	{
1038c2ecf20Sopenharmony_ci		/* 640x480 */
1048c2ecf20Sopenharmony_ci		.htp = 800,
1058c2ecf20Sopenharmony_ci		.hsp = 672,
1068c2ecf20Sopenharmony_ci		.hsw = 96,
1078c2ecf20Sopenharmony_ci		.hdp = 640,
1088c2ecf20Sopenharmony_ci		.vtr = 525,
1098c2ecf20Sopenharmony_ci		.vsp = 490,
1108c2ecf20Sopenharmony_ci		.vsw = 2,
1118c2ecf20Sopenharmony_ci		.vdp = 480,
1128c2ecf20Sopenharmony_ci		.disp_mode = 0x1400,
1138c2ecf20Sopenharmony_ci	},
1148c2ecf20Sopenharmony_ci	{
1158c2ecf20Sopenharmony_ci		/* 800x600 */
1168c2ecf20Sopenharmony_ci		.htp = 1060,
1178c2ecf20Sopenharmony_ci		.hsp = 864,
1188c2ecf20Sopenharmony_ci		.hsw = 72,
1198c2ecf20Sopenharmony_ci		.hdp = 800,
1208c2ecf20Sopenharmony_ci		.vtr = 628,
1218c2ecf20Sopenharmony_ci		.vsp = 601,
1228c2ecf20Sopenharmony_ci		.vsw = 2,
1238c2ecf20Sopenharmony_ci		.vdp = 600,
1248c2ecf20Sopenharmony_ci		.disp_mode = 0x0d00,
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci};
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int carmine_find_mode(const struct fb_var_screeninfo *var)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int i;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(car_modes); i++)
1338c2ecf20Sopenharmony_ci		if (car_modes[i].hdp == var->xres &&
1348c2ecf20Sopenharmony_ci		    car_modes[i].vdp == var->yres)
1358c2ecf20Sopenharmony_ci			return i;
1368c2ecf20Sopenharmony_ci	return -EINVAL;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void c_set_disp_reg(const struct carmine_fb *par,
1408c2ecf20Sopenharmony_ci		u32 offset, u32 val)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	writel(val, par->display_reg + offset);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic u32 c_get_disp_reg(const struct carmine_fb *par,
1468c2ecf20Sopenharmony_ci		u32 offset)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return readl(par->display_reg + offset);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void c_set_hw_reg(const struct carmine_hw *hw,
1528c2ecf20Sopenharmony_ci		u32 offset, u32 val)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	writel(val, hw->v_regs + offset);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic u32 c_get_hw_reg(const struct carmine_hw *hw,
1588c2ecf20Sopenharmony_ci		u32 offset)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return readl(hw->v_regs + offset);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int carmine_setcolreg(unsigned regno, unsigned red, unsigned green,
1648c2ecf20Sopenharmony_ci		unsigned blue, unsigned transp, struct fb_info *info)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	if (regno >= 16)
1678c2ecf20Sopenharmony_ci		return 1;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	red >>= 8;
1708c2ecf20Sopenharmony_ci	green >>= 8;
1718c2ecf20Sopenharmony_ci	blue >>= 8;
1728c2ecf20Sopenharmony_ci	transp >>= 8;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	((__be32 *)info->pseudo_palette)[regno] = cpu_to_be32(transp << 24 |
1758c2ecf20Sopenharmony_ci		red << 0 | green << 8 | blue << 16);
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int carmine_check_var(struct fb_var_screeninfo *var,
1808c2ecf20Sopenharmony_ci		struct fb_info *info)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	int ret;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	ret = carmine_find_mode(var);
1858c2ecf20Sopenharmony_ci	if (ret < 0)
1868c2ecf20Sopenharmony_ci		return ret;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (var->grayscale || var->rotate || var->nonstd)
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	var->xres_virtual = var->xres;
1928c2ecf20Sopenharmony_ci	var->yres_virtual = var->yres;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	var->bits_per_pixel = 32;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
1978c2ecf20Sopenharmony_ci	var->transp.offset = 24;
1988c2ecf20Sopenharmony_ci	var->red.offset = 0;
1998c2ecf20Sopenharmony_ci	var->green.offset = 8;
2008c2ecf20Sopenharmony_ci	var->blue.offset = 16;
2018c2ecf20Sopenharmony_ci#else
2028c2ecf20Sopenharmony_ci	var->transp.offset = 24;
2038c2ecf20Sopenharmony_ci	var->red.offset = 16;
2048c2ecf20Sopenharmony_ci	var->green.offset = 8;
2058c2ecf20Sopenharmony_ci	var->blue.offset = 0;
2068c2ecf20Sopenharmony_ci#endif
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	var->red.length = 8;
2098c2ecf20Sopenharmony_ci	var->green.length = 8;
2108c2ecf20Sopenharmony_ci	var->blue.length = 8;
2118c2ecf20Sopenharmony_ci	var->transp.length = 8;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	var->red.msb_right = 0;
2148c2ecf20Sopenharmony_ci	var->green.msb_right = 0;
2158c2ecf20Sopenharmony_ci	var->blue.msb_right = 0;
2168c2ecf20Sopenharmony_ci	var->transp.msb_right = 0;
2178c2ecf20Sopenharmony_ci	return 0;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void carmine_init_display_param(struct carmine_fb *par)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	u32 width;
2238c2ecf20Sopenharmony_ci	u32 height;
2248c2ecf20Sopenharmony_ci	u32 param;
2258c2ecf20Sopenharmony_ci	u32 window_size;
2268c2ecf20Sopenharmony_ci	u32 soffset = par->smem_offset;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0);
2298c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0);
2308c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE,
2318c2ecf20Sopenharmony_ci			CARMINE_CURSOR0_PRIORITY_MASK |
2328c2ecf20Sopenharmony_ci			CARMINE_CURSOR1_PRIORITY_MASK |
2338c2ecf20Sopenharmony_ci			CARMINE_CURSOR_CUTZ_MASK);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* Set default cursor position */
2368c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0);
2378c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Set default display mode */
2408c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE |
2418c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2428c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE,
2438c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2448c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE |
2458c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2468c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE |
2478c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2488c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE |
2498c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2508c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE |
2518c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2528c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE |
2538c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2548c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE |
2558c2ecf20Sopenharmony_ci			CARMINE_EXT_CMODE_DIRECT24_RGBA);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Set default frame size to layer mode register */
2588c2ecf20Sopenharmony_ci	width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT;
2598c2ecf20Sopenharmony_ci	width = width << CARMINE_DISP_WIDTH_SHIFT;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	height = par->res->vdp - 1;
2628c2ecf20Sopenharmony_ci	param = width | height;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param);
2658c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width);
2668c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param);
2678c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param);
2688c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param);
2698c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param);
2708c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param);
2718c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Set default pos and size */
2748c2ecf20Sopenharmony_ci	window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT;
2758c2ecf20Sopenharmony_ci	window_size |= par->res->hdp;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0);
2788c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size);
2798c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0);
2808c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size);
2818c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0);
2828c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size);
2838c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0);
2848c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size);
2858c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0);
2868c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size);
2878c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0);
2888c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size);
2898c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0);
2908c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size);
2918c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0);
2928c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* Set default origin address */
2958c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset);
2968c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset);
2978c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset);
2988c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset);
2998c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset);
3008c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset);
3018c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset);
3028c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Set default display address */
3058c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset);
3068c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset);
3078c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset);
3088c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset);
3098c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset);
3108c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset);
3118c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* Set default display position */
3148c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0);
3158c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0);
3168c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0);
3178c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0);
3188c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0);
3198c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0);
3208c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* Set default blend mode */
3238c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0);
3248c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0);
3258c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0);
3268c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0);
3278c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0);
3288c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0);
3298c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0);
3308c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* default transparency mode */
3338c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0);
3348c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0);
3358c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0);
3368c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0);
3378c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0);
3388c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0);
3398c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0);
3408c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* Set default read skip parameter */
3438c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0);
3448c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0);
3458c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0);
3468c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0);
3478c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0);
3488c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0);
3498c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0);
3528c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0);
3538c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0);
3548c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0);
3558c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0);
3568c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0);
3578c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0);
3608c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0);
3618c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0);
3628c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0);
3638c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0);
3648c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0);
3658c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void set_display_parameters(struct carmine_fb *par)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	u32 mode;
3718c2ecf20Sopenharmony_ci	u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/*
3748c2ecf20Sopenharmony_ci	 * display timing. Parameters are decreased by one because hardware
3758c2ecf20Sopenharmony_ci	 * spec is 0 to (n - 1)
3768c2ecf20Sopenharmony_ci	 * */
3778c2ecf20Sopenharmony_ci	hdp = par->res->hdp - 1;
3788c2ecf20Sopenharmony_ci	vdp = par->res->vdp - 1;
3798c2ecf20Sopenharmony_ci	htp = par->res->htp - 1;
3808c2ecf20Sopenharmony_ci	hsp = par->res->hsp - 1;
3818c2ecf20Sopenharmony_ci	hsw = par->res->hsw - 1;
3828c2ecf20Sopenharmony_ci	vtr = par->res->vtr - 1;
3838c2ecf20Sopenharmony_ci	vsp = par->res->vsp - 1;
3848c2ecf20Sopenharmony_ci	vsw = par->res->vsw - 1;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL,
3878c2ecf20Sopenharmony_ci			htp << CARMINE_DISP_HTP_SHIFT);
3888c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD,
3898c2ecf20Sopenharmony_ci			(hdp << CARMINE_DISP_HDB_SHIFT)	| hdp);
3908c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS,
3918c2ecf20Sopenharmony_ci			(vsw << CARMINE_DISP_VSW_SHIFT) |
3928c2ecf20Sopenharmony_ci			(hsw << CARMINE_DISP_HSW_SHIFT) |
3938c2ecf20Sopenharmony_ci			(hsp));
3948c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL,
3958c2ecf20Sopenharmony_ci			vtr << CARMINE_DISP_VTR_SHIFT);
3968c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS,
3978c2ecf20Sopenharmony_ci			(vdp << CARMINE_DISP_VDP_SHIFT) | vsp);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* clock */
4008c2ecf20Sopenharmony_ci	mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1);
4018c2ecf20Sopenharmony_ci	mode = (mode & ~CARMINE_DISP_DCM_MASK) |
4028c2ecf20Sopenharmony_ci		(par->res->disp_mode & CARMINE_DISP_DCM_MASK);
4038c2ecf20Sopenharmony_ci	/* enable video output and layer 0 */
4048c2ecf20Sopenharmony_ci	mode |= CARMINE_DEN | CARMINE_L0E;
4058c2ecf20Sopenharmony_ci	c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int carmine_set_par(struct fb_info *info)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct carmine_fb *par = info->par;
4118c2ecf20Sopenharmony_ci	int ret;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ret = carmine_find_mode(&info->var);
4148c2ecf20Sopenharmony_ci	if (ret < 0)
4158c2ecf20Sopenharmony_ci		return ret;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	par->new_mode = ret;
4188c2ecf20Sopenharmony_ci	if (par->cur_mode != par->new_mode) {
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		par->cur_mode = par->new_mode;
4218c2ecf20Sopenharmony_ci		par->res = &car_modes[par->new_mode];
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		carmine_init_display_param(par);
4248c2ecf20Sopenharmony_ci		set_display_parameters(par);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int init_hardware(struct carmine_hw *hw)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	u32 flags;
4348c2ecf20Sopenharmony_ci	u32 loops;
4358c2ecf20Sopenharmony_ci	u32 ret;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* Initialize Carmine */
4388c2ecf20Sopenharmony_ci	/* Sets internal clock */
4398c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE,
4408c2ecf20Sopenharmony_ci			CARMINE_DFLT_IP_CLOCK_ENABLE);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* Video signal output is turned off */
4438c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);
4448c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* Software reset */
4478c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1);
4488c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* I/O mode settings */
4518c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 |
4528c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_IO_CONT0;
4538c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0,
4548c2ecf20Sopenharmony_ci			flags);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* DRAM initial sequence */
4578c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD;
4588c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD,
4598c2ecf20Sopenharmony_ci			flags);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 |
4628c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_EMODE;
4638c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE,
4648c2ecf20Sopenharmony_ci			flags);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 |
4678c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_SET_TIME2;
4688c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2,
4698c2ecf20Sopenharmony_ci			flags);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 |
4728c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_FIFO_DEPTH;
4738c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1;
4768c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1,
4778c2ecf20Sopenharmony_ci			flags);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |
4808c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_STATES;
4818c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,
4828c2ecf20Sopenharmony_ci			flags);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* Executes DLL reset */
4858c2ecf20Sopenharmony_ci	if (CARMINE_DCTL_DLL_RESET) {
4868c2ecf20Sopenharmony_ci		for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) {
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci			ret = c_get_hw_reg(hw, CARMINE_DCTL_REG +
4898c2ecf20Sopenharmony_ci					CARMINE_DCTL_REG_RSV0_STATES);
4908c2ecf20Sopenharmony_ci			ret &= CARMINE_DCTL_REG_STATES_MASK;
4918c2ecf20Sopenharmony_ci			if (!ret)
4928c2ecf20Sopenharmony_ci				break;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci			mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL);
4958c2ecf20Sopenharmony_ci		}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) {
4988c2ecf20Sopenharmony_ci			printk(KERN_ERR "DRAM init failed\n");
4998c2ecf20Sopenharmony_ci			return -EIO;
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 |
5048c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_ADD;
5058c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |
5088c2ecf20Sopenharmony_ci		CARMINE_DFLT_IP_DCTL_STATES_AFT_RST;
5098c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,
5108c2ecf20Sopenharmony_ci			flags);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	/* Initialize the write back register */
5138c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM,
5148c2ecf20Sopenharmony_ci			CARMINE_WB_REG_WBM_DEFAULT);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* Initialize the Kottos registers */
5178c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0);
5188c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* Set DC offsets */
5218c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0);
5228c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0);
5238c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0);
5248c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0);
5258c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0);
5268c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0);
5278c2ecf20Sopenharmony_ci	return 0;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic const struct fb_ops carminefb_ops = {
5318c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5328c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
5338c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
5348c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	.fb_check_var	= carmine_check_var,
5378c2ecf20Sopenharmony_ci	.fb_set_par	= carmine_set_par,
5388c2ecf20Sopenharmony_ci	.fb_setcolreg	= carmine_setcolreg,
5398c2ecf20Sopenharmony_ci};
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base,
5428c2ecf20Sopenharmony_ci			    int smem_offset, struct device *device,
5438c2ecf20Sopenharmony_ci			    struct fb_info **rinfo)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	int ret;
5468c2ecf20Sopenharmony_ci	struct fb_info *info;
5478c2ecf20Sopenharmony_ci	struct carmine_fb *par;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof *par, device);
5508c2ecf20Sopenharmony_ci	if (!info)
5518c2ecf20Sopenharmony_ci		return -ENOMEM;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	par = info->par;
5548c2ecf20Sopenharmony_ci	par->display_reg = regs;
5558c2ecf20Sopenharmony_ci	par->smem_offset = smem_offset;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	info->screen_base = smem_base + smem_offset;
5588c2ecf20Sopenharmony_ci	info->screen_size = CARMINE_DISPLAY_MEM;
5598c2ecf20Sopenharmony_ci	info->fbops = &carminefb_ops;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	info->fix = carminefb_fix;
5628c2ecf20Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
5638c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	ret = fb_alloc_cmap(&info->cmap, 256, 1);
5668c2ecf20Sopenharmony_ci	if (ret < 0)
5678c2ecf20Sopenharmony_ci		goto err_free_fb;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (fb_mode >= ARRAY_SIZE(carmine_modedb))
5708c2ecf20Sopenharmony_ci		fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	par->cur_mode = par->new_mode = ~0;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb,
5758c2ecf20Sopenharmony_ci			ARRAY_SIZE(carmine_modedb),
5768c2ecf20Sopenharmony_ci			&carmine_modedb[fb_mode], 32);
5778c2ecf20Sopenharmony_ci	if (!ret || ret == 4) {
5788c2ecf20Sopenharmony_ci		ret = -EINVAL;
5798c2ecf20Sopenharmony_ci		goto err_dealloc_cmap;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb),
5838c2ecf20Sopenharmony_ci			&info->modelist);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	ret = register_framebuffer(info);
5868c2ecf20Sopenharmony_ci	if (ret < 0)
5878c2ecf20Sopenharmony_ci		goto err_dealloc_cmap;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	fb_info(info, "%s frame buffer device\n", info->fix.id);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	*rinfo = info;
5928c2ecf20Sopenharmony_ci	return 0;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cierr_dealloc_cmap:
5958c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
5968c2ecf20Sopenharmony_cierr_free_fb:
5978c2ecf20Sopenharmony_ci	framebuffer_release(info);
5988c2ecf20Sopenharmony_ci	return ret;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic void cleanup_fb_device(struct fb_info *info)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	if (info) {
6048c2ecf20Sopenharmony_ci		unregister_framebuffer(info);
6058c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
6068c2ecf20Sopenharmony_ci		framebuffer_release(info);
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct carmine_hw *hw;
6138c2ecf20Sopenharmony_ci	struct device *device = &dev->dev;
6148c2ecf20Sopenharmony_ci	struct fb_info *info;
6158c2ecf20Sopenharmony_ci	int ret;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	ret = pci_enable_device(dev);
6188c2ecf20Sopenharmony_ci	if (ret)
6198c2ecf20Sopenharmony_ci		return ret;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	ret = -ENOMEM;
6228c2ecf20Sopenharmony_ci	hw = kzalloc(sizeof *hw, GFP_KERNEL);
6238c2ecf20Sopenharmony_ci	if (!hw)
6248c2ecf20Sopenharmony_ci		goto err_enable_pci;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR);
6278c2ecf20Sopenharmony_ci	carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (!request_mem_region(carminefb_fix.mmio_start,
6308c2ecf20Sopenharmony_ci				carminefb_fix.mmio_len,
6318c2ecf20Sopenharmony_ci				"carminefb regbase")) {
6328c2ecf20Sopenharmony_ci		printk(KERN_ERR "carminefb: Can't reserve regbase.\n");
6338c2ecf20Sopenharmony_ci		ret = -EBUSY;
6348c2ecf20Sopenharmony_ci		goto err_free_hw;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci	hw->v_regs = ioremap(carminefb_fix.mmio_start,
6378c2ecf20Sopenharmony_ci			carminefb_fix.mmio_len);
6388c2ecf20Sopenharmony_ci	if (!hw->v_regs) {
6398c2ecf20Sopenharmony_ci		printk(KERN_ERR "carminefb: Can't remap %s register.\n",
6408c2ecf20Sopenharmony_ci				carminefb_fix.id);
6418c2ecf20Sopenharmony_ci		goto err_free_reg_mmio;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR);
6458c2ecf20Sopenharmony_ci	carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/* The memory area tends to be very large (256 MiB). Remap only what
6488c2ecf20Sopenharmony_ci	 * is required for that largest resolution to avoid remaps at run
6498c2ecf20Sopenharmony_ci	 * time
6508c2ecf20Sopenharmony_ci	 */
6518c2ecf20Sopenharmony_ci	if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM)
6528c2ecf20Sopenharmony_ci		carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) {
6558c2ecf20Sopenharmony_ci		printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d "
6568c2ecf20Sopenharmony_ci				"are required.", carminefb_fix.smem_len,
6578c2ecf20Sopenharmony_ci				CARMINE_TOTAL_DIPLAY_MEM);
6588c2ecf20Sopenharmony_ci		goto err_unmap_vregs;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (!request_mem_region(carminefb_fix.smem_start,
6628c2ecf20Sopenharmony_ci				carminefb_fix.smem_len,	"carminefb smem")) {
6638c2ecf20Sopenharmony_ci		printk(KERN_ERR "carminefb: Can't reserve smem.\n");
6648c2ecf20Sopenharmony_ci		goto err_unmap_vregs;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	hw->screen_mem = ioremap(carminefb_fix.smem_start,
6688c2ecf20Sopenharmony_ci			carminefb_fix.smem_len);
6698c2ecf20Sopenharmony_ci	if (!hw->screen_mem) {
6708c2ecf20Sopenharmony_ci		printk(KERN_ERR "carmine: Can't ioremap smem area.\n");
6718c2ecf20Sopenharmony_ci		goto err_reg_smem;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	ret = init_hardware(hw);
6758c2ecf20Sopenharmony_ci	if (ret)
6768c2ecf20Sopenharmony_ci		goto err_unmap_screen;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	info = NULL;
6798c2ecf20Sopenharmony_ci	if (fb_displays & CARMINE_USE_DISPLAY0) {
6808c2ecf20Sopenharmony_ci		ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG,
6818c2ecf20Sopenharmony_ci				hw->screen_mem, CARMINE_DISPLAY_MEM * 0,
6828c2ecf20Sopenharmony_ci				device, &info);
6838c2ecf20Sopenharmony_ci		if (ret)
6848c2ecf20Sopenharmony_ci			goto err_deinit_hw;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	hw->fb[0] = info;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	info = NULL;
6908c2ecf20Sopenharmony_ci	if (fb_displays & CARMINE_USE_DISPLAY1) {
6918c2ecf20Sopenharmony_ci		ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG,
6928c2ecf20Sopenharmony_ci				hw->screen_mem, CARMINE_DISPLAY_MEM * 1,
6938c2ecf20Sopenharmony_ci				device, &info);
6948c2ecf20Sopenharmony_ci		if (ret)
6958c2ecf20Sopenharmony_ci			goto err_cleanup_fb0;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	hw->fb[1] = info;
6998c2ecf20Sopenharmony_ci	info = NULL;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, hw);
7028c2ecf20Sopenharmony_ci	return 0;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cierr_cleanup_fb0:
7058c2ecf20Sopenharmony_ci	cleanup_fb_device(hw->fb[0]);
7068c2ecf20Sopenharmony_cierr_deinit_hw:
7078c2ecf20Sopenharmony_ci	/* disable clock, etc */
7088c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);
7098c2ecf20Sopenharmony_cierr_unmap_screen:
7108c2ecf20Sopenharmony_ci	iounmap(hw->screen_mem);
7118c2ecf20Sopenharmony_cierr_reg_smem:
7128c2ecf20Sopenharmony_ci	release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len);
7138c2ecf20Sopenharmony_cierr_unmap_vregs:
7148c2ecf20Sopenharmony_ci	iounmap(hw->v_regs);
7158c2ecf20Sopenharmony_cierr_free_reg_mmio:
7168c2ecf20Sopenharmony_ci	release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len);
7178c2ecf20Sopenharmony_cierr_free_hw:
7188c2ecf20Sopenharmony_ci	kfree(hw);
7198c2ecf20Sopenharmony_cierr_enable_pci:
7208c2ecf20Sopenharmony_ci	pci_disable_device(dev);
7218c2ecf20Sopenharmony_ci	return ret;
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic void carminefb_remove(struct pci_dev *dev)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct carmine_hw *hw = pci_get_drvdata(dev);
7278c2ecf20Sopenharmony_ci	struct fb_fix_screeninfo fix;
7288c2ecf20Sopenharmony_ci	int i;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/* in case we use only fb1 and not fb1 */
7318c2ecf20Sopenharmony_ci	if (hw->fb[0])
7328c2ecf20Sopenharmony_ci		fix = hw->fb[0]->fix;
7338c2ecf20Sopenharmony_ci	else
7348c2ecf20Sopenharmony_ci		fix = hw->fb[1]->fix;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	/* deactivate display(s) and switch clocks */
7378c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);
7388c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);
7398c2ecf20Sopenharmony_ci	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_DISPLAY; i++)
7428c2ecf20Sopenharmony_ci		cleanup_fb_device(hw->fb[i]);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	iounmap(hw->screen_mem);
7458c2ecf20Sopenharmony_ci	release_mem_region(fix.smem_start, fix.smem_len);
7468c2ecf20Sopenharmony_ci	iounmap(hw->v_regs);
7478c2ecf20Sopenharmony_ci	release_mem_region(fix.mmio_start, fix.mmio_len);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	pci_disable_device(dev);
7508c2ecf20Sopenharmony_ci	kfree(hw);
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf
7548c2ecf20Sopenharmony_cistatic struct pci_device_id carmine_devices[] = {
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)},
7578c2ecf20Sopenharmony_ci	{0, 0, 0, 0, 0, 0, 0}
7588c2ecf20Sopenharmony_ci};
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, carmine_devices);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic struct pci_driver carmine_pci_driver = {
7638c2ecf20Sopenharmony_ci	.name		= "carminefb",
7648c2ecf20Sopenharmony_ci	.id_table	= carmine_devices,
7658c2ecf20Sopenharmony_ci	.probe		= carminefb_probe,
7668c2ecf20Sopenharmony_ci	.remove		= carminefb_remove,
7678c2ecf20Sopenharmony_ci};
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic int __init carminefb_init(void)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	if (!(fb_displays &
7728c2ecf20Sopenharmony_ci		(CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) {
7738c2ecf20Sopenharmony_ci		printk(KERN_ERR "If you disable both displays than you don't "
7748c2ecf20Sopenharmony_ci				"need the driver at all\n");
7758c2ecf20Sopenharmony_ci		return -EINVAL;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci	return pci_register_driver(&carmine_pci_driver);
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_cimodule_init(carminefb_init);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic void __exit carminefb_cleanup(void)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	pci_unregister_driver(&carmine_pci_driver);
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_cimodule_exit(carminefb_cleanup);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>");
7888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices");
7898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
790