brintos

brintos / linux-shallow public Read only

0
0
Text · 45.9 KiB · 2682b20 Raw
1926 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Framebuffer driver for TI OMAP boards4 *5 * Copyright (C) 2004 Nokia Corporation6 * Author: Imre Deak <imre.deak@nokia.com>7 *8 * Acknowledgements:9 *   Alex McMains <aam@ridgerun.com>       - Original driver10 *   Juha Yrjola <juha.yrjola@nokia.com>   - Original driver and improvements11 *   Dirk Behme <dirk.behme@de.bosch.com>  - changes for 2.6 kernel API12 *   Texas Instruments                     - H3 support13 */14#include <linux/platform_device.h>15#include <linux/mm.h>16#include <linux/slab.h>17#include <linux/uaccess.h>18#include <linux/module.h>19#include <linux/sysfs.h>20 21#include <linux/omap-dma.h>22 23#include <linux/soc/ti/omap1-soc.h>24#include "omapfb.h"25#include "lcdc.h"26 27#define MODULE_NAME	"omapfb"28 29static unsigned int	def_accel;30static unsigned long	def_vram[OMAPFB_PLANE_NUM];31static unsigned int	def_vram_cnt;32static unsigned long	def_vxres;33static unsigned long	def_vyres;34static unsigned int	def_rotate;35static unsigned int	def_mirror;36 37static bool	manual_update = IS_BUILTIN(CONFIG_FB_OMAP_MANUAL_UPDATE);38 39static struct platform_device	*fbdev_pdev;40static struct lcd_panel		*fbdev_panel;41static struct omapfb_device	*omapfb_dev;42 43struct caps_table_struct {44	unsigned long flag;45	const char *name;46};47 48static const struct caps_table_struct ctrl_caps[] = {49	{ OMAPFB_CAPS_MANUAL_UPDATE,  "manual update" },50	{ OMAPFB_CAPS_TEARSYNC,       "tearing synchronization" },51	{ OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" },52	{ OMAPFB_CAPS_PLANE_SCALE,    "scale plane" },53	{ OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },54	{ OMAPFB_CAPS_WINDOW_SCALE,   "scale window" },55	{ OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" },56	{ OMAPFB_CAPS_WINDOW_ROTATE,  "rotate window" },57	{ OMAPFB_CAPS_SET_BACKLIGHT,  "backlight setting" },58};59 60static const struct caps_table_struct color_caps[] = {61	{ 1 << OMAPFB_COLOR_RGB565,	"RGB565", },62	{ 1 << OMAPFB_COLOR_YUV422,	"YUV422", },63	{ 1 << OMAPFB_COLOR_YUV420,	"YUV420", },64	{ 1 << OMAPFB_COLOR_CLUT_8BPP,	"CLUT8", },65	{ 1 << OMAPFB_COLOR_CLUT_4BPP,	"CLUT4", },66	{ 1 << OMAPFB_COLOR_CLUT_2BPP,	"CLUT2", },67	{ 1 << OMAPFB_COLOR_CLUT_1BPP,	"CLUT1", },68	{ 1 << OMAPFB_COLOR_RGB444,	"RGB444", },69	{ 1 << OMAPFB_COLOR_YUY422,	"YUY422", },70};71 72static void omapdss_release(struct device *dev)73{74}75 76/* dummy device for clocks */77static struct platform_device omapdss_device = {78	.name		= "omapdss_dss",79	.id		= -1,80	.dev            = {81		.release = omapdss_release,82	},83};84 85/*86 * ---------------------------------------------------------------------------87 * LCD panel88 * ---------------------------------------------------------------------------89 */90extern struct lcd_ctrl hwa742_ctrl;91 92static const struct lcd_ctrl *ctrls[] = {93	&omap1_int_ctrl,94 95#ifdef CONFIG_FB_OMAP_LCDC_HWA74296	&hwa742_ctrl,97#endif98};99 100#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL101extern struct lcd_ctrl_extif omap1_ext_if;102#endif103 104static void omapfb_rqueue_lock(struct omapfb_device *fbdev)105{106	mutex_lock(&fbdev->rqueue_mutex);107}108 109static void omapfb_rqueue_unlock(struct omapfb_device *fbdev)110{111	mutex_unlock(&fbdev->rqueue_mutex);112}113 114/*115 * ---------------------------------------------------------------------------116 * LCD controller and LCD DMA117 * ---------------------------------------------------------------------------118 */119/*120 * Allocate resources needed for LCD controller and LCD DMA operations. Video121 * memory is allocated from system memory according to the virtual display122 * size, except if a bigger memory size is specified explicitly as a kernel123 * parameter.124 */125static int ctrl_init(struct omapfb_device *fbdev)126{127	int r;128	int i;129 130	/* kernel/module vram parameters override boot tags/board config */131	if (def_vram_cnt) {132		for (i = 0; i < def_vram_cnt; i++)133			fbdev->mem_desc.region[i].size =134				PAGE_ALIGN(def_vram[i]);135		fbdev->mem_desc.region_cnt = i;136	}137 138	if (!fbdev->mem_desc.region_cnt) {139		struct lcd_panel *panel = fbdev->panel;140		int def_size;141		int bpp = panel->bpp;142 143		/* 12 bpp is packed in 16 bits */144		if (bpp == 12)145			bpp = 16;146		def_size = def_vxres * def_vyres * bpp / 8;147		fbdev->mem_desc.region_cnt = 1;148		fbdev->mem_desc.region[0].size = PAGE_ALIGN(def_size);149	}150	r = fbdev->ctrl->init(fbdev, 0, &fbdev->mem_desc);151	if (r < 0) {152		dev_err(fbdev->dev, "controller initialization failed (%d)\n",153			r);154		return r;155	}156 157#ifdef DEBUG158	for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {159		dev_dbg(fbdev->dev, "region%d phys %08x virt %p size=%lu\n",160			 i,161			 fbdev->mem_desc.region[i].paddr,162			 fbdev->mem_desc.region[i].vaddr,163			 fbdev->mem_desc.region[i].size);164	}165#endif166	return 0;167}168 169static void ctrl_cleanup(struct omapfb_device *fbdev)170{171	fbdev->ctrl->cleanup();172}173 174/* Must be called with fbdev->rqueue_mutex held. */175static int ctrl_change_mode(struct fb_info *fbi)176{177	int r;178	unsigned long offset;179	struct omapfb_plane_struct *plane = fbi->par;180	struct omapfb_device *fbdev = plane->fbdev;181	struct fb_var_screeninfo *var = &fbi->var;182 183	offset = var->yoffset * fbi->fix.line_length +184		 var->xoffset * var->bits_per_pixel / 8;185 186	if (fbdev->ctrl->sync)187		fbdev->ctrl->sync();188	r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out,189				 offset, var->xres_virtual,190				 plane->info.pos_x, plane->info.pos_y,191				 var->xres, var->yres, plane->color_mode);192	if (r < 0)193		return r;194 195	if (fbdev->ctrl->set_rotate != NULL) {196		r = fbdev->ctrl->set_rotate(var->rotate);197		if (r < 0)198			return r;199	}200 201	if (fbdev->ctrl->set_scale != NULL)202		r = fbdev->ctrl->set_scale(plane->idx,203				   var->xres, var->yres,204				   plane->info.out_width,205				   plane->info.out_height);206 207	return r;208}209 210/*211 * ---------------------------------------------------------------------------212 * fbdev framework callbacks and the ioctl interface213 * ---------------------------------------------------------------------------214 */215/* Called each time the omapfb device is opened */216static int omapfb_open(struct fb_info *info, int user)217{218	return 0;219}220 221static void omapfb_sync(struct fb_info *info);222 223/* Called when the omapfb device is closed. We make sure that any pending224 * gfx DMA operations are ended, before we return. */225static int omapfb_release(struct fb_info *info, int user)226{227	omapfb_sync(info);228	return 0;229}230 231/* Store a single color palette entry into a pseudo palette or the hardware232 * palette if one is available. For now we support only 16bpp and thus store233 * the entry only to the pseudo palette.234 */235static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,236			u_int blue, u_int transp, int update_hw_pal)237{238	struct omapfb_plane_struct *plane = info->par;239	struct omapfb_device *fbdev = plane->fbdev;240	struct fb_var_screeninfo *var = &info->var;241	int r = 0;242 243	switch (plane->color_mode) {244	case OMAPFB_COLOR_YUV422:245	case OMAPFB_COLOR_YUV420:246	case OMAPFB_COLOR_YUY422:247		r = -EINVAL;248		break;249	case OMAPFB_COLOR_CLUT_8BPP:250	case OMAPFB_COLOR_CLUT_4BPP:251	case OMAPFB_COLOR_CLUT_2BPP:252	case OMAPFB_COLOR_CLUT_1BPP:253		if (fbdev->ctrl->setcolreg)254			r = fbdev->ctrl->setcolreg(regno, red, green, blue,255							transp, update_hw_pal);256		fallthrough;257	case OMAPFB_COLOR_RGB565:258	case OMAPFB_COLOR_RGB444:259		if (r != 0)260			break;261 262		if (regno < 16) {263			u16 pal;264			pal = ((red >> (16 - var->red.length)) <<265					var->red.offset) |266			      ((green >> (16 - var->green.length)) <<267					var->green.offset) |268			      (blue >> (16 - var->blue.length));269			((u32 *)(info->pseudo_palette))[regno] = pal;270		}271		break;272	default:273		BUG();274	}275	return r;276}277 278static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,279			    u_int transp, struct fb_info *info)280{281	return _setcolreg(info, regno, red, green, blue, transp, 1);282}283 284static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)285{286	int count, index, r;287	u16 *red, *green, *blue, *transp;288	u16 trans = 0xffff;289 290	red     = cmap->red;291	green   = cmap->green;292	blue    = cmap->blue;293	transp  = cmap->transp;294	index   = cmap->start;295 296	for (count = 0; count < cmap->len; count++) {297		if (transp)298			trans = *transp++;299		r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,300				count == cmap->len - 1);301		if (r != 0)302			return r;303	}304 305	return 0;306}307 308static int omapfb_update_full_screen(struct fb_info *fbi);309 310static int omapfb_blank(int blank, struct fb_info *fbi)311{312	struct omapfb_plane_struct *plane = fbi->par;313	struct omapfb_device *fbdev = plane->fbdev;314	int do_update = 0;315	int r = 0;316 317	omapfb_rqueue_lock(fbdev);318	switch (blank) {319	case FB_BLANK_UNBLANK:320		if (fbdev->state == OMAPFB_SUSPENDED) {321			if (fbdev->ctrl->resume)322				fbdev->ctrl->resume();323			if (fbdev->panel->enable)324				fbdev->panel->enable(fbdev->panel);325			fbdev->state = OMAPFB_ACTIVE;326			if (fbdev->ctrl->get_update_mode() ==327					OMAPFB_MANUAL_UPDATE)328				do_update = 1;329		}330		break;331	case FB_BLANK_POWERDOWN:332		if (fbdev->state == OMAPFB_ACTIVE) {333			if (fbdev->panel->disable)334				fbdev->panel->disable(fbdev->panel);335			if (fbdev->ctrl->suspend)336				fbdev->ctrl->suspend();337			fbdev->state = OMAPFB_SUSPENDED;338		}339		break;340	default:341		r = -EINVAL;342	}343	omapfb_rqueue_unlock(fbdev);344 345	if (r == 0 && do_update)346		r = omapfb_update_full_screen(fbi);347 348	return r;349}350 351static void omapfb_sync(struct fb_info *fbi)352{353	struct omapfb_plane_struct *plane = fbi->par;354	struct omapfb_device *fbdev = plane->fbdev;355 356	omapfb_rqueue_lock(fbdev);357	if (fbdev->ctrl->sync)358		fbdev->ctrl->sync();359	omapfb_rqueue_unlock(fbdev);360}361 362/*363 * Set fb_info.fix fields and also updates fbdev.364 * When calling this fb_info.var must be set up already.365 */366static void set_fb_fix(struct fb_info *fbi, int from_init)367{368	struct fb_fix_screeninfo *fix = &fbi->fix;369	struct fb_var_screeninfo *var = &fbi->var;370	struct omapfb_plane_struct *plane = fbi->par;371	struct omapfb_mem_region *rg;372	int bpp;373 374	rg = &plane->fbdev->mem_desc.region[plane->idx];375	fbi->screen_base	= rg->vaddr;376 377	if (!from_init) {378		mutex_lock(&fbi->mm_lock);379		fix->smem_start		= rg->paddr;380		fix->smem_len		= rg->size;381		mutex_unlock(&fbi->mm_lock);382	} else {383		fix->smem_start		= rg->paddr;384		fix->smem_len		= rg->size;385	}386 387	fix->type = FB_TYPE_PACKED_PIXELS;388	bpp = var->bits_per_pixel;389	if (var->nonstd)390		fix->visual = FB_VISUAL_PSEUDOCOLOR;391	else switch (var->bits_per_pixel) {392	case 16:393	case 12:394		fix->visual = FB_VISUAL_TRUECOLOR;395		/* 12bpp is stored in 16 bits */396		bpp = 16;397		break;398	case 1:399	case 2:400	case 4:401	case 8:402		fix->visual = FB_VISUAL_PSEUDOCOLOR;403		break;404	}405	fix->accel		= FB_ACCEL_OMAP1610;406	fix->line_length	= var->xres_virtual * bpp / 8;407}408 409static int set_color_mode(struct omapfb_plane_struct *plane,410			  struct fb_var_screeninfo *var)411{412	switch (var->nonstd) {413	case 0:414		break;415	case OMAPFB_COLOR_YUV422:416		var->bits_per_pixel = 16;417		plane->color_mode = var->nonstd;418		return 0;419	case OMAPFB_COLOR_YUV420:420		var->bits_per_pixel = 12;421		plane->color_mode = var->nonstd;422		return 0;423	case OMAPFB_COLOR_YUY422:424		var->bits_per_pixel = 16;425		plane->color_mode = var->nonstd;426		return 0;427	default:428		return -EINVAL;429	}430 431	switch (var->bits_per_pixel) {432	case 1:433		plane->color_mode = OMAPFB_COLOR_CLUT_1BPP;434		return 0;435	case 2:436		plane->color_mode = OMAPFB_COLOR_CLUT_2BPP;437		return 0;438	case 4:439		plane->color_mode = OMAPFB_COLOR_CLUT_4BPP;440		return 0;441	case 8:442		plane->color_mode = OMAPFB_COLOR_CLUT_8BPP;443		return 0;444	case 12:445		var->bits_per_pixel = 16;446		fallthrough;447	case 16:448		if (plane->fbdev->panel->bpp == 12)449			plane->color_mode = OMAPFB_COLOR_RGB444;450		else451			plane->color_mode = OMAPFB_COLOR_RGB565;452		return 0;453	default:454		return -EINVAL;455	}456}457 458/*459 * Check the values in var against our capabilities and in case of out of460 * bound values try to adjust them.461 */462static int set_fb_var(struct fb_info *fbi,463		      struct fb_var_screeninfo *var)464{465	int		bpp;466	unsigned long	max_frame_size;467	unsigned long	line_size;468	int		xres_min, xres_max;469	int		yres_min, yres_max;470	struct omapfb_plane_struct *plane = fbi->par;471	struct omapfb_device *fbdev = plane->fbdev;472	struct lcd_panel *panel = fbdev->panel;473 474	if (set_color_mode(plane, var) < 0)475		return -EINVAL;476 477	bpp = var->bits_per_pixel;478	if (plane->color_mode == OMAPFB_COLOR_RGB444)479		bpp = 16;480 481	switch (var->rotate) {482	case 0:483	case 180:484		xres_min = OMAPFB_PLANE_XRES_MIN;485		xres_max = panel->x_res;486		yres_min = OMAPFB_PLANE_YRES_MIN;487		yres_max = panel->y_res;488		if (cpu_is_omap15xx()) {489			var->xres = panel->x_res;490			var->yres = panel->y_res;491		}492		break;493	case 90:494	case 270:495		xres_min = OMAPFB_PLANE_YRES_MIN;496		xres_max = panel->y_res;497		yres_min = OMAPFB_PLANE_XRES_MIN;498		yres_max = panel->x_res;499		if (cpu_is_omap15xx()) {500			var->xres = panel->y_res;501			var->yres = panel->x_res;502		}503		break;504	default:505		return -EINVAL;506	}507 508	if (var->xres < xres_min)509		var->xres = xres_min;510	if (var->yres < yres_min)511		var->yres = yres_min;512	if (var->xres > xres_max)513		var->xres = xres_max;514	if (var->yres > yres_max)515		var->yres = yres_max;516 517	if (var->xres_virtual < var->xres)518		var->xres_virtual = var->xres;519	if (var->yres_virtual < var->yres)520		var->yres_virtual = var->yres;521	max_frame_size = fbdev->mem_desc.region[plane->idx].size;522	line_size = var->xres_virtual * bpp / 8;523	if (line_size * var->yres_virtual > max_frame_size) {524		/* Try to keep yres_virtual first */525		line_size = max_frame_size / var->yres_virtual;526		var->xres_virtual = line_size * 8 / bpp;527		if (var->xres_virtual < var->xres) {528			/* Still doesn't fit. Shrink yres_virtual too */529			var->xres_virtual = var->xres;530			line_size = var->xres * bpp / 8;531			var->yres_virtual = max_frame_size / line_size;532		}533		/* Recheck this, as the virtual size changed. */534		if (var->xres_virtual < var->xres)535			var->xres = var->xres_virtual;536		if (var->yres_virtual < var->yres)537			var->yres = var->yres_virtual;538		if (var->xres < xres_min || var->yres < yres_min)539			return -EINVAL;540	}541	if (var->xres + var->xoffset > var->xres_virtual)542		var->xoffset = var->xres_virtual - var->xres;543	if (var->yres + var->yoffset > var->yres_virtual)544		var->yoffset = var->yres_virtual - var->yres;545 546	if (plane->color_mode == OMAPFB_COLOR_RGB444) {547		var->red.offset		= 8;548		var->red.length		= 4;549		var->red.msb_right	= 0;550		var->green.offset	= 4;551		var->green.length	= 4;552		var->green.msb_right	= 0;553		var->blue.offset	= 0;554		var->blue.length	= 4;555		var->blue.msb_right	= 0;556	} else {557		var->red.offset		= 11;558		var->red.length		= 5;559		var->red.msb_right	= 0;560		var->green.offset	= 5;561		var->green.length	= 6;562		var->green.msb_right	= 0;563		var->blue.offset	= 0;564		var->blue.length	= 5;565		var->blue.msb_right	= 0;566	}567 568	var->height		= -1;569	var->width		= -1;570	var->grayscale		= 0;571 572	/* pixclock in ps, the rest in pixclock */573	var->pixclock		= 10000000 / (panel->pixel_clock / 100);574	var->left_margin	= panel->hfp;575	var->right_margin	= panel->hbp;576	var->upper_margin	= panel->vfp;577	var->lower_margin	= panel->vbp;578	var->hsync_len		= panel->hsw;579	var->vsync_len		= panel->vsw;580 581	/* TODO: get these from panel->config */582	var->vmode		= FB_VMODE_NONINTERLACED;583	var->sync		= 0;584 585	return 0;586}587 588 589/*590 * Set new x,y offsets in the virtual display for the visible area and switch591 * to the new mode.592 */593static int omapfb_pan_display(struct fb_var_screeninfo *var,594			       struct fb_info *fbi)595{596	struct omapfb_plane_struct *plane = fbi->par;597	struct omapfb_device *fbdev = plane->fbdev;598	int r = 0;599 600	omapfb_rqueue_lock(fbdev);601	if (var->xoffset != fbi->var.xoffset ||602	    var->yoffset != fbi->var.yoffset) {603		struct fb_var_screeninfo *new_var = &fbdev->new_var;604 605		memcpy(new_var, &fbi->var, sizeof(*new_var));606		new_var->xoffset = var->xoffset;607		new_var->yoffset = var->yoffset;608		if (set_fb_var(fbi, new_var))609			r = -EINVAL;610		else {611			memcpy(&fbi->var, new_var, sizeof(*new_var));612			ctrl_change_mode(fbi);613		}614	}615	omapfb_rqueue_unlock(fbdev);616 617	return r;618}619 620/* Set mirror to vertical axis and switch to the new mode. */621static int omapfb_mirror(struct fb_info *fbi, int mirror)622{623	struct omapfb_plane_struct *plane = fbi->par;624	struct omapfb_device *fbdev = plane->fbdev;625	int r = 0;626 627	omapfb_rqueue_lock(fbdev);628	mirror = mirror ? 1 : 0;629	if (cpu_is_omap15xx())630		r = -EINVAL;631	else if (mirror != plane->info.mirror) {632		plane->info.mirror = mirror;633		r = ctrl_change_mode(fbi);634	}635	omapfb_rqueue_unlock(fbdev);636 637	return r;638}639 640/*641 * Check values in var, try to adjust them in case of out of bound values if642 * possible, or return error.643 */644static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)645{646	struct omapfb_plane_struct *plane = fbi->par;647	struct omapfb_device *fbdev = plane->fbdev;648	int r;649 650	omapfb_rqueue_lock(fbdev);651	if (fbdev->ctrl->sync != NULL)652		fbdev->ctrl->sync();653	r = set_fb_var(fbi, var);654	omapfb_rqueue_unlock(fbdev);655 656	return r;657}658 659/*660 * Switch to a new mode. The parameters for it has been check already by661 * omapfb_check_var.662 */663static int omapfb_set_par(struct fb_info *fbi)664{665	struct omapfb_plane_struct *plane = fbi->par;666	struct omapfb_device *fbdev = plane->fbdev;667	int r = 0;668 669	omapfb_rqueue_lock(fbdev);670	set_fb_fix(fbi, 0);671	r = ctrl_change_mode(fbi);672	omapfb_rqueue_unlock(fbdev);673 674	return r;675}676 677static int omapfb_update_window_async(struct fb_info *fbi,678				struct omapfb_update_window *win,679				void (*callback)(void *),680				void *callback_data)681{682	int xres, yres;683	struct omapfb_plane_struct *plane = fbi->par;684	struct omapfb_device *fbdev = plane->fbdev;685	struct fb_var_screeninfo *var = &fbi->var;686 687	switch (var->rotate) {688	case 0:689	case 180:690		xres = fbdev->panel->x_res;691		yres = fbdev->panel->y_res;692		break;693	case 90:694	case 270:695		xres = fbdev->panel->y_res;696		yres = fbdev->panel->x_res;697		break;698	default:699		return -EINVAL;700	}701 702	if (win->x >= xres || win->y >= yres ||703	    win->out_x > xres || win->out_y > yres)704		return -EINVAL;705 706	if (!fbdev->ctrl->update_window ||707	    fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)708		return -ENODEV;709 710	if (win->x + win->width > xres)711		win->width = xres - win->x;712	if (win->y + win->height > yres)713		win->height = yres - win->y;714	if (win->out_x + win->out_width > xres)715		win->out_width = xres - win->out_x;716	if (win->out_y + win->out_height > yres)717		win->out_height = yres - win->out_y;718	if (!win->width || !win->height || !win->out_width || !win->out_height)719		return 0;720 721	return fbdev->ctrl->update_window(fbi, win, callback, callback_data);722}723 724static int omapfb_update_win(struct fb_info *fbi,725				struct omapfb_update_window *win)726{727	struct omapfb_plane_struct *plane = fbi->par;728	int ret;729 730	omapfb_rqueue_lock(plane->fbdev);731	ret = omapfb_update_window_async(fbi, win, NULL, NULL);732	omapfb_rqueue_unlock(plane->fbdev);733 734	return ret;735}736 737static int omapfb_update_full_screen(struct fb_info *fbi)738{739	struct omapfb_plane_struct *plane = fbi->par;740	struct omapfb_device *fbdev = plane->fbdev;741	struct omapfb_update_window win;742	int r;743 744	if (!fbdev->ctrl->update_window ||745	    fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)746		return -ENODEV;747 748	win.x = 0;749	win.y = 0;750	win.width = fbi->var.xres;751	win.height = fbi->var.yres;752	win.out_x = 0;753	win.out_y = 0;754	win.out_width = fbi->var.xres;755	win.out_height = fbi->var.yres;756	win.format = 0;757 758	omapfb_rqueue_lock(fbdev);759	r = fbdev->ctrl->update_window(fbi, &win, NULL, NULL);760	omapfb_rqueue_unlock(fbdev);761 762	return r;763}764 765static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)766{767	struct omapfb_plane_struct *plane = fbi->par;768	struct omapfb_device *fbdev = plane->fbdev;769	struct lcd_panel *panel = fbdev->panel;770	struct omapfb_plane_info old_info;771	int r = 0;772 773	if (pi->pos_x + pi->out_width > panel->x_res ||774	    pi->pos_y + pi->out_height > panel->y_res)775		return -EINVAL;776 777	omapfb_rqueue_lock(fbdev);778	if (pi->enabled && !fbdev->mem_desc.region[plane->idx].size) {779		/*780		 * This plane's memory was freed, can't enable it781		 * until it's reallocated.782		 */783		r = -EINVAL;784		goto out;785	}786	old_info = plane->info;787	plane->info = *pi;788	if (pi->enabled) {789		r = ctrl_change_mode(fbi);790		if (r < 0) {791			plane->info = old_info;792			goto out;793		}794	}795	r = fbdev->ctrl->enable_plane(plane->idx, pi->enabled);796	if (r < 0) {797		plane->info = old_info;798		goto out;799	}800out:801	omapfb_rqueue_unlock(fbdev);802	return r;803}804 805static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)806{807	struct omapfb_plane_struct *plane = fbi->par;808 809	*pi = plane->info;810	return 0;811}812 813static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)814{815	struct omapfb_plane_struct *plane = fbi->par;816	struct omapfb_device *fbdev = plane->fbdev;817	struct omapfb_mem_region *rg = &fbdev->mem_desc.region[plane->idx];818	size_t size;819	int r = 0;820 821	if (fbdev->ctrl->setup_mem == NULL)822		return -ENODEV;823	if (mi->type != OMAPFB_MEMTYPE_SDRAM)824		return -EINVAL;825 826	size = PAGE_ALIGN(mi->size);827	omapfb_rqueue_lock(fbdev);828	if (plane->info.enabled) {829		r = -EBUSY;830		goto out;831	}832	if (rg->size != size || rg->type != mi->type) {833		struct fb_var_screeninfo *new_var = &fbdev->new_var;834		unsigned long old_size = rg->size;835		u8	      old_type = rg->type;836		unsigned long paddr;837 838		rg->size = size;839		rg->type = mi->type;840		/*841		 * size == 0 is a special case, for which we842		 * don't check / adjust the screen parameters.843		 * This isn't a problem since the plane can't844		 * be reenabled unless its size is > 0.845		 */846		if (old_size != size && size) {847			if (size) {848				memcpy(new_var, &fbi->var, sizeof(*new_var));849				r = set_fb_var(fbi, new_var);850				if (r < 0)851					goto out;852			}853		}854 855		if (fbdev->ctrl->sync)856			fbdev->ctrl->sync();857		r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr);858		if (r < 0) {859			/* Revert changes. */860			rg->size = old_size;861			rg->type = old_type;862			goto out;863		}864		rg->paddr = paddr;865 866		if (old_size != size) {867			if (size) {868				memcpy(&fbi->var, new_var, sizeof(fbi->var));869				set_fb_fix(fbi, 0);870			} else {871				/*872				 * Set these explicitly to indicate that the873				 * plane memory is dealloce'd, the other874				 * screen parameters in var / fix are invalid.875				 */876				mutex_lock(&fbi->mm_lock);877				fbi->fix.smem_start = 0;878				fbi->fix.smem_len = 0;879				mutex_unlock(&fbi->mm_lock);880			}881		}882	}883out:884	omapfb_rqueue_unlock(fbdev);885 886	return r;887}888 889static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)890{891	struct omapfb_plane_struct *plane = fbi->par;892	struct omapfb_device *fbdev = plane->fbdev;893	struct omapfb_mem_region *rg;894 895	rg = &fbdev->mem_desc.region[plane->idx];896	memset(mi, 0, sizeof(*mi));897	mi->size = rg->size;898	mi->type = rg->type;899 900	return 0;901}902 903static int omapfb_set_color_key(struct omapfb_device *fbdev,904				struct omapfb_color_key *ck)905{906	int r;907 908	if (!fbdev->ctrl->set_color_key)909		return -ENODEV;910 911	omapfb_rqueue_lock(fbdev);912	r = fbdev->ctrl->set_color_key(ck);913	omapfb_rqueue_unlock(fbdev);914 915	return r;916}917 918static int omapfb_get_color_key(struct omapfb_device *fbdev,919				struct omapfb_color_key *ck)920{921	int r;922 923	if (!fbdev->ctrl->get_color_key)924		return -ENODEV;925 926	omapfb_rqueue_lock(fbdev);927	r = fbdev->ctrl->get_color_key(ck);928	omapfb_rqueue_unlock(fbdev);929 930	return r;931}932 933static struct blocking_notifier_head omapfb_client_list[OMAPFB_PLANE_NUM];934static int notifier_inited;935 936static void omapfb_init_notifier(void)937{938	int i;939 940	for (i = 0; i < OMAPFB_PLANE_NUM; i++)941		BLOCKING_INIT_NOTIFIER_HEAD(&omapfb_client_list[i]);942}943 944int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb,945				omapfb_notifier_callback_t callback,946				void *callback_data)947{948	int r;949 950	if ((unsigned)omapfb_nb->plane_idx >= OMAPFB_PLANE_NUM)951		return -EINVAL;952 953	if (!notifier_inited) {954		omapfb_init_notifier();955		notifier_inited = 1;956	}957 958	omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *,959					unsigned long, void *))callback;960	omapfb_nb->data = callback_data;961	r = blocking_notifier_chain_register(962				&omapfb_client_list[omapfb_nb->plane_idx],963				&omapfb_nb->nb);964	if (r)965		return r;966	if (omapfb_dev != NULL &&967	    omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) {968		omapfb_dev->ctrl->bind_client(omapfb_nb);969	}970 971	return 0;972}973EXPORT_SYMBOL(omapfb_register_client);974 975int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb)976{977	return blocking_notifier_chain_unregister(978		&omapfb_client_list[omapfb_nb->plane_idx], &omapfb_nb->nb);979}980EXPORT_SYMBOL(omapfb_unregister_client);981 982void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event)983{984	int i;985 986	if (!notifier_inited)987		/* no client registered yet */988		return;989 990	for (i = 0; i < OMAPFB_PLANE_NUM; i++)991		blocking_notifier_call_chain(&omapfb_client_list[i], event,992				    fbdev->fb_info[i]);993}994EXPORT_SYMBOL(omapfb_notify_clients);995 996static int omapfb_set_update_mode(struct omapfb_device *fbdev,997				   enum omapfb_update_mode mode)998{999	int r;1000 1001	omapfb_rqueue_lock(fbdev);1002	r = fbdev->ctrl->set_update_mode(mode);1003	omapfb_rqueue_unlock(fbdev);1004 1005	return r;1006}1007 1008static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)1009{1010	int r;1011 1012	omapfb_rqueue_lock(fbdev);1013	r = fbdev->ctrl->get_update_mode();1014	omapfb_rqueue_unlock(fbdev);1015 1016	return r;1017}1018 1019static void omapfb_get_caps(struct omapfb_device *fbdev, int plane,1020				     struct omapfb_caps *caps)1021{1022	memset(caps, 0, sizeof(*caps));1023	fbdev->ctrl->get_caps(plane, caps);1024	if (fbdev->panel->get_caps)1025		caps->ctrl |= fbdev->panel->get_caps(fbdev->panel);1026}1027 1028/* For lcd testing */1029void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval)1030{1031	omapfb_rqueue_lock(fbdev);1032	*(u16 *)fbdev->mem_desc.region[0].vaddr = pixval;1033	if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) {1034		struct omapfb_update_window win;1035 1036		memset(&win, 0, sizeof(win));1037		win.width = 2;1038		win.height = 2;1039		win.out_width = 2;1040		win.out_height = 2;1041		fbdev->ctrl->update_window(fbdev->fb_info[0], &win, NULL, NULL);1042	}1043	omapfb_rqueue_unlock(fbdev);1044}1045EXPORT_SYMBOL(omapfb_write_first_pixel);1046 1047/*1048 * Ioctl interface. Part of the kernel mode frame buffer API is duplicated1049 * here to be accessible by user mode code.1050 */1051static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,1052			unsigned long arg)1053{1054	struct omapfb_plane_struct *plane = fbi->par;1055	struct omapfb_device	*fbdev = plane->fbdev;1056	const struct fb_ops *ops = fbi->fbops;1057	union {1058		struct omapfb_update_window	update_window;1059		struct omapfb_plane_info	plane_info;1060		struct omapfb_mem_info		mem_info;1061		struct omapfb_color_key		color_key;1062		enum omapfb_update_mode		update_mode;1063		struct omapfb_caps		caps;1064		unsigned int		mirror;1065		int			plane_out;1066		int			enable_plane;1067	} p;1068	int r = 0;1069 1070	BUG_ON(!ops);1071	switch (cmd) {1072	case OMAPFB_MIRROR:1073		if (get_user(p.mirror, (int __user *)arg))1074			r = -EFAULT;1075		else1076			omapfb_mirror(fbi, p.mirror);1077		break;1078	case OMAPFB_SYNC_GFX:1079		omapfb_sync(fbi);1080		break;1081	case OMAPFB_VSYNC:1082		break;1083	case OMAPFB_SET_UPDATE_MODE:1084		if (get_user(p.update_mode, (int __user *)arg))1085			r = -EFAULT;1086		else1087			r = omapfb_set_update_mode(fbdev, p.update_mode);1088		break;1089	case OMAPFB_GET_UPDATE_MODE:1090		p.update_mode = omapfb_get_update_mode(fbdev);1091		if (put_user(p.update_mode,1092					(enum omapfb_update_mode __user *)arg))1093			r = -EFAULT;1094		break;1095	case OMAPFB_UPDATE_WINDOW_OLD:1096		if (copy_from_user(&p.update_window, (void __user *)arg,1097				   sizeof(struct omapfb_update_window_old)))1098			r = -EFAULT;1099		else {1100			struct omapfb_update_window *u = &p.update_window;1101			u->out_x = u->x;1102			u->out_y = u->y;1103			u->out_width = u->width;1104			u->out_height = u->height;1105			memset(u->reserved, 0, sizeof(u->reserved));1106			r = omapfb_update_win(fbi, u);1107		}1108		break;1109	case OMAPFB_UPDATE_WINDOW:1110		if (copy_from_user(&p.update_window, (void __user *)arg,1111				   sizeof(p.update_window)))1112			r = -EFAULT;1113		else1114			r = omapfb_update_win(fbi, &p.update_window);1115		break;1116	case OMAPFB_SETUP_PLANE:1117		if (copy_from_user(&p.plane_info, (void __user *)arg,1118				   sizeof(p.plane_info)))1119			r = -EFAULT;1120		else1121			r = omapfb_setup_plane(fbi, &p.plane_info);1122		break;1123	case OMAPFB_QUERY_PLANE:1124		if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0)1125			break;1126		if (copy_to_user((void __user *)arg, &p.plane_info,1127				   sizeof(p.plane_info)))1128			r = -EFAULT;1129		break;1130	case OMAPFB_SETUP_MEM:1131		if (copy_from_user(&p.mem_info, (void __user *)arg,1132				   sizeof(p.mem_info)))1133			r = -EFAULT;1134		else1135			r = omapfb_setup_mem(fbi, &p.mem_info);1136		break;1137	case OMAPFB_QUERY_MEM:1138		if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0)1139			break;1140		if (copy_to_user((void __user *)arg, &p.mem_info,1141				   sizeof(p.mem_info)))1142			r = -EFAULT;1143		break;1144	case OMAPFB_SET_COLOR_KEY:1145		if (copy_from_user(&p.color_key, (void __user *)arg,1146				   sizeof(p.color_key)))1147			r = -EFAULT;1148		else1149			r = omapfb_set_color_key(fbdev, &p.color_key);1150		break;1151	case OMAPFB_GET_COLOR_KEY:1152		if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0)1153			break;1154		if (copy_to_user((void __user *)arg, &p.color_key,1155				 sizeof(p.color_key)))1156			r = -EFAULT;1157		break;1158	case OMAPFB_GET_CAPS:1159		omapfb_get_caps(fbdev, plane->idx, &p.caps);1160		if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))1161			r = -EFAULT;1162		break;1163	case OMAPFB_LCD_TEST:1164		{1165			int test_num;1166 1167			if (get_user(test_num, (int __user *)arg)) {1168				r = -EFAULT;1169				break;1170			}1171			if (!fbdev->panel->run_test) {1172				r = -EINVAL;1173				break;1174			}1175			r = fbdev->panel->run_test(fbdev->panel, test_num);1176			break;1177		}1178	case OMAPFB_CTRL_TEST:1179		{1180			int test_num;1181 1182			if (get_user(test_num, (int __user *)arg)) {1183				r = -EFAULT;1184				break;1185			}1186			if (!fbdev->ctrl->run_test) {1187				r = -EINVAL;1188				break;1189			}1190			r = fbdev->ctrl->run_test(test_num);1191			break;1192		}1193	default:1194		r = -EINVAL;1195	}1196 1197	return r;1198}1199 1200static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma)1201{1202	struct omapfb_plane_struct *plane = info->par;1203	struct omapfb_device *fbdev = plane->fbdev;1204	int r;1205 1206	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);1207 1208	omapfb_rqueue_lock(fbdev);1209	r = fbdev->ctrl->mmap(info, vma);1210	omapfb_rqueue_unlock(fbdev);1211 1212	return r;1213}1214 1215/*1216 * Callback table for the frame buffer framework. Some of these pointers1217 * will be changed according to the current setting of fb_info->accel_flags.1218 */1219static struct fb_ops omapfb_ops = {1220	.owner		= THIS_MODULE,1221	FB_DEFAULT_IOMEM_OPS,1222	.fb_open        = omapfb_open,1223	.fb_release     = omapfb_release,1224	.fb_setcolreg	= omapfb_setcolreg,1225	.fb_setcmap	= omapfb_setcmap,1226	.fb_blank       = omapfb_blank,1227	.fb_ioctl	= omapfb_ioctl,1228	.fb_check_var	= omapfb_check_var,1229	.fb_set_par	= omapfb_set_par,1230	.fb_pan_display = omapfb_pan_display,1231};1232 1233/*1234 * ---------------------------------------------------------------------------1235 * Sysfs interface1236 * ---------------------------------------------------------------------------1237 */1238/* omapfbX sysfs entries */1239static ssize_t omapfb_show_caps_num(struct device *dev,1240				    struct device_attribute *attr, char *buf)1241{1242	struct omapfb_device *fbdev = dev_get_drvdata(dev);1243	int plane;1244	size_t size = 0;1245	struct omapfb_caps caps;1246 1247	plane = 0;1248	while (plane < OMAPFB_PLANE_NUM) {1249		omapfb_get_caps(fbdev, plane, &caps);1250		size += sysfs_emit_at(buf, size,1251			"plane#%d %#010x %#010x %#010x\n",1252			plane, caps.ctrl, caps.plane_color, caps.wnd_color);1253		plane++;1254	}1255	return size;1256}1257 1258static ssize_t omapfb_show_caps_text(struct device *dev,1259				     struct device_attribute *attr, char *buf)1260{1261	struct omapfb_device *fbdev = dev_get_drvdata(dev);1262	int i;1263	struct omapfb_caps caps;1264	int plane;1265	size_t size = 0;1266 1267	plane = 0;1268	while (plane < OMAPFB_PLANE_NUM) {1269		omapfb_get_caps(fbdev, plane, &caps);1270		size += sysfs_emit_at(buf, size, "plane#%d:\n", plane);1271		for (i = 0; i < ARRAY_SIZE(ctrl_caps); i++) {1272			if (ctrl_caps[i].flag & caps.ctrl)1273				size += sysfs_emit_at(buf, size,1274					" %s\n", ctrl_caps[i].name);1275		}1276		size += sysfs_emit_at(buf, size, " plane colors:\n");1277		for (i = 0; i < ARRAY_SIZE(color_caps); i++) {1278			if (color_caps[i].flag & caps.plane_color)1279				size += sysfs_emit_at(buf, size,1280					"  %s\n", color_caps[i].name);1281		}1282		size += sysfs_emit_at(buf, size, " window colors:\n");1283		for (i = 0; i < ARRAY_SIZE(color_caps); i++) {1284			if (color_caps[i].flag & caps.wnd_color)1285				size += sysfs_emit_at(buf, size,1286					"  %s\n", color_caps[i].name);1287		}1288 1289		plane++;1290	}1291	return size;1292}1293 1294static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL);1295static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL);1296 1297/* panel sysfs entries */1298static ssize_t omapfb_show_panel_name(struct device *dev,1299				      struct device_attribute *attr, char *buf)1300{1301	struct omapfb_device *fbdev = dev_get_drvdata(dev);1302 1303	return sysfs_emit(buf, "%s\n", fbdev->panel->name);1304}1305 1306static ssize_t omapfb_show_bklight_level(struct device *dev,1307					 struct device_attribute *attr,1308					 char *buf)1309{1310	struct omapfb_device *fbdev = dev_get_drvdata(dev);1311	int r;1312 1313	if (fbdev->panel->get_bklight_level) {1314		r = sysfs_emit(buf, "%d\n",1315			       fbdev->panel->get_bklight_level(fbdev->panel));1316	} else1317		r = -ENODEV;1318	return r;1319}1320 1321static ssize_t omapfb_store_bklight_level(struct device *dev,1322					  struct device_attribute *attr,1323					  const char *buf, size_t size)1324{1325	struct omapfb_device *fbdev = dev_get_drvdata(dev);1326	int r;1327 1328	if (fbdev->panel->set_bklight_level) {1329		unsigned int level;1330 1331		if (sscanf(buf, "%10d", &level) == 1) {1332			r = fbdev->panel->set_bklight_level(fbdev->panel,1333							    level);1334		} else1335			r = -EINVAL;1336	} else1337		r = -ENODEV;1338	return r ? r : size;1339}1340 1341static ssize_t omapfb_show_bklight_max(struct device *dev,1342				       struct device_attribute *attr, char *buf)1343{1344	struct omapfb_device *fbdev = dev_get_drvdata(dev);1345	int r;1346 1347	if (fbdev->panel->get_bklight_level) {1348		r = sysfs_emit(buf, "%d\n",1349			       fbdev->panel->get_bklight_max(fbdev->panel));1350	} else1351		r = -ENODEV;1352	return r;1353}1354 1355static struct device_attribute dev_attr_panel_name =1356	__ATTR(name, 0444, omapfb_show_panel_name, NULL);1357static DEVICE_ATTR(backlight_level, 0664,1358		   omapfb_show_bklight_level, omapfb_store_bklight_level);1359static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL);1360 1361static struct attribute *panel_attrs[] = {1362	&dev_attr_panel_name.attr,1363	&dev_attr_backlight_level.attr,1364	&dev_attr_backlight_max.attr,1365	NULL,1366};1367 1368static const struct attribute_group panel_attr_grp = {1369	.name  = "panel",1370	.attrs = panel_attrs,1371};1372 1373/* ctrl sysfs entries */1374static ssize_t omapfb_show_ctrl_name(struct device *dev,1375				     struct device_attribute *attr, char *buf)1376{1377	struct omapfb_device *fbdev = dev_get_drvdata(dev);1378 1379	return sysfs_emit(buf, "%s\n", fbdev->ctrl->name);1380}1381 1382static struct device_attribute dev_attr_ctrl_name =1383	__ATTR(name, 0444, omapfb_show_ctrl_name, NULL);1384 1385static struct attribute *ctrl_attrs[] = {1386	&dev_attr_ctrl_name.attr,1387	NULL,1388};1389 1390static const struct attribute_group ctrl_attr_grp = {1391	.name  = "ctrl",1392	.attrs = ctrl_attrs,1393};1394 1395static int omapfb_register_sysfs(struct omapfb_device *fbdev)1396{1397	int r;1398 1399	if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num)))1400		goto fail0;1401 1402	if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text)))1403		goto fail1;1404 1405	if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp)))1406		goto fail2;1407 1408	if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp)))1409		goto fail3;1410 1411	return 0;1412fail3:1413	sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);1414fail2:1415	device_remove_file(fbdev->dev, &dev_attr_caps_text);1416fail1:1417	device_remove_file(fbdev->dev, &dev_attr_caps_num);1418fail0:1419	dev_err(fbdev->dev, "unable to register sysfs interface\n");1420	return r;1421}1422 1423static void omapfb_unregister_sysfs(struct omapfb_device *fbdev)1424{1425	sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp);1426	sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);1427	device_remove_file(fbdev->dev, &dev_attr_caps_num);1428	device_remove_file(fbdev->dev, &dev_attr_caps_text);1429}1430 1431/*1432 * ---------------------------------------------------------------------------1433 * LDM callbacks1434 * ---------------------------------------------------------------------------1435 */1436/* Initialize system fb_info object and set the default video mode.1437 * The frame buffer memory already allocated by lcddma_init1438 */1439static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info)1440{1441	struct fb_var_screeninfo	*var = &info->var;1442	struct fb_fix_screeninfo	*fix = &info->fix;1443	int				r = 0;1444 1445	info->fbops = &omapfb_ops;1446 1447	strscpy(fix->id, MODULE_NAME, sizeof(fix->id));1448 1449	info->pseudo_palette = fbdev->pseudo_palette;1450 1451	var->accel_flags  = def_accel ? FB_ACCELF_TEXT : 0;1452	var->xres = def_vxres;1453	var->yres = def_vyres;1454	var->xres_virtual = def_vxres;1455	var->yres_virtual = def_vyres;1456	var->rotate	  = def_rotate;1457	var->bits_per_pixel = fbdev->panel->bpp;1458 1459	set_fb_var(info, var);1460	set_fb_fix(info, 1);1461 1462	r = fb_alloc_cmap(&info->cmap, 16, 0);1463	if (r != 0)1464		dev_err(fbdev->dev, "unable to allocate color map memory\n");1465 1466	return r;1467}1468 1469/* Release the fb_info object */1470static void fbinfo_cleanup(struct omapfb_device *fbdev, struct fb_info *fbi)1471{1472	fb_dealloc_cmap(&fbi->cmap);1473}1474 1475static void planes_cleanup(struct omapfb_device *fbdev)1476{1477	int i;1478 1479	for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {1480		if (fbdev->fb_info[i] == NULL)1481			break;1482		fbinfo_cleanup(fbdev, fbdev->fb_info[i]);1483		framebuffer_release(fbdev->fb_info[i]);1484	}1485}1486 1487static int planes_init(struct omapfb_device *fbdev)1488{1489	struct fb_info *fbi;1490	int i;1491	int r;1492 1493	for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {1494		struct omapfb_plane_struct *plane;1495		fbi = framebuffer_alloc(sizeof(struct omapfb_plane_struct),1496					fbdev->dev);1497		if (fbi == NULL) {1498			planes_cleanup(fbdev);1499			return -ENOMEM;1500		}1501		plane = fbi->par;1502		plane->idx = i;1503		plane->fbdev = fbdev;1504		plane->info.mirror = def_mirror;1505		fbdev->fb_info[i] = fbi;1506 1507		if ((r = fbinfo_init(fbdev, fbi)) < 0) {1508			framebuffer_release(fbi);1509			planes_cleanup(fbdev);1510			return r;1511		}1512		plane->info.out_width = fbi->var.xres;1513		plane->info.out_height = fbi->var.yres;1514	}1515	return 0;1516}1517 1518/*1519 * Free driver resources. Can be called to rollback an aborted initialization1520 * sequence.1521 */1522static void omapfb_free_resources(struct omapfb_device *fbdev, int state)1523{1524	int i;1525 1526	switch (state) {1527	case OMAPFB_ACTIVE:1528		for (i = 0; i < fbdev->mem_desc.region_cnt; i++)1529			unregister_framebuffer(fbdev->fb_info[i]);1530		fallthrough;1531	case 7:1532		omapfb_unregister_sysfs(fbdev);1533		fallthrough;1534	case 6:1535		if (fbdev->panel->disable)1536			fbdev->panel->disable(fbdev->panel);1537		fallthrough;1538	case 5:1539		omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED);1540		fallthrough;1541	case 4:1542		planes_cleanup(fbdev);1543		fallthrough;1544	case 3:1545		ctrl_cleanup(fbdev);1546		fallthrough;1547	case 2:1548		if (fbdev->panel->cleanup)1549			fbdev->panel->cleanup(fbdev->panel);1550		fallthrough;1551	case 1:1552		dev_set_drvdata(fbdev->dev, NULL);1553		kfree(fbdev);1554		break;1555	case 0:1556		/* nothing to free */1557		break;1558	default:1559		BUG();1560	}1561}1562 1563static int omapfb_find_ctrl(struct omapfb_device *fbdev)1564{1565	struct omapfb_platform_data *conf;1566	char name[17];1567	int i;1568 1569	conf = dev_get_platdata(fbdev->dev);1570 1571	fbdev->ctrl = NULL;1572 1573	strscpy(name, conf->lcd.ctrl_name, sizeof(name));1574 1575	if (strcmp(name, "internal") == 0) {1576		fbdev->ctrl = fbdev->int_ctrl;1577		return 0;1578	}1579 1580	for (i = 0; i < ARRAY_SIZE(ctrls); i++) {1581		dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name);1582		if (strcmp(ctrls[i]->name, name) == 0) {1583			fbdev->ctrl = ctrls[i];1584			break;1585		}1586	}1587 1588	if (fbdev->ctrl == NULL) {1589		dev_dbg(fbdev->dev, "ctrl %s not supported\n", name);1590		return -1;1591	}1592 1593	return 0;1594}1595 1596/*1597 * Called by LDM binding to probe and attach a new device.1598 * Initialization sequence:1599 *   1. allocate system omapfb_device structure1600 *   2. select controller type according to platform configuration1601 *      init LCD panel1602 *   3. init LCD controller and LCD DMA1603 *   4. init system fb_info structure for all planes1604 *   5. setup video mode for first plane and enable it1605 *   6. enable LCD panel1606 *   7. register sysfs attributes1607 *   OMAPFB_ACTIVE: register system fb_info structure for all planes1608 */1609static int omapfb_do_probe(struct platform_device *pdev,1610				struct lcd_panel *panel)1611{1612	struct omapfb_device	*fbdev = NULL;1613	int			init_state;1614	unsigned long		phz, hhz, vhz;1615	unsigned long		vram;1616	int			i;1617	int			r = 0;1618 1619	init_state = 0;1620 1621	if (pdev->num_resources != 2) {1622		dev_err(&pdev->dev, "probed for an unknown device\n");1623		r = -ENODEV;1624		goto cleanup;1625	}1626 1627	if (dev_get_platdata(&pdev->dev) == NULL) {1628		dev_err(&pdev->dev, "missing platform data\n");1629		r = -ENOENT;1630		goto cleanup;1631	}1632 1633	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);1634	if (fbdev == NULL) {1635		dev_err(&pdev->dev,1636			"unable to allocate memory for device info\n");1637		r = -ENOMEM;1638		goto cleanup;1639	}1640 1641	r = platform_get_irq(pdev, 0);1642	if (r < 0)1643		goto cleanup;1644	fbdev->int_irq = r;1645 1646	r = platform_get_irq(pdev, 1);1647	if (r < 0)1648		goto cleanup;1649	fbdev->ext_irq = r;1650 1651	init_state++;1652 1653	fbdev->dev = &pdev->dev;1654	fbdev->panel = panel;1655	fbdev->dssdev = &omapdss_device;1656	platform_set_drvdata(pdev, fbdev);1657 1658	mutex_init(&fbdev->rqueue_mutex);1659 1660	fbdev->int_ctrl = &omap1_int_ctrl;1661#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL1662	fbdev->ext_if = &omap1_ext_if;1663#endif1664	if (omapfb_find_ctrl(fbdev) < 0) {1665		dev_err(fbdev->dev,1666			"LCD controller not found, board not supported\n");1667		r = -ENODEV;1668		goto cleanup;1669	}1670 1671	if (fbdev->panel->init) {1672		r = fbdev->panel->init(fbdev->panel, fbdev);1673		if (r)1674			goto cleanup;1675	}1676 1677	pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);1678 1679	def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res;1680	def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res;1681 1682	init_state++;1683 1684	r = ctrl_init(fbdev);1685	if (r)1686		goto cleanup;1687	if (fbdev->ctrl->mmap != NULL)1688		omapfb_ops.fb_mmap = omapfb_mmap;1689	init_state++;1690 1691	r = planes_init(fbdev);1692	if (r)1693		goto cleanup;1694	init_state++;1695 1696#ifdef CONFIG_FB_OMAP_DMA_TUNE1697	/* Set DMA priority for EMIFF access to highest */1698	omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);1699#endif1700 1701	r = ctrl_change_mode(fbdev->fb_info[0]);1702	if (r) {1703		dev_err(fbdev->dev, "mode setting failed\n");1704		goto cleanup;1705	}1706 1707	/* GFX plane is enabled by default */1708	r = fbdev->ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);1709	if (r)1710		goto cleanup;1711 1712	omapfb_set_update_mode(fbdev, manual_update ?1713				   OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE);1714	init_state++;1715 1716	if (fbdev->panel->enable) {1717		r = fbdev->panel->enable(fbdev->panel);1718		if (r)1719			goto cleanup;1720	}1721	init_state++;1722 1723	r = omapfb_register_sysfs(fbdev);1724	if (r)1725		goto cleanup;1726	init_state++;1727 1728	vram = 0;1729	for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {1730		r = register_framebuffer(fbdev->fb_info[i]);1731		if (r != 0) {1732			dev_err(fbdev->dev,1733				"registering framebuffer %d failed\n", i);1734			goto cleanup;1735		}1736		vram += fbdev->mem_desc.region[i].size;1737	}1738 1739	fbdev->state = OMAPFB_ACTIVE;1740 1741	panel = fbdev->panel;1742	phz = panel->pixel_clock * 1000;1743	hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw);1744	vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw);1745 1746	omapfb_dev = fbdev;1747 1748	pr_info("omapfb: Framebuffer initialized. Total vram %lu planes %d\n",1749			vram, fbdev->mem_desc.region_cnt);1750	pr_info("omapfb: Pixclock %lu kHz hfreq %lu.%lu kHz "1751			"vfreq %lu.%lu Hz\n",1752			phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10);1753 1754	return 0;1755 1756cleanup:1757	omapfb_free_resources(fbdev, init_state);1758 1759	return r;1760}1761 1762static int omapfb_probe(struct platform_device *pdev)1763{1764	int r;1765 1766	BUG_ON(fbdev_pdev != NULL);1767 1768	r = platform_device_register(&omapdss_device);1769	if (r) {1770		dev_err(&pdev->dev, "can't register omapdss device\n");1771		return r;1772	}1773 1774	/* Delay actual initialization until the LCD is registered */1775	fbdev_pdev = pdev;1776	if (fbdev_panel != NULL)1777		omapfb_do_probe(fbdev_pdev, fbdev_panel);1778	return 0;1779}1780 1781void omapfb_register_panel(struct lcd_panel *panel)1782{1783	BUG_ON(fbdev_panel != NULL);1784 1785	fbdev_panel = panel;1786	if (fbdev_pdev != NULL)1787		omapfb_do_probe(fbdev_pdev, fbdev_panel);1788}1789EXPORT_SYMBOL_GPL(omapfb_register_panel);1790 1791/* Called when the device is being detached from the driver */1792static void omapfb_remove(struct platform_device *pdev)1793{1794	struct omapfb_device *fbdev = platform_get_drvdata(pdev);1795	enum omapfb_state saved_state = fbdev->state;1796 1797	/* FIXME: wait till completion of pending events */1798 1799	fbdev->state = OMAPFB_DISABLED;1800	omapfb_free_resources(fbdev, saved_state);1801 1802	platform_device_unregister(&omapdss_device);1803	fbdev->dssdev = NULL;1804}1805 1806/* PM suspend */1807static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)1808{1809	struct omapfb_device *fbdev = platform_get_drvdata(pdev);1810 1811	if (fbdev != NULL)1812		omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]);1813	return 0;1814}1815 1816/* PM resume */1817static int omapfb_resume(struct platform_device *pdev)1818{1819	struct omapfb_device *fbdev = platform_get_drvdata(pdev);1820 1821	if (fbdev != NULL)1822		omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]);1823	return 0;1824}1825 1826static struct platform_driver omapfb_driver = {1827	.probe		= omapfb_probe,1828	.remove		= omapfb_remove,1829	.suspend	= omapfb_suspend,1830	.resume		= omapfb_resume,1831	.driver		= {1832		.name	= MODULE_NAME,1833	},1834};1835 1836#ifndef MODULE1837 1838/* Process kernel command line parameters */1839static int __init omapfb_setup(char *options)1840{1841	char *this_opt = NULL;1842	int r = 0;1843 1844	pr_debug("omapfb: options %s\n", options);1845 1846	if (!options || !*options)1847		return 0;1848 1849	while (!r && (this_opt = strsep(&options, ",")) != NULL) {1850		if (!strncmp(this_opt, "accel", 5))1851			def_accel = 1;1852		else if (!strncmp(this_opt, "vram:", 5)) {1853			unsigned long long vram;1854			char *suffix;1855 1856			vram = memparse(this_opt + 5, &suffix);1857			switch (suffix[0]) {1858			case '\0':1859				break;1860			default:1861				pr_debug("omapfb: invalid vram suffix %c\n",1862					 suffix[0]);1863				r = -1;1864			}1865			def_vram[def_vram_cnt++] = vram;1866		}1867		else if (!strncmp(this_opt, "vxres:", 6))1868			def_vxres = simple_strtoul(this_opt + 6, NULL, 0);1869		else if (!strncmp(this_opt, "vyres:", 6))1870			def_vyres = simple_strtoul(this_opt + 6, NULL, 0);1871		else if (!strncmp(this_opt, "rotate:", 7))1872			def_rotate = (simple_strtoul(this_opt + 7, NULL, 0));1873		else if (!strncmp(this_opt, "mirror:", 7))1874			def_mirror = (simple_strtoul(this_opt + 7, NULL, 0));1875		else if (!strncmp(this_opt, "manual_update", 13))1876			manual_update = 1;1877		else {1878			pr_debug("omapfb: invalid option\n");1879			r = -1;1880		}1881	}1882 1883	return r;1884}1885 1886#endif1887 1888/* Register both the driver and the device */1889static int __init omapfb_init(void)1890{1891#ifndef MODULE1892	char *option;1893 1894	if (fb_get_options("omapfb", &option))1895		return -ENODEV;1896	omapfb_setup(option);1897#endif1898	/* Register the driver with LDM */1899	if (platform_driver_register(&omapfb_driver)) {1900		pr_debug("failed to register omapfb driver\n");1901		return -ENODEV;1902	}1903 1904	return 0;1905}1906 1907static void __exit omapfb_cleanup(void)1908{1909	platform_driver_unregister(&omapfb_driver);1910}1911 1912module_param_named(accel, def_accel, uint, 0664);1913module_param_array_named(vram, def_vram, ulong, &def_vram_cnt, 0664);1914module_param_named(vxres, def_vxres, long, 0664);1915module_param_named(vyres, def_vyres, long, 0664);1916module_param_named(rotate, def_rotate, uint, 0664);1917module_param_named(mirror, def_mirror, uint, 0664);1918module_param_named(manual_update, manual_update, bool, 0664);1919 1920module_init(omapfb_init);1921module_exit(omapfb_cleanup);1922 1923MODULE_DESCRIPTION("TI OMAP framebuffer driver");1924MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>");1925MODULE_LICENSE("GPL");1926