brintos

brintos / linux-shallow public Read only

0
0
Text · 30.2 KiB · 04c1b32 Raw
1231 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/aperture.h>3#include <linux/kernel.h>4#include <linux/module.h>5#include <linux/errno.h>6#include <linux/string.h>7#include <linux/mm.h>8#include <linux/slab.h>9#include <linux/delay.h>10#include <linux/fb.h>11#include <linux/ioport.h>12#include <linux/init.h>13#include <linux/pci.h>14#include <linux/mm_types.h>15#include <linux/vmalloc.h>16#include <linux/pagemap.h>17#include <linux/console.h>18 19#include "sm750.h"20#include "sm750_accel.h"21#include "sm750_cursor.h"22 23/*24 * #ifdef __BIG_ENDIAN25 * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,26 * size_t count, loff_t *ppos);27 * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,28 * size_t count, loff_t *ppos);29 * #endif30 */31 32/* common var for all device */33static int g_hwcursor = 1;34static int g_noaccel;35static int g_nomtrr;36static const char *g_fbmode[] = {NULL, NULL};37static const char *g_def_fbmode = "1024x768-32@60";38static char *g_settings;39static int g_dualview;40static char *g_option;41 42static const struct fb_videomode lynx750_ext[] = {43	/*	1024x600-60 VESA	[1.71:1] */44	{NULL,  60, 1024, 600, 20423, 144,  40, 18, 1, 104, 3,45	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,46	 FB_VMODE_NONINTERLACED},47 48	/*	1024x600-70 VESA */49	{NULL,  70, 1024, 600, 17211, 152,  48, 21, 1, 104, 3,50	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,51	 FB_VMODE_NONINTERLACED},52 53	/*	1024x600-75 VESA */54	{NULL,  75, 1024, 600, 15822, 160,  56, 23, 1, 104, 3,55	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,56	 FB_VMODE_NONINTERLACED},57 58	/*	1024x600-85 VESA */59	{NULL,  85, 1024, 600, 13730, 168,  56, 26, 1, 112, 3,60	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,61	 FB_VMODE_NONINTERLACED},62 63	/*	720x480	*/64	{NULL, 60,  720,  480,  37427, 88,   16, 13, 1,   72,  3,65	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,66	 FB_VMODE_NONINTERLACED},67 68	/*	1280x720		[1.78:1]	*/69	{NULL, 60,  1280,  720,  13426, 162, 86, 22, 1,  136, 3,70	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,71	 FB_VMODE_NONINTERLACED},72 73	/*	1280x768@60 */74	{NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,75	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,76	 FB_VMODE_NONINTERLACED},77 78	/*	1360 x 768	[1.77083:1]	*/79	{NULL,  60, 1360, 768, 11804, 208,  64, 23, 1, 144, 3,80	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,81	 FB_VMODE_NONINTERLACED},82 83	/*	1368 x 768      [1.78:1]	*/84	{NULL, 60,  1368,  768,  11647, 216, 72, 23, 1,  144, 3,85	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,86	 FB_VMODE_NONINTERLACED},87 88	/*	1440 x 900		[16:10]	*/89	{NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,90	 FB_SYNC_VERT_HIGH_ACT,91	 FB_VMODE_NONINTERLACED},92 93	/*	1440x960		[15:10]	*/94	{NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,95	 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,96	 FB_VMODE_NONINTERLACED},97 98	/*	1920x1080	[16:9]	*/99	{NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,100	 FB_SYNC_VERT_HIGH_ACT,101	 FB_VMODE_NONINTERLACED},102};103 104/* no hardware cursor supported under version 2.6.10, kernel bug */105static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)106{107	struct lynxfb_par *par;108	struct lynxfb_crtc *crtc;109	struct lynx_cursor *cursor;110 111	par = info->par;112	crtc = &par->crtc;113	cursor = &crtc->cursor;114 115	if (fbcursor->image.width > cursor->max_w ||116	    fbcursor->image.height > cursor->max_h ||117	    fbcursor->image.depth > 1) {118		return -ENXIO;119	}120 121	sm750_hw_cursor_disable(cursor);122	if (fbcursor->set & FB_CUR_SETSIZE)123		sm750_hw_cursor_setSize(cursor,124					fbcursor->image.width,125					fbcursor->image.height);126 127	if (fbcursor->set & FB_CUR_SETPOS)128		sm750_hw_cursor_setPos(cursor,129				       fbcursor->image.dx - info->var.xoffset,130				       fbcursor->image.dy - info->var.yoffset);131 132	if (fbcursor->set & FB_CUR_SETCMAP) {133		/* get the 16bit color of kernel means */134		u16 fg, bg;135 136		fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800)) |137		     ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5) |138		     ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);139 140		bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800)) |141		     ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5) |142		     ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);143 144		sm750_hw_cursor_setColor(cursor, fg, bg);145	}146 147	if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {148		sm750_hw_cursor_setData(cursor,149					fbcursor->rop,150					fbcursor->image.data,151					fbcursor->mask);152	}153 154	if (fbcursor->enable)155		sm750_hw_cursor_enable(cursor);156 157	return 0;158}159 160static void lynxfb_ops_fillrect(struct fb_info *info,161				const struct fb_fillrect *region)162{163	struct lynxfb_par *par;164	struct sm750_dev *sm750_dev;165	unsigned int base, pitch, Bpp, rop;166	u32 color;167 168	if (info->state != FBINFO_STATE_RUNNING)169		return;170 171	par = info->par;172	sm750_dev = par->dev;173 174	/*175	 * each time 2d function begin to work,below three variable always need176	 * be set, seems we can put them together in some place177	 */178	base = par->crtc.o_screen;179	pitch = info->fix.line_length;180	Bpp = info->var.bits_per_pixel >> 3;181 182	color = (Bpp == 1) ? region->color :183		((u32 *)info->pseudo_palette)[region->color];184	rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR : HW_ROP2_COPY;185 186	/*187	 * If not use spin_lock, system will die if user load driver188	 * and immediately unload driver frequently (dual)189	 * since they fb_count could change during the lifetime of190	 * this lock, we are holding it for all cases.191	 */192	spin_lock(&sm750_dev->slock);193 194	sm750_dev->accel.de_fillrect(&sm750_dev->accel,195				     base, pitch, Bpp,196				     region->dx, region->dy,197				     region->width, region->height,198				     color, rop);199	spin_unlock(&sm750_dev->slock);200}201 202static void lynxfb_ops_copyarea(struct fb_info *info,203				const struct fb_copyarea *region)204{205	struct lynxfb_par *par;206	struct sm750_dev *sm750_dev;207	unsigned int base, pitch, Bpp;208 209	par = info->par;210	sm750_dev = par->dev;211 212	/*213	 * each time 2d function begin to work,below three variable always need214	 * be set, seems we can put them together in some place215	 */216	base = par->crtc.o_screen;217	pitch = info->fix.line_length;218	Bpp = info->var.bits_per_pixel >> 3;219 220	/*221	 * If not use spin_lock, system will die if user load driver222	 * and immediately unload driver frequently (dual)223	 * since they fb_count could change during the lifetime of224	 * this lock, we are holding it for all cases.225	 */226	spin_lock(&sm750_dev->slock);227 228	sm750_dev->accel.de_copyarea(&sm750_dev->accel,229				     base, pitch, region->sx, region->sy,230				     base, pitch, Bpp, region->dx, region->dy,231				     region->width, region->height,232				     HW_ROP2_COPY);233	spin_unlock(&sm750_dev->slock);234}235 236static void lynxfb_ops_imageblit(struct fb_info *info,237				 const struct fb_image *image)238{239	unsigned int base, pitch, Bpp;240	unsigned int fgcol, bgcol;241	struct lynxfb_par *par;242	struct sm750_dev *sm750_dev;243 244	par = info->par;245	sm750_dev = par->dev;246	/*247	 * each time 2d function begin to work,below three variable always need248	 * be set, seems we can put them together in some place249	 */250	base = par->crtc.o_screen;251	pitch = info->fix.line_length;252	Bpp = info->var.bits_per_pixel >> 3;253 254	/* TODO: Implement hardware acceleration for image->depth > 1 */255	if (image->depth != 1) {256		cfb_imageblit(info, image);257		return;258	}259 260	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||261	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {262		fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];263		bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];264	} else {265		fgcol = image->fg_color;266		bgcol = image->bg_color;267	}268 269	/*270	 * If not use spin_lock, system will die if user load driver271	 * and immediately unload driver frequently (dual)272	 * since they fb_count could change during the lifetime of273	 * this lock, we are holding it for all cases.274	 */275	spin_lock(&sm750_dev->slock);276 277	sm750_dev->accel.de_imageblit(&sm750_dev->accel,278				      image->data, image->width >> 3, 0,279				      base, pitch, Bpp,280				      image->dx, image->dy,281				      image->width, image->height,282				      fgcol, bgcol, HW_ROP2_COPY);283	spin_unlock(&sm750_dev->slock);284}285 286static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,287				  struct fb_info *info)288{289	struct lynxfb_par *par;290	struct lynxfb_crtc *crtc;291 292	if (!info)293		return -EINVAL;294 295	par = info->par;296	crtc = &par->crtc;297	return hw_sm750_pan_display(crtc, var, info);298}299 300static inline void lynxfb_set_visual_mode(struct fb_info *info)301{302	switch (info->var.bits_per_pixel) {303	case 8:304		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;305		break;306	case 16:307	case 24:308	case 32:309		info->fix.visual = FB_VISUAL_TRUECOLOR;310		break;311	default:312		break;313	}314}315 316static inline int lynxfb_set_color_offsets(struct fb_info *info)317{318	lynxfb_set_visual_mode(info);319 320	switch (info->var.bits_per_pixel) {321	case 8:322		info->var.red.offset = 0;323		info->var.red.length = 8;324		info->var.green.offset = 0;325		info->var.green.length = 8;326		info->var.blue.offset = 0;327		info->var.blue.length = 8;328		info->var.transp.length = 0;329		info->var.transp.offset = 0;330		break;331	case 16:332		info->var.red.offset = 11;333		info->var.red.length = 5;334		info->var.green.offset = 5;335		info->var.green.length = 6;336		info->var.blue.offset = 0;337		info->var.blue.length = 5;338		info->var.transp.length = 0;339		info->var.transp.offset = 0;340		break;341	case 24:342	case 32:343		info->var.red.offset = 16;344		info->var.red.length = 8;345		info->var.green.offset = 8;346		info->var.green.length = 8;347		info->var.blue.offset = 0;348		info->var.blue.length = 8;349		break;350	default:351		return -EINVAL;352	}353	return 0;354}355 356static int lynxfb_ops_set_par(struct fb_info *info)357{358	struct lynxfb_par *par;359	struct lynxfb_crtc *crtc;360	struct lynxfb_output *output;361	struct fb_var_screeninfo *var;362	struct fb_fix_screeninfo *fix;363	int ret;364	unsigned int line_length;365 366	if (!info)367		return -EINVAL;368 369	ret = 0;370	par = info->par;371	crtc = &par->crtc;372	output = &par->output;373	var = &info->var;374	fix = &info->fix;375 376	/* fix structure is not so FIX ... */377	line_length = var->xres_virtual * var->bits_per_pixel / 8;378	line_length = ALIGN(line_length, crtc->line_pad);379	fix->line_length = line_length;380	pr_info("fix->line_length = %d\n", fix->line_length);381 382	/*383	 * var->red,green,blue,transp are need to be set by driver384	 * and these data should be set before setcolreg routine385	 */386 387	ret = lynxfb_set_color_offsets(info);388 389	var->height = -1;390	var->width = -1;391	var->accel_flags = 0;/*FB_ACCELF_TEXT;*/392 393	if (ret) {394		pr_err("bpp %d not supported\n", var->bits_per_pixel);395		return ret;396	}397	ret = hw_sm750_crtc_setMode(crtc, var, fix);398	if (!ret)399		ret = hw_sm750_output_setMode(output, var, fix);400	return ret;401}402 403static inline unsigned int chan_to_field(unsigned int chan,404					 struct fb_bitfield *bf)405{406	chan &= 0xffff;407	chan >>= 16 - bf->length;408	return chan << bf->offset;409}410 411static int __maybe_unused lynxfb_suspend(struct device *dev)412{413	struct fb_info *info;414	struct sm750_dev *sm750_dev;415 416	sm750_dev = dev_get_drvdata(dev);417 418	console_lock();419	info = sm750_dev->fbinfo[0];420	if (info)421		/* 1 means do suspend */422		fb_set_suspend(info, 1);423	info = sm750_dev->fbinfo[1];424	if (info)425		/* 1 means do suspend */426		fb_set_suspend(info, 1);427 428	console_unlock();429	return 0;430}431 432static int __maybe_unused lynxfb_resume(struct device *dev)433{434	struct pci_dev *pdev = to_pci_dev(dev);435	struct fb_info *info;436	struct sm750_dev *sm750_dev;437 438	struct lynxfb_par *par;439	struct lynxfb_crtc *crtc;440	struct lynx_cursor *cursor;441 442	sm750_dev = pci_get_drvdata(pdev);443 444	console_lock();445 446	hw_sm750_inithw(sm750_dev, pdev);447 448	info = sm750_dev->fbinfo[0];449 450	if (info) {451		par = info->par;452		crtc = &par->crtc;453		cursor = &crtc->cursor;454		memset_io(cursor->vstart, 0x0, cursor->size);455		memset_io(crtc->v_screen, 0x0, crtc->vidmem_size);456		lynxfb_ops_set_par(info);457		fb_set_suspend(info, 0);458	}459 460	info = sm750_dev->fbinfo[1];461 462	if (info) {463		par = info->par;464		crtc = &par->crtc;465		cursor = &crtc->cursor;466		memset_io(cursor->vstart, 0x0, cursor->size);467		memset_io(crtc->v_screen, 0x0, crtc->vidmem_size);468		lynxfb_ops_set_par(info);469		fb_set_suspend(info, 0);470	}471 472	pdev->dev.power.power_state.event = PM_EVENT_RESUME;473 474	console_unlock();475	return 0;476}477 478static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,479				struct fb_info *info)480{481	int ret;482	struct lynxfb_par *par;483	struct lynxfb_crtc *crtc;484	resource_size_t request;485 486	ret = 0;487	par = info->par;488	crtc = &par->crtc;489 490	pr_debug("check var:%dx%d-%d\n",491		 var->xres,492		 var->yres,493		 var->bits_per_pixel);494 495	ret = lynxfb_set_color_offsets(info);496 497	if (ret) {498		pr_err("bpp %d not supported\n", var->bits_per_pixel);499		return ret;500	}501 502	var->height = -1;503	var->width = -1;504	var->accel_flags = 0;/* FB_ACCELF_TEXT; */505 506	/* check if current fb's video memory big enough to hold the onscreen*/507	request = var->xres_virtual * (var->bits_per_pixel >> 3);508	/* defaulty crtc->channel go with par->index */509 510	request = ALIGN(request, crtc->line_pad);511	request = request * var->yres_virtual;512	if (crtc->vidmem_size < request) {513		pr_err("not enough video memory for mode\n");514		return -ENOMEM;515	}516 517	return hw_sm750_crtc_checkMode(crtc, var);518}519 520static int lynxfb_ops_setcolreg(unsigned int regno,521				unsigned int red,522				unsigned int green,523				unsigned int blue,524				unsigned int transp,525				struct fb_info *info)526{527	struct lynxfb_par *par;528	struct lynxfb_crtc *crtc;529	struct fb_var_screeninfo *var;530	int ret;531 532	par = info->par;533	crtc = &par->crtc;534	var = &info->var;535	ret = 0;536 537	if (regno > 256) {538		pr_err("regno = %d\n", regno);539		return -EINVAL;540	}541 542	if (info->var.grayscale)543		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;544 545	if (var->bits_per_pixel == 8 &&546	    info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {547		red >>= 8;548		green >>= 8;549		blue >>= 8;550		ret = hw_sm750_setColReg(crtc, regno, red, green, blue);551		goto exit;552	}553 554	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {555		u32 val;556 557		if (var->bits_per_pixel == 16 ||558		    var->bits_per_pixel == 32 ||559		    var->bits_per_pixel == 24) {560			val = chan_to_field(red, &var->red);561			val |= chan_to_field(green, &var->green);562			val |= chan_to_field(blue, &var->blue);563			par->pseudo_palette[regno] = val;564			goto exit;565		}566	}567 568	ret = -EINVAL;569 570exit:571	return ret;572}573 574static int lynxfb_ops_blank(int blank, struct fb_info *info)575{576	struct lynxfb_par *par;577	struct lynxfb_output *output;578 579	pr_debug("blank = %d.\n", blank);580	par = info->par;581	output = &par->output;582	return output->proc_setBLANK(output, blank);583}584 585static int sm750fb_set_drv(struct lynxfb_par *par)586{587	int ret;588	struct sm750_dev *sm750_dev;589	struct lynxfb_output *output;590	struct lynxfb_crtc *crtc;591 592	ret = 0;593 594	sm750_dev = par->dev;595	output = &par->output;596	crtc = &par->crtc;597 598	crtc->vidmem_size = sm750_dev->vidmem_size;599	if (sm750_dev->fb_count > 1)600		crtc->vidmem_size >>= 1;601 602	/* setup crtc and output member */603	sm750_dev->hwCursor = g_hwcursor;604 605	crtc->line_pad = 16;606	crtc->xpanstep = 8;607	crtc->ypanstep = 1;608	crtc->ywrapstep = 0;609 610	output->proc_setBLANK = (sm750_dev->revid == SM750LE_REVISION_ID) ?611				 hw_sm750le_setBLANK : hw_sm750_setBLANK;612	/* chip specific phase */613	sm750_dev->accel.de_wait = (sm750_dev->revid == SM750LE_REVISION_ID) ?614				    hw_sm750le_deWait : hw_sm750_deWait;615	switch (sm750_dev->dataflow) {616	case sm750_simul_pri:617		output->paths = sm750_pnc;618		crtc->channel = sm750_primary;619		crtc->o_screen = 0;620		crtc->v_screen = sm750_dev->pvMem;621		pr_info("use simul primary mode\n");622		break;623	case sm750_simul_sec:624		output->paths = sm750_pnc;625		crtc->channel = sm750_secondary;626		crtc->o_screen = 0;627		crtc->v_screen = sm750_dev->pvMem;628		break;629	case sm750_dual_normal:630		if (par->index == 0) {631			output->paths = sm750_panel;632			crtc->channel = sm750_primary;633			crtc->o_screen = 0;634			crtc->v_screen = sm750_dev->pvMem;635		} else {636			output->paths = sm750_crt;637			crtc->channel = sm750_secondary;638			/* not consider of padding stuffs for o_screen,need fix */639			crtc->o_screen = sm750_dev->vidmem_size >> 1;640			crtc->v_screen = sm750_dev->pvMem + crtc->o_screen;641		}642		break;643	case sm750_dual_swap:644		if (par->index == 0) {645			output->paths = sm750_panel;646			crtc->channel = sm750_secondary;647			crtc->o_screen = 0;648			crtc->v_screen = sm750_dev->pvMem;649		} else {650			output->paths = sm750_crt;651			crtc->channel = sm750_primary;652			/* not consider of padding stuffs for o_screen,653			 * need fix654			 */655			crtc->o_screen = sm750_dev->vidmem_size >> 1;656			crtc->v_screen = sm750_dev->pvMem + crtc->o_screen;657		}658		break;659	default:660		ret = -EINVAL;661	}662 663	return ret;664}665 666static const struct fb_ops lynxfb_ops = {667	.owner = THIS_MODULE,668	FB_DEFAULT_IOMEM_OPS,669	.fb_check_var =  lynxfb_ops_check_var,670	.fb_set_par = lynxfb_ops_set_par,671	.fb_setcolreg = lynxfb_ops_setcolreg,672	.fb_blank = lynxfb_ops_blank,673	.fb_pan_display = lynxfb_ops_pan_display,674};675 676static const struct fb_ops lynxfb_ops_with_cursor = {677	.owner = THIS_MODULE,678	FB_DEFAULT_IOMEM_OPS,679	.fb_check_var =  lynxfb_ops_check_var,680	.fb_set_par = lynxfb_ops_set_par,681	.fb_setcolreg = lynxfb_ops_setcolreg,682	.fb_blank = lynxfb_ops_blank,683	.fb_pan_display = lynxfb_ops_pan_display,684	.fb_cursor = lynxfb_ops_cursor,685};686 687static const struct fb_ops lynxfb_ops_accel = {688	.owner = THIS_MODULE,689	__FB_DEFAULT_IOMEM_OPS_RDWR,690	.fb_check_var =  lynxfb_ops_check_var,691	.fb_set_par = lynxfb_ops_set_par,692	.fb_setcolreg = lynxfb_ops_setcolreg,693	.fb_blank = lynxfb_ops_blank,694	.fb_pan_display = lynxfb_ops_pan_display,695	.fb_fillrect = lynxfb_ops_fillrect,696	.fb_copyarea = lynxfb_ops_copyarea,697	.fb_imageblit = lynxfb_ops_imageblit,698	__FB_DEFAULT_IOMEM_OPS_MMAP,699};700 701static const struct fb_ops lynxfb_ops_accel_with_cursor = {702	.owner = THIS_MODULE,703	__FB_DEFAULT_IOMEM_OPS_RDWR,704	.fb_check_var =  lynxfb_ops_check_var,705	.fb_set_par = lynxfb_ops_set_par,706	.fb_setcolreg = lynxfb_ops_setcolreg,707	.fb_blank = lynxfb_ops_blank,708	.fb_pan_display = lynxfb_ops_pan_display,709	.fb_fillrect = lynxfb_ops_fillrect,710	.fb_copyarea = lynxfb_ops_copyarea,711	.fb_imageblit = lynxfb_ops_imageblit,712	.fb_cursor = lynxfb_ops_cursor,713	__FB_DEFAULT_IOMEM_OPS_MMAP,714};715 716static int lynxfb_set_fbinfo(struct fb_info *info, int index)717{718	int i;719	struct lynxfb_par *par;720	struct sm750_dev *sm750_dev;721	struct lynxfb_crtc *crtc;722	struct lynxfb_output *output;723	struct fb_var_screeninfo *var;724	struct fb_fix_screeninfo *fix;725 726	const struct fb_videomode *pdb[] = {727		lynx750_ext, NULL, vesa_modes,728	};729	int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};730	static const char * const mdb_desc[] = {731		"driver prepared modes",732		"kernel prepared default modedb",733		"kernel HELPERS prepared vesa_modes",734	};735 736	static const char *fixId[2] = {737		"sm750_fb1", "sm750_fb2",738	};739 740	int ret, line_length;741 742	ret = 0;743	par = (struct lynxfb_par *)info->par;744	sm750_dev = par->dev;745	crtc = &par->crtc;746	output = &par->output;747	var = &info->var;748	fix = &info->fix;749 750	/* set index */751	par->index = index;752	output->channel = &crtc->channel;753	sm750fb_set_drv(par);754 755	/*756	 * set current cursor variable and proc pointer,757	 * must be set after crtc member initialized758	 */759	crtc->cursor.offset = crtc->o_screen + crtc->vidmem_size - 1024;760	crtc->cursor.mmio = sm750_dev->pvReg +761		0x800f0 + (int)crtc->channel * 0x140;762 763	pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);764	crtc->cursor.max_h = 64;765	crtc->cursor.max_w = 64;766	crtc->cursor.size = crtc->cursor.max_h * crtc->cursor.max_w * 2 / 8;767	crtc->cursor.vstart = sm750_dev->pvMem + crtc->cursor.offset;768 769	memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);770	if (!g_hwcursor)771		sm750_hw_cursor_disable(&crtc->cursor);772 773	/* set info->fbops, must be set before fb_find_mode */774	if (!sm750_dev->accel_off) {775		/* use 2d acceleration */776		if (!g_hwcursor)777			info->fbops = &lynxfb_ops_accel;778		else779			info->fbops = &lynxfb_ops_accel_with_cursor;780	} else {781		if (!g_hwcursor)782			info->fbops = &lynxfb_ops;783		else784			info->fbops = &lynxfb_ops_with_cursor;785	}786 787	if (!g_fbmode[index]) {788		g_fbmode[index] = g_def_fbmode;789		if (index)790			g_fbmode[index] = g_fbmode[0];791	}792 793	for (i = 0; i < 3; i++) {794		ret = fb_find_mode(var, info, g_fbmode[index],795				   pdb[i], cdb[i], NULL, 8);796 797		if (ret == 1) {798			pr_info("success! use specified mode:%s in %s\n",799				g_fbmode[index],800				mdb_desc[i]);801			break;802		} else if (ret == 2) {803			pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",804				g_fbmode[index],805				mdb_desc[i]);806			break;807		} else if (ret == 3) {808			pr_warn("wanna use default mode\n");809			/*break;*/810		} else if (ret == 4) {811			pr_warn("fall back to any valid mode\n");812		} else {813			pr_warn("ret = %d,fb_find_mode failed,with %s\n",814				ret,815				mdb_desc[i]);816		}817	}818 819	/* some member of info->var had been set by fb_find_mode */820 821	pr_info("Member of info->var is :\n"822		"xres=%d\n"823		"yres=%d\n"824		"xres_virtual=%d\n"825		"yres_virtual=%d\n"826		"xoffset=%d\n"827		"yoffset=%d\n"828		"bits_per_pixel=%d\n"829		" ...\n",830		var->xres,831		var->yres,832		var->xres_virtual,833		var->yres_virtual,834		var->xoffset,835		var->yoffset,836		var->bits_per_pixel);837 838	/* set par */839	par->info = info;840 841	/* set info */842	line_length = ALIGN((var->xres_virtual * var->bits_per_pixel / 8),843			    crtc->line_pad);844 845	info->pseudo_palette = &par->pseudo_palette[0];846	info->screen_base = crtc->v_screen;847	pr_debug("screen_base vaddr = %p\n", info->screen_base);848	info->screen_size = line_length * var->yres_virtual;849 850	/* set info->fix */851	fix->type = FB_TYPE_PACKED_PIXELS;852	fix->type_aux = 0;853	fix->xpanstep = crtc->xpanstep;854	fix->ypanstep = crtc->ypanstep;855	fix->ywrapstep = crtc->ywrapstep;856	fix->accel = FB_ACCEL_SMI;857 858	strscpy(fix->id, fixId[index], sizeof(fix->id));859 860	fix->smem_start = crtc->o_screen + sm750_dev->vidmem_start;861	pr_info("fix->smem_start = %lx\n", fix->smem_start);862	/*863	 * according to mmap experiment from user space application,864	 * fix->mmio_len should not larger than virtual size865	 * (xres_virtual x yres_virtual x ByPP)866	 * Below line maybe buggy when user mmap fb dev node and write867	 * data into the bound over virtual size868	 */869	fix->smem_len = crtc->vidmem_size;870	pr_info("fix->smem_len = %x\n", fix->smem_len);871	info->screen_size = fix->smem_len;872	fix->line_length = line_length;873	fix->mmio_start = sm750_dev->vidreg_start;874	pr_info("fix->mmio_start = %lx\n", fix->mmio_start);875	fix->mmio_len = sm750_dev->vidreg_size;876	pr_info("fix->mmio_len = %x\n", fix->mmio_len);877 878	lynxfb_set_visual_mode(info);879 880	/* set var */881	var->activate = FB_ACTIVATE_NOW;882	var->accel_flags = 0;883	var->vmode = FB_VMODE_NONINTERLACED;884 885	pr_debug("#1 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",886		 info->cmap.start, info->cmap.len,887		 info->cmap.red, info->cmap.green, info->cmap.blue,888		 info->cmap.transp);889 890	ret = fb_alloc_cmap(&info->cmap, 256, 0);891	if (ret < 0) {892		pr_err("Could not allocate memory for cmap.\n");893		goto exit;894	}895 896	pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",897		 info->cmap.start, info->cmap.len,898		 info->cmap.red, info->cmap.green, info->cmap.blue,899		 info->cmap.transp);900 901exit:902	lynxfb_ops_check_var(var, info);903	return ret;904}905 906/*	chip specific g_option configuration routine */907static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src)908{909	char *opt;910	int swap;911 912	swap = 0;913 914	sm750_dev->initParm.chip_clk = 0;915	sm750_dev->initParm.mem_clk = 0;916	sm750_dev->initParm.master_clk = 0;917	sm750_dev->initParm.powerMode = 0;918	sm750_dev->initParm.setAllEngOff = 0;919	sm750_dev->initParm.resetMemory = 1;920 921	/* defaultly turn g_hwcursor on for both view */922	g_hwcursor = 3;923 924	if (!src || !*src) {925		dev_warn(&sm750_dev->pdev->dev, "no specific g_option.\n");926		goto NO_PARAM;927	}928 929	while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {930		dev_info(&sm750_dev->pdev->dev, "opt=%s\n", opt);931		dev_info(&sm750_dev->pdev->dev, "src=%s\n", src);932 933		if (!strncmp(opt, "swap", strlen("swap"))) {934			swap = 1;935		} else if (!strncmp(opt, "nocrt", strlen("nocrt"))) {936			sm750_dev->nocrt = 1;937		} else if (!strncmp(opt, "36bit", strlen("36bit"))) {938			sm750_dev->pnltype = sm750_doubleTFT;939		} else if (!strncmp(opt, "18bit", strlen("18bit"))) {940			sm750_dev->pnltype = sm750_dualTFT;941		} else if (!strncmp(opt, "24bit", strlen("24bit"))) {942			sm750_dev->pnltype = sm750_24TFT;943		} else if (!strncmp(opt, "nohwc0", strlen("nohwc0"))) {944			g_hwcursor &= ~0x1;945		} else if (!strncmp(opt, "nohwc1", strlen("nohwc1"))) {946			g_hwcursor &= ~0x2;947		} else if (!strncmp(opt, "nohwc", strlen("nohwc"))) {948			g_hwcursor = 0;949		} else {950			if (!g_fbmode[0]) {951				g_fbmode[0] = opt;952				dev_info(&sm750_dev->pdev->dev,953					 "find fbmode0 : %s\n", g_fbmode[0]);954			} else if (!g_fbmode[1]) {955				g_fbmode[1] = opt;956				dev_info(&sm750_dev->pdev->dev,957					 "find fbmode1 : %s\n", g_fbmode[1]);958			} else {959				dev_warn(&sm750_dev->pdev->dev, "How many view you wann set?\n");960			}961		}962	}963 964NO_PARAM:965	if (sm750_dev->revid != SM750LE_REVISION_ID) {966		if (sm750_dev->fb_count > 1) {967			if (swap)968				sm750_dev->dataflow = sm750_dual_swap;969			else970				sm750_dev->dataflow = sm750_dual_normal;971		} else {972			if (swap)973				sm750_dev->dataflow = sm750_simul_sec;974			else975				sm750_dev->dataflow = sm750_simul_pri;976		}977	} else {978		/* SM750LE only have one crt channel */979		sm750_dev->dataflow = sm750_simul_sec;980		/* sm750le do not have complex attributes */981		sm750_dev->nocrt = 0;982	}983}984 985static void sm750fb_framebuffer_release(struct sm750_dev *sm750_dev)986{987	struct fb_info *fb_info;988 989	while (sm750_dev->fb_count) {990		fb_info = sm750_dev->fbinfo[sm750_dev->fb_count - 1];991		unregister_framebuffer(fb_info);992		framebuffer_release(fb_info);993		sm750_dev->fb_count--;994	}995}996 997static int sm750fb_framebuffer_alloc(struct sm750_dev *sm750_dev, int fbidx)998{999	struct fb_info *fb_info;1000	struct lynxfb_par *par;1001	int err;1002 1003	fb_info = framebuffer_alloc(sizeof(struct lynxfb_par),1004				    &sm750_dev->pdev->dev);1005	if (!fb_info)1006		return -ENOMEM;1007 1008	sm750_dev->fbinfo[fbidx] = fb_info;1009	par = fb_info->par;1010	par->dev = sm750_dev;1011 1012	err = lynxfb_set_fbinfo(fb_info, fbidx);1013	if (err)1014		goto release_fb;1015 1016	err = register_framebuffer(fb_info);1017	if (err < 0)1018		goto release_fb;1019 1020	sm750_dev->fb_count++;1021 1022	return 0;1023 1024release_fb:1025	framebuffer_release(fb_info);1026	return err;1027}1028 1029static int lynxfb_pci_probe(struct pci_dev *pdev,1030			    const struct pci_device_id *ent)1031{1032	struct sm750_dev *sm750_dev = NULL;1033	int max_fb;1034	int fbidx;1035	int err;1036 1037	err = aperture_remove_conflicting_pci_devices(pdev, "sm750_fb1");1038	if (err)1039		return err;1040 1041	/* enable device */1042	err = pcim_enable_device(pdev);1043	if (err)1044		return err;1045 1046	err = -ENOMEM;1047	sm750_dev = devm_kzalloc(&pdev->dev, sizeof(*sm750_dev), GFP_KERNEL);1048	if (!sm750_dev)1049		return err;1050 1051	sm750_dev->fbinfo[0] = NULL;1052	sm750_dev->fbinfo[1] = NULL;1053	sm750_dev->devid = pdev->device;1054	sm750_dev->revid = pdev->revision;1055	sm750_dev->pdev = pdev;1056	sm750_dev->mtrr_off = g_nomtrr;1057	sm750_dev->mtrr.vram = 0;1058	sm750_dev->accel_off = g_noaccel;1059	spin_lock_init(&sm750_dev->slock);1060 1061	if (!sm750_dev->accel_off) {1062		/*1063		 * hook deInit and 2d routines, notes that below hw_xxx1064		 * routine can work on most of lynx chips1065		 * if some chip need specific function,1066		 * please hook it in smXXX_set_drv routine1067		 */1068		sm750_dev->accel.de_init = sm750_hw_de_init;1069		sm750_dev->accel.de_fillrect = sm750_hw_fillrect;1070		sm750_dev->accel.de_copyarea = sm750_hw_copyarea;1071		sm750_dev->accel.de_imageblit = sm750_hw_imageblit;1072	}1073 1074	/* call chip specific setup routine  */1075	sm750fb_setup(sm750_dev, g_settings);1076 1077	/* call chip specific mmap routine */1078	err = hw_sm750_map(sm750_dev, pdev);1079	if (err)1080		return err;1081 1082	if (!sm750_dev->mtrr_off)1083		sm750_dev->mtrr.vram = arch_phys_wc_add(sm750_dev->vidmem_start,1084							sm750_dev->vidmem_size);1085 1086	memset_io(sm750_dev->pvMem, 0, sm750_dev->vidmem_size);1087 1088	pci_set_drvdata(pdev, sm750_dev);1089 1090	/* call chipInit routine */1091	hw_sm750_inithw(sm750_dev, pdev);1092 1093	/* allocate frame buffer info structures according to g_dualview */1094	max_fb = g_dualview ? 2 : 1;1095	for (fbidx = 0; fbidx < max_fb; fbidx++) {1096		err = sm750fb_framebuffer_alloc(sm750_dev, fbidx);1097		if (err)1098			goto release_fb;1099	}1100 1101	return 0;1102 1103release_fb:1104	sm750fb_framebuffer_release(sm750_dev);1105	return err;1106}1107 1108static void lynxfb_pci_remove(struct pci_dev *pdev)1109{1110	struct sm750_dev *sm750_dev;1111 1112	sm750_dev = pci_get_drvdata(pdev);1113 1114	sm750fb_framebuffer_release(sm750_dev);1115	arch_phys_wc_del(sm750_dev->mtrr.vram);1116 1117	iounmap(sm750_dev->pvReg);1118	iounmap(sm750_dev->pvMem);1119	kfree(g_settings);1120}1121 1122static int __init lynxfb_setup(char *options)1123{1124	int len;1125	char *opt, *tmp;1126 1127	if (!options || !*options) {1128		pr_warn("no options.\n");1129		return 0;1130	}1131 1132	pr_info("options:%s\n", options);1133 1134	len = strlen(options) + 1;1135	g_settings = kzalloc(len, GFP_KERNEL);1136	if (!g_settings)1137		return -ENOMEM;1138 1139	tmp = g_settings;1140 1141	/*1142	 * Notes:1143	 * char * strsep(char **s,const char * ct);1144	 * @s: the string to be searched1145	 * @ct :the characters to search for1146	 *1147	 * strsep() updates @options to pointer after the first found token1148	 * it also returns the pointer ahead the token.1149	 */1150	while ((opt = strsep(&options, ":")) != NULL) {1151		/* options that mean for any lynx chips are configured here */1152		if (!strncmp(opt, "noaccel", strlen("noaccel"))) {1153			g_noaccel = 1;1154		} else if (!strncmp(opt, "nomtrr", strlen("nomtrr"))) {1155			g_nomtrr = 1;1156		} else if (!strncmp(opt, "dual", strlen("dual"))) {1157			g_dualview = 1;1158		} else {1159			strcat(tmp, opt);1160			tmp += strlen(opt);1161			if (options)1162				*tmp++ = ':';1163			else1164				*tmp++ = 0;1165		}1166	}1167 1168	/* misc g_settings are transport to chip specific routines */1169	pr_info("parameter left for chip specific analysis:%s\n", g_settings);1170	return 0;1171}1172 1173static const struct pci_device_id smi_pci_table[] = {1174	{ PCI_DEVICE(0x126f, 0x0750), },1175	{0,}1176};1177 1178MODULE_DEVICE_TABLE(pci, smi_pci_table);1179 1180static SIMPLE_DEV_PM_OPS(lynxfb_pm_ops, lynxfb_suspend, lynxfb_resume);1181 1182static struct pci_driver lynxfb_driver = {1183	.name =		"sm750fb",1184	.id_table =	smi_pci_table,1185	.probe =	lynxfb_pci_probe,1186	.remove =	lynxfb_pci_remove,1187	.driver.pm =	&lynxfb_pm_ops,1188};1189 1190static int __init lynxfb_init(void)1191{1192	char *option;1193 1194	if (fb_modesetting_disabled("sm750fb"))1195		return -ENODEV;1196 1197#ifdef MODULE1198	option = g_option;1199#else1200	if (fb_get_options("sm750fb", &option))1201		return -ENODEV;1202#endif1203 1204	lynxfb_setup(option);1205	return pci_register_driver(&lynxfb_driver);1206}1207module_init(lynxfb_init);1208 1209static void __exit lynxfb_exit(void)1210{1211	pci_unregister_driver(&lynxfb_driver);1212}1213module_exit(lynxfb_exit);1214 1215module_param(g_option, charp, 0444);1216 1217MODULE_PARM_DESC(g_option,1218		 "\n\t\tCommon options:\n"1219		 "\t\tnoaccel:disable 2d capabilities\n"1220		 "\t\tnomtrr:disable MTRR attribute for video memory\n"1221		 "\t\tdualview:dual frame buffer feature enabled\n"1222		 "\t\tnohwc:disable hardware cursor\n"1223		 "\t\tUsual example:\n"1224		 "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"1225		 );1226 1227MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");1228MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");1229MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");1230MODULE_LICENSE("Dual BSD/GPL");1231