⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sh_mobile_lcdcfb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * SuperH Mobile LCDC Framebuffer * * Copyright (c) 2008 Magnus Damm * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. */#include <linux/kernel.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/clk.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/interrupt.h>#include <video/sh_mobile_lcdc.h>#include <asm/atomic.h>#define PALETTE_NR 16struct sh_mobile_lcdc_priv;struct sh_mobile_lcdc_chan {	struct sh_mobile_lcdc_priv *lcdc;	unsigned long *reg_offs;	unsigned long ldmt1r_value;	unsigned long enabled; /* ME and SE in LDCNT2R */	struct sh_mobile_lcdc_chan_cfg cfg;	u32 pseudo_palette[PALETTE_NR];	struct fb_info info;	dma_addr_t dma_handle;	struct fb_deferred_io defio;};struct sh_mobile_lcdc_priv {	void __iomem *base;	int irq;#ifdef CONFIG_HAVE_CLK	atomic_t clk_usecnt;	struct clk *dot_clk;	struct clk *clk;#endif	unsigned long lddckr;	struct sh_mobile_lcdc_chan ch[2];};/* shared registers */#define _LDDCKR 0x410#define _LDDCKSTPR 0x414#define _LDINTR 0x468#define _LDSR 0x46c#define _LDCNT1R 0x470#define _LDCNT2R 0x474#define _LDDDSR 0x47c#define _LDDWD0R 0x800#define _LDDRDR 0x840#define _LDDWAR 0x900#define _LDDRAR 0x904/* per-channel registers */enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,       LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };static unsigned long lcdc_offs_mainlcd[] = {	[LDDCKPAT1R] = 0x400,	[LDDCKPAT2R] = 0x404,	[LDMT1R] = 0x418,	[LDMT2R] = 0x41c,	[LDMT3R] = 0x420,	[LDDFR] = 0x424,	[LDSM1R] = 0x428,	[LDSM2R] = 0x42c,	[LDSA1R] = 0x430,	[LDMLSR] = 0x438,	[LDHCNR] = 0x448,	[LDHSYNR] = 0x44c,	[LDVLNR] = 0x450,	[LDVSYNR] = 0x454,	[LDPMR] = 0x460,};static unsigned long lcdc_offs_sublcd[] = {	[LDDCKPAT1R] = 0x408,	[LDDCKPAT2R] = 0x40c,	[LDMT1R] = 0x600,	[LDMT2R] = 0x604,	[LDMT3R] = 0x608,	[LDDFR] = 0x60c,	[LDSM1R] = 0x610,	[LDSM2R] = 0x614,	[LDSA1R] = 0x618,	[LDMLSR] = 0x620,	[LDHCNR] = 0x624,	[LDHSYNR] = 0x628,	[LDVLNR] = 0x62c,	[LDVSYNR] = 0x630,	[LDPMR] = 0x63c,};#define START_LCDC	0x00000001#define LCDC_RESET	0x00000100#define DISPLAY_BEU	0x00000008#define LCDC_ENABLE	0x00000001#define LDINTR_FE	0x00000400#define LDINTR_FS	0x00000004static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,			    int reg_nr, unsigned long data){	iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);}static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,				    int reg_nr){	return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);}static void lcdc_write(struct sh_mobile_lcdc_priv *priv,		       unsigned long reg_offs, unsigned long data){	iowrite32(data, priv->base + reg_offs);}static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv,			       unsigned long reg_offs){	return ioread32(priv->base + reg_offs);}static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,			  unsigned long reg_offs,			  unsigned long mask, unsigned long until){	while ((lcdc_read(priv, reg_offs) & mask) != until)		cpu_relax();}static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan){	return chan->cfg.chan == LCDC_CHAN_SUBLCD;}static void lcdc_sys_write_index(void *handle, unsigned long data){	struct sh_mobile_lcdc_chan *ch = handle;	lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);	lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));}static void lcdc_sys_write_data(void *handle, unsigned long data){	struct sh_mobile_lcdc_chan *ch = handle;	lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);	lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));}static unsigned long lcdc_sys_read_data(void *handle){	struct sh_mobile_lcdc_chan *ch = handle;	lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);	lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));	udelay(1);	return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff;}struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {	lcdc_sys_write_index,	lcdc_sys_write_data,	lcdc_sys_read_data,};#ifdef CONFIG_HAVE_CLKstatic void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv){	if (atomic_inc_and_test(&priv->clk_usecnt)) {		clk_enable(priv->clk);		if (priv->dot_clk)			clk_enable(priv->dot_clk);	}}static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv){	if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {		if (priv->dot_clk)			clk_disable(priv->dot_clk);		clk_disable(priv->clk);	}}#elsestatic void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}#endifstatic void sh_mobile_lcdc_deferred_io(struct fb_info *info,				       struct list_head *pagelist){	struct sh_mobile_lcdc_chan *ch = info->par;	/* enable clocks before accessing hardware */	sh_mobile_lcdc_clk_on(ch->lcdc);	/* trigger panel update */	lcdc_write_chan(ch, LDSM2R, 1);}static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info){	struct fb_deferred_io *fbdefio = info->fbdefio;	if (fbdefio)		schedule_delayed_work(&info->deferred_work, fbdefio->delay);}static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data){	struct sh_mobile_lcdc_priv *priv = data;	unsigned long tmp;	/* acknowledge interrupt */	tmp = lcdc_read(priv, _LDINTR);	tmp &= 0xffffff00; /* mask in high 24 bits */	tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */	lcdc_write(priv, _LDINTR, tmp);	/* disable clocks */	sh_mobile_lcdc_clk_off(priv);	return IRQ_HANDLED;}static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,				      int start){	unsigned long tmp = lcdc_read(priv, _LDCNT2R);	int k;	/* start or stop the lcdc */	if (start)		lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);	else		lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);	/* wait until power is applied/stopped on all channels */	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)		if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)			while (1) {				tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;				if (start && tmp == 3)					break;				if (!start && tmp == 0)					break;				cpu_relax();			}	if (!start)		lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */}static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv){	struct sh_mobile_lcdc_chan *ch;	struct fb_videomode *lcd_cfg;	struct sh_mobile_lcdc_board_cfg	*board_cfg;	unsigned long tmp;	int k, m;	int ret = 0;	/* enable clocks before accessing the hardware */	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)		if (priv->ch[k].enabled)			sh_mobile_lcdc_clk_on(priv);	/* reset */	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);	lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);	/* enable LCDC channels */	tmp = lcdc_read(priv, _LDCNT2R);	tmp |= priv->ch[0].enabled;	tmp |= priv->ch[1].enabled;	lcdc_write(priv, _LDCNT2R, tmp);	/* read data from external memory, avoid using the BEU for now */	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);	/* stop the lcdc first */	sh_mobile_lcdc_start_stop(priv, 0);	/* configure clocks */	tmp = priv->lddckr;	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {		ch = &priv->ch[k];		if (!priv->ch[k].enabled)			continue;		m = ch->cfg.clock_divider;		if (!m)			continue;		if (m == 1)			m = 1 << 6;		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);		lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);	}	lcdc_write(priv, _LDDCKR, tmp);	/* start dotclock again */	lcdc_write(priv, _LDDCKSTPR, 0);	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);	/* interrupts are disabled to begin with */	lcdc_write(priv, _LDINTR, 0);	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {		ch = &priv->ch[k];		lcd_cfg = &ch->cfg.lcd_cfg;		if (!ch->enabled)			continue;		tmp = ch->ldmt1r_value;		tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;		tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;		tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;		tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;		tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;		lcdc_write_chan(ch, LDMT1R, tmp);		/* setup SYS bus */		lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);		lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);		/* horizontal configuration */		tmp = lcd_cfg->xres + lcd_cfg->hsync_len;		tmp += lcd_cfg->left_margin;		tmp += lcd_cfg->right_margin;		tmp /= 8; /* HTCN */		tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */		lcdc_write_chan(ch, LDHCNR, tmp);		tmp = lcd_cfg->xres;		tmp += lcd_cfg->right_margin;		tmp /= 8; /* HSYNP */		tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */		lcdc_write_chan(ch, LDHSYNR, tmp);		/* power supply */		lcdc_write_chan(ch, LDPMR, 0);		/* vertical configuration */		tmp = lcd_cfg->yres + lcd_cfg->vsync_len;		tmp += lcd_cfg->upper_margin;		tmp += lcd_cfg->lower_margin; /* VTLN */		tmp |= lcd_cfg->yres << 16; /* VDLN */		lcdc_write_chan(ch, LDVLNR, tmp);		tmp = lcd_cfg->yres;		tmp += lcd_cfg->lower_margin; /* VSYNP */		tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */		lcdc_write_chan(ch, LDVSYNR, tmp);		board_cfg = &ch->cfg.board_cfg;		if (board_cfg->setup_sys)			ret = board_cfg->setup_sys(board_cfg->board_data, ch,						   &sh_mobile_lcdc_sys_bus_ops);		if (ret)			return ret;	}	/* word and long word swap */	lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {		ch = &priv->ch[k];		if (!priv->ch[k].enabled)			continue;		/* set bpp format in PKF[4:0] */		tmp = lcdc_read_chan(ch, LDDFR);		tmp &= ~(0x0001001f);		tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0;		lcdc_write_chan(ch, LDDFR, tmp);		/* point out our frame buffer */		lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start);		/* set line size */		lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);		/* setup deferred io if SYS bus */		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;		if (ch->ldmt1r_value & (1 << 12) && tmp) {			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;			ch->defio.delay = msecs_to_jiffies(tmp);			ch->info.fbdefio = &ch->defio;			fb_deferred_io_init(&ch->info);			/* one-shot mode */			lcdc_write_chan(ch, LDSM1R, 1);			/* enable "Frame End Interrupt Enable" bit */			lcdc_write(priv, _LDINTR, LDINTR_FE);		} else {			/* continuous read mode */			lcdc_write_chan(ch, LDSM1R, 0);		}	}	/* display output */	lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);	/* start the lcdc */	sh_mobile_lcdc_start_stop(priv, 1);	/* tell the board code to enable the panel */	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {		ch = &priv->ch[k];		board_cfg = &ch->cfg.board_cfg;		if (board_cfg->display_on)			board_cfg->display_on(board_cfg->board_data);	}	return 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -