diff options
| -rw-r--r-- | drivers/video/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/mxc_ipuv3_fb.c | 642 | ||||
| -rw-r--r-- | drivers/video/mxcfb.h | 68 | 
3 files changed, 711 insertions, 0 deletions
| diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 4be82e739..5b7b26126 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o  COBJS-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o  COBJS-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o  COBJS-$(CONFIG_VIDEO_MX3) += mx3fb.o +COBJS-$(CONFIG_VIDEO_MX5) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o  COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o  COBJS-$(CONFIG_SED156X) += sed156x.o  COBJS-$(CONFIG_VIDEO_SM501) += sm501.o diff --git a/drivers/video/mxc_ipuv3_fb.c b/drivers/video/mxc_ipuv3_fb.c new file mode 100644 index 000000000..a66981c27 --- /dev/null +++ b/drivers/video/mxc_ipuv3_fb.c @@ -0,0 +1,642 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * MX51 Linux framebuffer: + * + * (C) Copyright 2004-2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* #define DEBUG */ +#include <common.h> +#include <asm/errno.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/fb.h> +#include <asm/io.h> +#include <malloc.h> +#include <lcd.h> +#include "videomodes.h" +#include "ipu.h" +#include "mxcfb.h" + +DECLARE_GLOBAL_DATA_PTR; + +void *lcd_base;			/* Start of framebuffer memory	*/ +void *lcd_console_address;	/* Start of console buffer	*/ + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +short console_col; +short console_row; + +vidinfo_t panel_info; + +static int mxcfb_map_video_memory(struct fb_info *fbi); +static int mxcfb_unmap_video_memory(struct fb_info *fbi); + +void lcd_initcolregs(void) +{ +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +void lcd_disable(void) +{ +} + +void lcd_panel_disable(void) +{ +} + +void fb_videomode_to_var(struct fb_var_screeninfo *var, +			 const struct fb_videomode *mode) +{ +	var->xres = mode->xres; +	var->yres = mode->yres; +	var->xres_virtual = mode->xres; +	var->yres_virtual = mode->yres; +	var->xoffset = 0; +	var->yoffset = 0; +	var->pixclock = mode->pixclock; +	var->left_margin = mode->left_margin; +	var->right_margin = mode->right_margin; +	var->upper_margin = mode->upper_margin; +	var->lower_margin = mode->lower_margin; +	var->hsync_len = mode->hsync_len; +	var->vsync_len = mode->vsync_len; +	var->sync = mode->sync; +	var->vmode = mode->vmode & FB_VMODE_MASK; +} + +/* + * Structure containing the MXC specific framebuffer information. + */ +struct mxcfb_info { +	int blank; +	ipu_channel_t ipu_ch; +	int ipu_di; +	u32 ipu_di_pix_fmt; +	unsigned char overlay; +	unsigned char alpha_chan_en; +	dma_addr_t alpha_phy_addr0; +	dma_addr_t alpha_phy_addr1; +	void *alpha_virt_addr0; +	void *alpha_virt_addr1; +	uint32_t alpha_mem_len; +	uint32_t cur_ipu_buf; +	uint32_t cur_ipu_alpha_buf; + +	u32 pseudo_palette[16]; +}; + +enum { +	BOTH_ON, +	SRC_ON, +	TGT_ON, +	BOTH_OFF +}; + +static unsigned long default_bpp = 16; +static unsigned char g_dp_in_use; +static struct fb_info *mxcfb_info[3]; +static int ext_clk_used; + +static uint32_t bpp_to_pixfmt(struct fb_info *fbi) +{ +	uint32_t pixfmt = 0; + +	debug("bpp_to_pixfmt: %d\n", fbi->var.bits_per_pixel); + +	if (fbi->var.nonstd) +		return fbi->var.nonstd; + +	switch (fbi->var.bits_per_pixel) { +	case 24: +		pixfmt = IPU_PIX_FMT_BGR24; +		break; +	case 32: +		pixfmt = IPU_PIX_FMT_BGR32; +		break; +	case 16: +		pixfmt = IPU_PIX_FMT_RGB565; +		break; +	} +	return pixfmt; +} + +/* + * Set fixed framebuffer parameters based on variable settings. + * + * @param       info     framebuffer information pointer + */ +static int mxcfb_set_fix(struct fb_info *info) +{ +	struct fb_fix_screeninfo *fix = &info->fix; +	struct fb_var_screeninfo *var = &info->var; + +	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + +	fix->type = FB_TYPE_PACKED_PIXELS; +	fix->accel = FB_ACCEL_NONE; +	fix->visual = FB_VISUAL_TRUECOLOR; +	fix->xpanstep = 1; +	fix->ypanstep = 1; + +	return 0; +} + +static int setup_disp_channel1(struct fb_info *fbi) +{ +	ipu_channel_params_t params; +	struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + +	memset(¶ms, 0, sizeof(params)); +	params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; + +	debug("%s called\n", __func__); +	/* +	 * Assuming interlaced means yuv output, below setting also +	 * valid for mem_dc_sync. FG should have the same vmode as BG. +	 */ +	if (fbi->var.vmode & FB_VMODE_INTERLACED) { +		params.mem_dp_bg_sync.interlaced = 1; +		params.mem_dp_bg_sync.out_pixel_fmt = +			IPU_PIX_FMT_YUV444; +	} else { +		if (mxc_fbi->ipu_di_pix_fmt) { +			params.mem_dp_bg_sync.out_pixel_fmt = +				mxc_fbi->ipu_di_pix_fmt; +		} else { +			params.mem_dp_bg_sync.out_pixel_fmt = +				IPU_PIX_FMT_RGB666; +		} +	} +	params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi); +	if (mxc_fbi->alpha_chan_en) +		params.mem_dp_bg_sync.alpha_chan_en = 1; + +	ipu_init_channel(mxc_fbi->ipu_ch, ¶ms); + +	return 0; +} + +static int setup_disp_channel2(struct fb_info *fbi) +{ +	int retval = 0; +	struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + +	mxc_fbi->cur_ipu_buf = 1; +	if (mxc_fbi->alpha_chan_en) +		mxc_fbi->cur_ipu_alpha_buf = 1; + +	fbi->var.xoffset = fbi->var.yoffset = 0; + +	debug("%s: %x %d %d %d %lx %lx\n", +		__func__, +		mxc_fbi->ipu_ch, +		fbi->var.xres, +		fbi->var.yres, +		fbi->fix.line_length, +		fbi->fix.smem_start, +		fbi->fix.smem_start + +		(fbi->fix.line_length * fbi->var.yres)); + +	retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, +					 bpp_to_pixfmt(fbi), +					 fbi->var.xres, fbi->var.yres, +					 fbi->fix.line_length, +					 fbi->fix.smem_start + +					 (fbi->fix.line_length * fbi->var.yres), +					 fbi->fix.smem_start, +					 0, 0); +	if (retval) +		printf("ipu_init_channel_buffer error %d\n", retval); + +	return retval; +} + +/* + * Set framebuffer parameters and change the operating mode. + * + * @param       info     framebuffer information pointer + */ +static int mxcfb_set_par(struct fb_info *fbi) +{ +	int retval = 0; +	u32 mem_len; +	ipu_di_signal_cfg_t sig_cfg; +	struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; +	uint32_t out_pixel_fmt; + +	ipu_disable_channel(mxc_fbi->ipu_ch); +	ipu_uninit_channel(mxc_fbi->ipu_ch); +	mxcfb_set_fix(fbi); + +	mem_len = fbi->var.yres_virtual * fbi->fix.line_length; +	if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { +		if (fbi->fix.smem_start) +			mxcfb_unmap_video_memory(fbi); + +		if (mxcfb_map_video_memory(fbi) < 0) +			return -ENOMEM; +	} + +	setup_disp_channel1(fbi); + +	memset(&sig_cfg, 0, sizeof(sig_cfg)); +	if (fbi->var.vmode & FB_VMODE_INTERLACED) { +		sig_cfg.interlaced = 1; +		out_pixel_fmt = IPU_PIX_FMT_YUV444; +	} else { +		if (mxc_fbi->ipu_di_pix_fmt) +			out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; +		else +			out_pixel_fmt = IPU_PIX_FMT_RGB666; +	} +	if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */ +		sig_cfg.odd_field_first = 1; +	if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used) +		sig_cfg.ext_clk = 1; +	if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) +		sig_cfg.Hsync_pol = 1; +	if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) +		sig_cfg.Vsync_pol = 1; +	if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL)) +		sig_cfg.clk_pol = 1; +	if (fbi->var.sync & FB_SYNC_DATA_INVERT) +		sig_cfg.data_pol = 1; +	if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) +		sig_cfg.enable_pol = 1; +	if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) +		sig_cfg.clkidle_en = 1; + +	debug("pixclock = %ul Hz\n", +		(u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); + +	if (ipu_init_sync_panel(mxc_fbi->ipu_di, +				(PICOS2KHZ(fbi->var.pixclock)) * 1000UL, +				fbi->var.xres, fbi->var.yres, +				out_pixel_fmt, +				fbi->var.left_margin, +				fbi->var.hsync_len, +				fbi->var.right_margin, +				fbi->var.upper_margin, +				fbi->var.vsync_len, +				fbi->var.lower_margin, +				0, sig_cfg) != 0) { +		puts("mxcfb: Error initializing panel.\n"); +		return -EINVAL; +	} + +	retval = setup_disp_channel2(fbi); +	if (retval) +		return retval; + +	if (mxc_fbi->blank == FB_BLANK_UNBLANK) +		ipu_enable_channel(mxc_fbi->ipu_ch); + +	return retval; +} + +/* + * Check framebuffer variable parameters and adjust to valid values. + * + * @param       var      framebuffer variable parameters + * + * @param       info     framebuffer information pointer + */ +static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ +	u32 vtotal; +	u32 htotal; + +	if (var->xres_virtual < var->xres) +		var->xres_virtual = var->xres; +	if (var->yres_virtual < var->yres) +		var->yres_virtual = var->yres; + +	if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && +	    (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8)) +		var->bits_per_pixel = default_bpp; + +	switch (var->bits_per_pixel) { +	case 8: +		var->red.length = 3; +		var->red.offset = 5; +		var->red.msb_right = 0; + +		var->green.length = 3; +		var->green.offset = 2; +		var->green.msb_right = 0; + +		var->blue.length = 2; +		var->blue.offset = 0; +		var->blue.msb_right = 0; + +		var->transp.length = 0; +		var->transp.offset = 0; +		var->transp.msb_right = 0; +		break; +	case 16: +		var->red.length = 5; +		var->red.offset = 11; +		var->red.msb_right = 0; + +		var->green.length = 6; +		var->green.offset = 5; +		var->green.msb_right = 0; + +		var->blue.length = 5; +		var->blue.offset = 0; +		var->blue.msb_right = 0; + +		var->transp.length = 0; +		var->transp.offset = 0; +		var->transp.msb_right = 0; +		break; +	case 24: +		var->red.length = 8; +		var->red.offset = 16; +		var->red.msb_right = 0; + +		var->green.length = 8; +		var->green.offset = 8; +		var->green.msb_right = 0; + +		var->blue.length = 8; +		var->blue.offset = 0; +		var->blue.msb_right = 0; + +		var->transp.length = 0; +		var->transp.offset = 0; +		var->transp.msb_right = 0; +		break; +	case 32: +		var->red.length = 8; +		var->red.offset = 16; +		var->red.msb_right = 0; + +		var->green.length = 8; +		var->green.offset = 8; +		var->green.msb_right = 0; + +		var->blue.length = 8; +		var->blue.offset = 0; +		var->blue.msb_right = 0; + +		var->transp.length = 8; +		var->transp.offset = 24; +		var->transp.msb_right = 0; +		break; +	} + +	if (var->pixclock < 1000) { +		htotal = var->xres + var->right_margin + var->hsync_len + +		    var->left_margin; +		vtotal = var->yres + var->lower_margin + var->vsync_len + +		    var->upper_margin; +		var->pixclock = (vtotal * htotal * 6UL) / 100UL; +		var->pixclock = KHZ2PICOS(var->pixclock); +		printf("pixclock set for 60Hz refresh = %u ps\n", +			var->pixclock); +	} + +	var->height = -1; +	var->width = -1; +	var->grayscale = 0; + +	return 0; +} + +static int mxcfb_map_video_memory(struct fb_info *fbi) +{ +	if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) { +		fbi->fix.smem_len = fbi->var.yres_virtual * +				    fbi->fix.line_length; +	} + +	fbi->screen_base = (char *)lcd_base; +	fbi->fix.smem_start = (unsigned long)lcd_base; +	if (fbi->screen_base == 0) { +		puts("Unable to allocate framebuffer memory\n"); +		fbi->fix.smem_len = 0; +		fbi->fix.smem_start = 0; +		return -EBUSY; +	} + +	debug("allocated fb @ paddr=0x%08X, size=%d.\n", +		(uint32_t) fbi->fix.smem_start, fbi->fix.smem_len); + +	fbi->screen_size = fbi->fix.smem_len; + +	/* Clear the screen */ +	memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); + +	return 0; +} + +static int mxcfb_unmap_video_memory(struct fb_info *fbi) +{ +	fbi->screen_base = 0; +	fbi->fix.smem_start = 0; +	fbi->fix.smem_len = 0; +	return 0; +} + +/* + * Initializes the framebuffer information pointer. After allocating + * sufficient memory for the framebuffer structure, the fields are + * filled with custom information passed in from the configurable + * structures.  This includes information such as bits per pixel, + * color maps, screen width/height and RGBA offsets. + * + * @return      Framebuffer structure initialized with our information + */ +static struct fb_info *mxcfb_init_fbinfo(void) +{ +#define BYTES_PER_LONG 4 +#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) +	struct fb_info *fbi; +	struct mxcfb_info *mxcfbi; +	char *p; +	int size = sizeof(struct mxcfb_info) + PADDING + +		sizeof(struct fb_info); + +	debug("%s: %d %d %d %d\n", +		__func__, +		PADDING, +		size, +		sizeof(struct mxcfb_info), +		sizeof(struct fb_info)); +	/* +	 * Allocate sufficient memory for the fb structure +	 */ + +	p = malloc(size); +	if (!p) +		return NULL; + +	memset(p, 0, size); + +	fbi = (struct fb_info *)p; +	fbi->par = p + sizeof(struct fb_info) + PADDING; + +	mxcfbi = (struct mxcfb_info *)fbi->par; +	debug("Framebuffer structures at: fbi=0x%x mxcfbi=0x%x\n", +		(unsigned int)fbi, (unsigned int)mxcfbi); + +	fbi->var.activate = FB_ACTIVATE_NOW; + +	fbi->flags = FBINFO_FLAG_DEFAULT; +	fbi->pseudo_palette = mxcfbi->pseudo_palette; + +	return fbi; +} + +/* + * Probe routine for the framebuffer driver. It is called during the + * driver binding process.      The following functions are performed in + * this routine: Framebuffer initialization, Memory allocation and + * mapping, Framebuffer registration, IPU initialization. + * + * @return      Appropriate error code to the kernel common code + */ +static int mxcfb_probe(u32 interface_pix_fmt, struct fb_videomode *mode) +{ +	struct fb_info *fbi; +	struct mxcfb_info *mxcfbi; +	int ret = 0; + +	/* +	 * Initialize FB structures +	 */ +	fbi = mxcfb_init_fbinfo(); +	if (!fbi) { +		ret = -ENOMEM; +		goto err0; +	} +	mxcfbi = (struct mxcfb_info *)fbi->par; + +	if (!g_dp_in_use) { +		mxcfbi->ipu_ch = MEM_BG_SYNC; +		mxcfbi->blank = FB_BLANK_UNBLANK; +	} else { +		mxcfbi->ipu_ch = MEM_DC_SYNC; +		mxcfbi->blank = FB_BLANK_POWERDOWN; +	} + +	mxcfbi->ipu_di = 0; + +	ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); +	ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); +	strcpy(fbi->fix.id, "DISP3 BG"); + +	g_dp_in_use = 1; + +	mxcfb_info[mxcfbi->ipu_di] = fbi; + +	/* Need dummy values until real panel is configured */ +	fbi->var.xres = 640; +	fbi->var.yres = 480; +	fbi->var.bits_per_pixel = 16; + +	mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; +	fb_videomode_to_var(&fbi->var, mode); + +	mxcfb_check_var(&fbi->var, fbi); + +	/* Default Y virtual size is 2x panel size */ +	fbi->var.yres_virtual = fbi->var.yres * 2; + +	mxcfb_set_fix(fbi); + +	/* alocate fb first */ +	if (mxcfb_map_video_memory(fbi) < 0) +		return -ENOMEM; + +	mxcfb_set_par(fbi); + +	/* Setting panel_info for lcd */ +	panel_info.cmap = NULL; +	panel_info.vl_col = fbi->var.xres; +	panel_info.vl_row = fbi->var.yres; +	panel_info.vl_bpix = LCD_BPP; + +	lcd_line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; + +	debug("MXC IPUV3 configured\n" +		"XRES = %d YRES = %d BitsXpixel = %d\n", +		panel_info.vl_col, +		panel_info.vl_row, +		panel_info.vl_bpix); + +	ipu_dump_registers(); + +	return 0; + +err0: +	return ret; +} + +int overwrite_console(void) +{ +	/* Keep stdout / stderr on serial, our LCD is for splashscreen only */ +	return 1; +} + +void lcd_ctrl_init(void *lcdbase) +{ +	u32 mem_len = panel_info.vl_col * +		panel_info.vl_row * +		NBITS(panel_info.vl_bpix) / 8; + +	/* +	 * We rely on lcdbase being a physical address, i.e., either MMU off, +	 * or 1-to-1 mapping. Might want to add some virt2phys here. +	 */ +	if (!lcdbase) +		return; + +	memset(lcdbase, 0, mem_len); +} + +int mx51_fb_init(struct fb_videomode *mode) +{ +	int ret; + +	ret = ipu_probe(); +	if (ret) +		puts("Error initializing IPU\n"); + +	lcd_base += 56; + +	debug("Framebuffer at 0x%x\n", (unsigned int)lcd_base); +	ret = mxcfb_probe(IPU_PIX_FMT_RGB666, mode); + +	return ret; +} diff --git a/drivers/video/mxcfb.h b/drivers/video/mxcfb.h new file mode 100644 index 000000000..d50819660 --- /dev/null +++ b/drivers/video/mxcfb.h @@ -0,0 +1,68 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2004-2009 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __ASM_ARCH_MXCFB_H__ +#define __ASM_ARCH_MXCFB_H__ + +#define FB_SYNC_OE_LOW_ACT	0x80000000 +#define FB_SYNC_CLK_LAT_FALL	0x40000000 +#define FB_SYNC_DATA_INVERT	0x20000000 +#define FB_SYNC_CLK_IDLE_EN	0x10000000 +#define FB_SYNC_SHARP_MODE	0x08000000 +#define FB_SYNC_SWAP_RGB	0x04000000 + +struct mxcfb_gbl_alpha { +	int enable; +	int alpha; +}; + +struct mxcfb_loc_alpha { +	int enable; +	int alpha_in_pixel; +	unsigned long alpha_phy_addr0; +	unsigned long alpha_phy_addr1; +}; + +struct mxcfb_color_key { +	int enable; +	__u32 color_key; +}; + +struct mxcfb_pos { +	__u16 x; +	__u16 y; +}; + +struct mxcfb_gamma { +	int enable; +	int constk[16]; +	int slopek[16]; +}; + +#endif |