brintos

brintos / linux-shallow public Read only

0
0
Text · 22.1 KiB · e56065c Raw
794 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Frame buffer driver for the Carmine GPU.4 *5 * The driver configures the GPU as follows6 * - FB0 is display 0 with unique memory area7 * - FB1 is display 1 with unique memory area8 * - both display use 32 bit colors9 */10#include <linux/aperture.h>11#include <linux/delay.h>12#include <linux/errno.h>13#include <linux/fb.h>14#include <linux/interrupt.h>15#include <linux/pci.h>16#include <linux/slab.h>17#include <linux/module.h>18 19#include "carminefb.h"20#include "carminefb_regs.h"21 22#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)23#error  "The endianness of the target host has not been defined."24#endif25 26/*27 * The initial video mode can be supplied via two different ways:28 * - as a string that is passed to fb_find_mode() (module option fb_mode_str)29 * - as an integer that picks the video mode from carmine_modedb[] (module30 *   option fb_mode)31 *32 * If nothing is used than the initial video mode will be the33 * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[].34 */35#define CARMINEFB_DEFAULT_VIDEO_MODE	136 37static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;38module_param(fb_mode, uint, 0444);39MODULE_PARM_DESC(fb_mode, "Initial video mode as integer.");40 41static char *fb_mode_str;42module_param(fb_mode_str, charp, 0444);43MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");44 45/*46 * Carminefb displays:47 * 0b000 None48 * 0b001 Display 049 * 0b010 Display 150 */51static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1;52module_param(fb_displays, int, 0444);53MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used");54 55struct carmine_hw {56	void __iomem *v_regs;57	void __iomem *screen_mem;58	struct fb_info *fb[MAX_DISPLAY];59};60 61struct carmine_resolution {62	u32 htp;63	u32 hsp;64	u32 hsw;65	u32 hdp;66	u32 vtr;67	u32 vsp;68	u32 vsw;69	u32 vdp;70	u32 disp_mode;71};72 73struct carmine_fb {74	void __iomem *display_reg;75	void __iomem *screen_base;76	u32 smem_offset;77	u32 cur_mode;78	u32 new_mode;79	struct carmine_resolution *res;80	u32 pseudo_palette[16];81};82 83static struct fb_fix_screeninfo carminefb_fix = {84	.id = "Carmine",85	.type = FB_TYPE_PACKED_PIXELS,86	.visual = FB_VISUAL_TRUECOLOR,87	.accel = FB_ACCEL_NONE,88};89 90static const struct fb_videomode carmine_modedb[] = {91	{92		.name		= "640x480",93		.xres		= 640,94		.yres		= 480,95	}, {96		.name		= "800x600",97		.xres		= 800,98		.yres		= 600,99	},100};101 102static struct carmine_resolution car_modes[] = {103	{104		/* 640x480 */105		.htp = 800,106		.hsp = 672,107		.hsw = 96,108		.hdp = 640,109		.vtr = 525,110		.vsp = 490,111		.vsw = 2,112		.vdp = 480,113		.disp_mode = 0x1400,114	},115	{116		/* 800x600 */117		.htp = 1060,118		.hsp = 864,119		.hsw = 72,120		.hdp = 800,121		.vtr = 628,122		.vsp = 601,123		.vsw = 2,124		.vdp = 600,125		.disp_mode = 0x0d00,126	}127};128 129static int carmine_find_mode(const struct fb_var_screeninfo *var)130{131	int i;132 133	for (i = 0; i < ARRAY_SIZE(car_modes); i++)134		if (car_modes[i].hdp == var->xres &&135		    car_modes[i].vdp == var->yres)136			return i;137	return -EINVAL;138}139 140static void c_set_disp_reg(const struct carmine_fb *par,141		u32 offset, u32 val)142{143	writel(val, par->display_reg + offset);144}145 146static u32 c_get_disp_reg(const struct carmine_fb *par,147		u32 offset)148{149	return readl(par->display_reg + offset);150}151 152static void c_set_hw_reg(const struct carmine_hw *hw,153		u32 offset, u32 val)154{155	writel(val, hw->v_regs + offset);156}157 158static u32 c_get_hw_reg(const struct carmine_hw *hw,159		u32 offset)160{161	return readl(hw->v_regs + offset);162}163 164static int carmine_setcolreg(unsigned regno, unsigned red, unsigned green,165		unsigned blue, unsigned transp, struct fb_info *info)166{167	if (regno >= 16)168		return 1;169 170	red >>= 8;171	green >>= 8;172	blue >>= 8;173	transp >>= 8;174 175	((__be32 *)info->pseudo_palette)[regno] = cpu_to_be32(transp << 24 |176		red << 0 | green << 8 | blue << 16);177	return 0;178}179 180static int carmine_check_var(struct fb_var_screeninfo *var,181		struct fb_info *info)182{183	int ret;184 185	ret = carmine_find_mode(var);186	if (ret < 0)187		return ret;188 189	if (var->grayscale || var->rotate || var->nonstd)190		return -EINVAL;191 192	var->xres_virtual = var->xres;193	var->yres_virtual = var->yres;194 195	var->bits_per_pixel = 32;196 197#ifdef __BIG_ENDIAN198	var->transp.offset = 24;199	var->red.offset = 0;200	var->green.offset = 8;201	var->blue.offset = 16;202#else203	var->transp.offset = 24;204	var->red.offset = 16;205	var->green.offset = 8;206	var->blue.offset = 0;207#endif208 209	var->red.length = 8;210	var->green.length = 8;211	var->blue.length = 8;212	var->transp.length = 8;213 214	var->red.msb_right = 0;215	var->green.msb_right = 0;216	var->blue.msb_right = 0;217	var->transp.msb_right = 0;218	return 0;219}220 221static void carmine_init_display_param(struct carmine_fb *par)222{223	u32 width;224	u32 height;225	u32 param;226	u32 window_size;227	u32 soffset = par->smem_offset;228 229	c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0);230	c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0);231	c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE,232			CARMINE_CURSOR0_PRIORITY_MASK |233			CARMINE_CURSOR1_PRIORITY_MASK |234			CARMINE_CURSOR_CUTZ_MASK);235 236	/* Set default cursor position */237	c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0);238	c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0);239 240	/* Set default display mode */241	c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE |242			CARMINE_EXT_CMODE_DIRECT24_RGBA);243	c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE,244			CARMINE_EXT_CMODE_DIRECT24_RGBA);245	c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE |246			CARMINE_EXT_CMODE_DIRECT24_RGBA);247	c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE |248			CARMINE_EXT_CMODE_DIRECT24_RGBA);249	c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE |250			CARMINE_EXT_CMODE_DIRECT24_RGBA);251	c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE |252			CARMINE_EXT_CMODE_DIRECT24_RGBA);253	c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE |254			CARMINE_EXT_CMODE_DIRECT24_RGBA);255	c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE |256			CARMINE_EXT_CMODE_DIRECT24_RGBA);257 258	/* Set default frame size to layer mode register */259	width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT;260	width = width << CARMINE_DISP_WIDTH_SHIFT;261 262	height = par->res->vdp - 1;263	param = width | height;264 265	c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param);266	c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width);267	c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param);268	c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param);269	c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param);270	c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param);271	c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param);272	c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param);273 274	/* Set default pos and size */275	window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT;276	window_size |= par->res->hdp;277 278	c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0);279	c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size);280	c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0);281	c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size);282	c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0);283	c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size);284	c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0);285	c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size);286	c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0);287	c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size);288	c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0);289	c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size);290	c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0);291	c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size);292	c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0);293	c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size);294 295	/* Set default origin address */296	c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset);297	c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset);298	c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset);299	c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset);300	c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset);301	c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset);302	c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset);303	c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset);304 305	/* Set default display address */306	c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset);307	c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset);308	c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset);309	c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset);310	c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset);311	c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset);312	c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset);313 314	/* Set default display position */315	c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0);316	c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0);317	c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0);318	c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0);319	c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0);320	c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0);321	c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0);322 323	/* Set default blend mode */324	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0);325	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0);326	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0);327	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0);328	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0);329	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0);330	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0);331	c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0);332 333	/* default transparency mode */334	c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0);335	c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0);336	c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0);337	c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0);338	c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0);339	c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0);340	c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0);341	c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0);342 343	/* Set default read skip parameter */344	c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0);345	c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0);346	c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0);347	c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0);348	c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0);349	c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0);350	c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0);351 352	c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0);353	c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0);354	c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0);355	c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0);356	c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0);357	c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0);358	c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0);359 360	c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0);361	c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0);362	c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0);363	c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0);364	c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0);365	c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0);366	c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0);367}368 369static void set_display_parameters(struct carmine_fb *par)370{371	u32 mode;372	u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw;373 374	/*375	 * display timing. Parameters are decreased by one because hardware376	 * spec is 0 to (n - 1)377	 * */378	hdp = par->res->hdp - 1;379	vdp = par->res->vdp - 1;380	htp = par->res->htp - 1;381	hsp = par->res->hsp - 1;382	hsw = par->res->hsw - 1;383	vtr = par->res->vtr - 1;384	vsp = par->res->vsp - 1;385	vsw = par->res->vsw - 1;386 387	c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL,388			htp << CARMINE_DISP_HTP_SHIFT);389	c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD,390			(hdp << CARMINE_DISP_HDB_SHIFT)	| hdp);391	c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS,392			(vsw << CARMINE_DISP_VSW_SHIFT) |393			(hsw << CARMINE_DISP_HSW_SHIFT) |394			(hsp));395	c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL,396			vtr << CARMINE_DISP_VTR_SHIFT);397	c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS,398			(vdp << CARMINE_DISP_VDP_SHIFT) | vsp);399 400	/* clock */401	mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1);402	mode = (mode & ~CARMINE_DISP_DCM_MASK) |403		(par->res->disp_mode & CARMINE_DISP_DCM_MASK);404	/* enable video output and layer 0 */405	mode |= CARMINE_DEN | CARMINE_L0E;406	c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode);407}408 409static int carmine_set_par(struct fb_info *info)410{411	struct carmine_fb *par = info->par;412	int ret;413 414	ret = carmine_find_mode(&info->var);415	if (ret < 0)416		return ret;417 418	par->new_mode = ret;419	if (par->cur_mode != par->new_mode) {420 421		par->cur_mode = par->new_mode;422		par->res = &car_modes[par->new_mode];423 424		carmine_init_display_param(par);425		set_display_parameters(par);426	}427 428	info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;429	return 0;430}431 432static int init_hardware(struct carmine_hw *hw)433{434	u32 flags;435	u32 loops;436	u32 ret;437 438	/* Initialize Carmine */439	/* Sets internal clock */440	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE,441			CARMINE_DFLT_IP_CLOCK_ENABLE);442 443	/* Video signal output is turned off */444	c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);445	c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);446 447	/* Software reset */448	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1);449	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0);450 451	/* I/O mode settings */452	flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 |453		CARMINE_DFLT_IP_DCTL_IO_CONT0;454	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0,455			flags);456 457	/* DRAM initial sequence */458	flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD;459	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD,460			flags);461 462	flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 |463		CARMINE_DFLT_IP_DCTL_EMODE;464	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE,465			flags);466 467	flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 |468		CARMINE_DFLT_IP_DCTL_SET_TIME2;469	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2,470			flags);471 472	flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 |473		CARMINE_DFLT_IP_DCTL_FIFO_DEPTH;474	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags);475 476	flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1;477	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1,478			flags);479 480	flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |481		CARMINE_DFLT_IP_DCTL_STATES;482	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,483			flags);484 485	/* Executes DLL reset */486	if (CARMINE_DCTL_DLL_RESET) {487		for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) {488 489			ret = c_get_hw_reg(hw, CARMINE_DCTL_REG +490					CARMINE_DCTL_REG_RSV0_STATES);491			ret &= CARMINE_DCTL_REG_STATES_MASK;492			if (!ret)493				break;494 495			mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL);496		}497 498		if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) {499			printk(KERN_ERR "DRAM init failed\n");500			return -EIO;501		}502	}503 504	flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 |505		CARMINE_DFLT_IP_DCTL_ADD;506	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags);507 508	flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |509		CARMINE_DFLT_IP_DCTL_STATES_AFT_RST;510	c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,511			flags);512 513	/* Initialize the write back register */514	c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM,515			CARMINE_WB_REG_WBM_DEFAULT);516 517	/* Initialize the Kottos registers */518	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0);519	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0);520 521	/* Set DC offsets */522	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0);523	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0);524	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0);525	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0);526	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0);527	c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0);528	return 0;529}530 531static const struct fb_ops carminefb_ops = {532	.owner		= THIS_MODULE,533	FB_DEFAULT_IOMEM_OPS,534	.fb_check_var	= carmine_check_var,535	.fb_set_par	= carmine_set_par,536	.fb_setcolreg	= carmine_setcolreg,537};538 539static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base,540			    int smem_offset, struct device *device,541			    struct fb_info **rinfo)542{543	int ret;544	struct fb_info *info;545	struct carmine_fb *par;546 547	info = framebuffer_alloc(sizeof *par, device);548	if (!info)549		return -ENOMEM;550 551	par = info->par;552	par->display_reg = regs;553	par->smem_offset = smem_offset;554 555	info->screen_base = smem_base + smem_offset;556	info->screen_size = CARMINE_DISPLAY_MEM;557	info->fbops = &carminefb_ops;558 559	info->fix = carminefb_fix;560	info->pseudo_palette = par->pseudo_palette;561 562	ret = fb_alloc_cmap(&info->cmap, 256, 1);563	if (ret < 0)564		goto err_free_fb;565 566	if (fb_mode >= ARRAY_SIZE(carmine_modedb))567		fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;568 569	par->cur_mode = par->new_mode = ~0;570 571	ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb,572			ARRAY_SIZE(carmine_modedb),573			&carmine_modedb[fb_mode], 32);574	if (!ret || ret == 4) {575		ret = -EINVAL;576		goto err_dealloc_cmap;577	}578 579	fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb),580			&info->modelist);581 582	ret = register_framebuffer(info);583	if (ret < 0)584		goto err_dealloc_cmap;585 586	fb_info(info, "%s frame buffer device\n", info->fix.id);587 588	*rinfo = info;589	return 0;590 591err_dealloc_cmap:592	fb_dealloc_cmap(&info->cmap);593err_free_fb:594	framebuffer_release(info);595	return ret;596}597 598static void cleanup_fb_device(struct fb_info *info)599{600	if (info) {601		unregister_framebuffer(info);602		fb_dealloc_cmap(&info->cmap);603		framebuffer_release(info);604	}605}606 607static int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent)608{609	struct carmine_hw *hw;610	struct device *device = &dev->dev;611	struct fb_info *info;612	int ret;613 614	ret = aperture_remove_conflicting_pci_devices(dev, "carminefb");615	if (ret)616		return ret;617 618	ret = pci_enable_device(dev);619	if (ret)620		return ret;621 622	ret = -ENOMEM;623	hw = kzalloc(sizeof *hw, GFP_KERNEL);624	if (!hw)625		goto err_enable_pci;626 627	carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR);628	carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR);629 630	if (!request_mem_region(carminefb_fix.mmio_start,631				carminefb_fix.mmio_len,632				"carminefb regbase")) {633		printk(KERN_ERR "carminefb: Can't reserve regbase.\n");634		ret = -EBUSY;635		goto err_free_hw;636	}637	hw->v_regs = ioremap(carminefb_fix.mmio_start,638			carminefb_fix.mmio_len);639	if (!hw->v_regs) {640		printk(KERN_ERR "carminefb: Can't remap %s register.\n",641				carminefb_fix.id);642		goto err_free_reg_mmio;643	}644 645	carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR);646	carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR);647 648	/* The memory area tends to be very large (256 MiB). Remap only what649	 * is required for that largest resolution to avoid remaps at run650	 * time651	 */652	if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM)653		carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM;654 655	else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) {656		printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d "657				"are required.", carminefb_fix.smem_len,658				CARMINE_TOTAL_DIPLAY_MEM);659		goto err_unmap_vregs;660	}661 662	if (!request_mem_region(carminefb_fix.smem_start,663				carminefb_fix.smem_len,	"carminefb smem")) {664		printk(KERN_ERR "carminefb: Can't reserve smem.\n");665		goto err_unmap_vregs;666	}667 668	hw->screen_mem = ioremap(carminefb_fix.smem_start,669			carminefb_fix.smem_len);670	if (!hw->screen_mem) {671		printk(KERN_ERR "carmine: Can't ioremap smem area.\n");672		goto err_reg_smem;673	}674 675	ret = init_hardware(hw);676	if (ret)677		goto err_unmap_screen;678 679	info = NULL;680	if (fb_displays & CARMINE_USE_DISPLAY0) {681		ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG,682				hw->screen_mem, CARMINE_DISPLAY_MEM * 0,683				device, &info);684		if (ret)685			goto err_deinit_hw;686	}687 688	hw->fb[0] = info;689 690	info = NULL;691	if (fb_displays & CARMINE_USE_DISPLAY1) {692		ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG,693				hw->screen_mem, CARMINE_DISPLAY_MEM * 1,694				device, &info);695		if (ret)696			goto err_cleanup_fb0;697	}698 699	hw->fb[1] = info;700	info = NULL;701 702	pci_set_drvdata(dev, hw);703	return 0;704 705err_cleanup_fb0:706	cleanup_fb_device(hw->fb[0]);707err_deinit_hw:708	/* disable clock, etc */709	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);710err_unmap_screen:711	iounmap(hw->screen_mem);712err_reg_smem:713	release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len);714err_unmap_vregs:715	iounmap(hw->v_regs);716err_free_reg_mmio:717	release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len);718err_free_hw:719	kfree(hw);720err_enable_pci:721	pci_disable_device(dev);722	return ret;723}724 725static void carminefb_remove(struct pci_dev *dev)726{727	struct carmine_hw *hw = pci_get_drvdata(dev);728	struct fb_fix_screeninfo fix;729	int i;730 731	/* in case we use only fb1 and not fb1 */732	if (hw->fb[0])733		fix = hw->fb[0]->fix;734	else735		fix = hw->fb[1]->fix;736 737	/* deactivate display(s) and switch clocks */738	c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);739	c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);740	c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);741 742	for (i = 0; i < MAX_DISPLAY; i++)743		cleanup_fb_device(hw->fb[i]);744 745	iounmap(hw->screen_mem);746	release_mem_region(fix.smem_start, fix.smem_len);747	iounmap(hw->v_regs);748	release_mem_region(fix.mmio_start, fix.mmio_len);749 750	pci_disable_device(dev);751	kfree(hw);752}753 754#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf755static struct pci_device_id carmine_devices[] = {756{757	PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)},758	{0, 0, 0, 0, 0, 0, 0}759};760 761MODULE_DEVICE_TABLE(pci, carmine_devices);762 763static struct pci_driver carmine_pci_driver = {764	.name		= "carminefb",765	.id_table	= carmine_devices,766	.probe		= carminefb_probe,767	.remove		= carminefb_remove,768};769 770static int __init carminefb_init(void)771{772	if (fb_modesetting_disabled("carminefb"))773		return -ENODEV;774 775	if (!(fb_displays &776		(CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) {777		printk(KERN_ERR "If you disable both displays than you don't "778				"need the driver at all\n");779		return -EINVAL;780	}781	return pci_register_driver(&carmine_pci_driver);782}783module_init(carminefb_init);784 785static void __exit carminefb_cleanup(void)786{787	pci_unregister_driver(&carmine_pci_driver);788}789module_exit(carminefb_cleanup);790 791MODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>");792MODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices");793MODULE_LICENSE("GPL v2");794