📄 lcdc.c
字号:
/* * linux/arch/arm/mach-omap/lcdc.c * * OMAP LCD controller * * Copyright (C) 2004 Nokia Corporation * Author: Imre Deak <imre.deak@nokia.com> * * 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. */ #include <linux/kernel.h>#include <linux/device.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/err.h>#include <asm/hardware.h>#include <asm/mach-types.h>#include <asm/hardware/clock.h>#include "lcdc.h"struct lcd_controller { int initialized; void *owner; spinlock_t spinlock; int active; struct completion done_comp; u32 irq_mask; u32 stat_mask; lcdc_irq_handler_t irq_handler; unsigned long handler_data; struct clk * lcd_ck;};static struct lcd_controller lcdc;int omap_lcdc_request(void *devid, lcdc_irq_handler_t handler, unsigned long handler_data){ BUG_ON(devid == (void *)-1); if (!lcdc.initialized) { printk(KERN_ERR "lcdc: not initialized\n"); return -1; } spin_lock_irq(&lcdc.spinlock); if (lcdc.owner != (void *)-1) { printk(KERN_ERR "lcdc: already reserved\n"); spin_unlock_irq(&lcdc.spinlock); return -1; } lcdc.owner = devid; spin_unlock_irq(&lcdc.spinlock); lcdc.active = 0; lcdc.irq_mask = 0; lcdc.stat_mask = 0; lcdc.irq_handler = handler; lcdc.handler_data = handler_data; return 0;}void omap_lcdc_release(void *devid){ BUG_ON(devid != lcdc.owner); lcdc.owner = (void *)-1;}void omap_lcdc_set_config(int tft, int signal_levels){ u32 l; l = omap_readl(OMAP_LCDC_CONTROL); l &= ~OMAP_LCDC_CTRL_LCD_TFT; l |= tft ? OMAP_LCDC_CTRL_LCD_TFT : 0; omap_writel(l, OMAP_LCDC_CONTROL); l = omap_readl(OMAP_LCDC_TIMING2); l &= ~(((1 << 6) - 1) << 20); l |= signal_levels << 20; omap_writel(l, OMAP_LCDC_TIMING2);}void omap_lcdc_set_video_mode(struct lcdc_video_mode *mode){ u32 l; l = mode->x_res - 1; l |= (mode->hsw - 1) << 10; l |= (mode->hfp - 1) << 16; l |= (mode->hbp - 1) << 24; omap_writel(l, OMAP_LCDC_TIMING0); l = mode->y_res - 1; l |= (mode->vsw - 1) << 10; l |= mode->vfp << 16; l |= mode->vbp << 24; omap_writel(l, OMAP_LCDC_TIMING1); l = omap_readl(OMAP_LCDC_TIMING2); l &= ~0xff; l |= mode->pcd; l |= mode->acb << 8; omap_writel(l, OMAP_LCDC_TIMING2);}void omap_lcdc_set_load_mode(enum lcdc_load_mode load_mode){ u32 l; l = omap_readl(OMAP_LCDC_CONTROL); l &= ~(3 << 20); switch (load_mode) { case OMAP_LCDC_LOAD_PALETTE: l |= 1 << 20; break; case OMAP_LCDC_LOAD_FRAME: l |= 2 << 20; break; case OMAP_LCDC_LOAD_PALETTE_AND_FRAME: break; default: BUG(); } omap_writel(l, OMAP_LCDC_CONTROL);}int omap_lcdc_get_palette_size(struct lcdc_video_mode *mode){ switch (mode->bpp) { case 1: case 2: case 4: case 8: return 256; case 12: case 16: return 32; default: BUG(); } return 0;}static void __enable_irqs(int irq_mask, int enable){ int stat_mask = 0; if (irq_mask & OMAP_LCDC_IRQ_LOADED_PALETTE) stat_mask |= OMAP_LCDC_STAT_LOADED_PALETTE; if (irq_mask & OMAP_LCDC_IRQ_VSYNC) stat_mask |= OMAP_LCDC_STAT_VSYNC; if (irq_mask & OMAP_LCDC_IRQ_DONE) stat_mask |= OMAP_LCDC_STAT_DONE; if (irq_mask & (OMAP_LCDC_IRQ_LINE | OMAP_LCDC_IRQ_LINE_NIRQ)) stat_mask |= OMAP_LCDC_STAT_LINE_INT; if (enable) { lcdc.irq_mask |= irq_mask; lcdc.stat_mask |= stat_mask; } else { lcdc.irq_mask &= ~irq_mask; lcdc.stat_mask &= ~stat_mask; }}void omap_lcdc_enable_irqs(int mask){ __enable_irqs(mask, 1);}void omap_lcdc_disable_irqs(int mask){ __enable_irqs(mask, 0);}void omap_lcdc_set_line_irq(int line){ u32 l; BUG_ON(line >= 1024); l = omap_readl(OMAP_LCDC_LINE_INT); l &= ~1023; l |= line; omap_writel(l, OMAP_LCDC_LINE_INT);}void omap_lcdc_init_palette(struct lcdc_video_mode *mode, void *palette, int size){ memset(palette, 0, size); switch (mode->bpp) { case 1: /* 0 is already set */ break; case 2: *(u32 *)palette = 0x1000; break; case 4: *(u32 *)palette = 0x2000; break; case 8: *(u32 *)palette = 0x3000; break; case 12: case 16: *(u32 *)palette = 0x4000; break; default: BUG(); }}void omap_lcdc_enable(void){ u32 l; BUG_ON(lcdc.irq_mask && !lcdc.irq_handler); lcdc.active = 1; l = omap_readl(OMAP_LCDC_CONTROL); l |= OMAP_LCDC_CTRL_LCD_EN; l &= ~OMAP_LCDC_IRQ_MASK; l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE; /* enabled IRQs */ omap_writel(l, OMAP_LCDC_CONTROL);}void omap_lcdc_disable_async(void){ u32 l; u32 mask; lcdc.active = 0; init_completion(&lcdc.done_comp); l = omap_readl(OMAP_LCDC_CONTROL); mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK; /* Preserve the DONE mask, since we still want to get the * final DONE irq. It will be sdisabled in the IRQ handler. */ mask &= ~OMAP_LCDC_IRQ_DONE; l &= ~mask; omap_writel(l, OMAP_LCDC_CONTROL);}static void omap_lcdc_done_timeout(unsigned long data){ printk(KERN_ERR "lcdc: timeout waiting for DONE\n"); complete(&lcdc.done_comp);}void omap_lcdc_disable(void){ struct timer_list timeout; omap_lcdc_disable_async(); init_timer(&timeout); timeout.function = omap_lcdc_done_timeout; timeout.expires = jiffies + HZ / 10; /* 100 ms */ add_timer(&timeout); wait_for_completion(&lcdc.done_comp); del_timer(&timeout);}static void omap_lcdc_reset(u32 status){ static unsigned long reset_count = 0; static unsigned long last_jiffies = 0; omap_lcdc_disable_async(); reset_count++; if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) { printk(KERN_ERR "lcdc: resetting " "(status %#010x,reset count %lu)\n", status, reset_count); last_jiffies = jiffies; } if (reset_count < 100) { omap_lcdc_enable(); } else { reset_count = 0; printk(KERN_ERR "lcdc: too many reset attempts, " "giving up.\n"); }}static irqreturn_t omap_lcdc_irq_handler(int irq, void *dev_id, struct pt_regs *fp){ int reset = 0; u32 status; status = omap_readl(OMAP_LCDC_STATUS); if (status & OMAP_LCDC_STAT_FUF) /* FIFO underflow */ reset = 1; if (status & OMAP_LCDC_STAT_SYNC_LOST) reset = 1; if (status & OMAP_LCDC_STAT_DONE) { u32 l; /* Disable IRQ_DONE. The status bit will be cleared * only when the controller is reenabled and we don't * want to get more interrupts. */ l = omap_readl(OMAP_LCDC_CONTROL); l &= ~OMAP_LCDC_IRQ_DONE; omap_writel(l, OMAP_LCDC_CONTROL); complete(&lcdc.done_comp); } if (reset) omap_lcdc_reset(status); else if (status & lcdc.stat_mask) lcdc.irq_handler(status, lcdc.handler_data); /* Clear these interrupt status bits. * Sync_lost, FUF bits were cleared by disabling the LCD controller * LOADED_PALETTE can be cleared this way only in palette only * load mode. In other load modes it's cleared by disabling the * controller. */ status &= ~(OMAP_LCDC_STAT_VSYNC | OMAP_LCDC_STAT_LOADED_PALETTE | OMAP_LCDC_STAT_ABC | OMAP_LCDC_STAT_LINE_INT); omap_writel(status, OMAP_LCDC_STATUS); return IRQ_HANDLED;}int omap_lcdc_init(void){ u32 l; int rate; int r; struct clk *tc_ck; l = 0; omap_writel(l, OMAP_LCDC_CONTROL); /* FIXME: * According to errata some platforms have a clock rate limitiation */ lcdc.lcd_ck = clk_get(0, "lcd_ck"); if (IS_ERR(lcdc.lcd_ck)) { printk(KERN_ERR "lcdc: unable to access LCD clock\n"); return PTR_ERR(lcdc.lcd_ck); } tc_ck = clk_get(0, "tc_ck"); if (IS_ERR(tc_ck)) { printk(KERN_ERR "lcdc: unable to access TC clock\n"); clk_put(lcdc.lcd_ck); return PTR_ERR(tc_ck); } rate = clk_get_rate(tc_ck); clk_put(tc_ck); if (machine_is_omap_innovator() || machine_is_omap_h3() || machine_is_omap_osk()) rate /= 3; if (clk_set_rate(lcdc.lcd_ck, rate) < 0) printk(KERN_WARNING "lcdc: failed to adjust LCD rate\n"); clk_use(lcdc.lcd_ck); if ((r = request_irq(OMAP_LCDC_IRQ, omap_lcdc_irq_handler, 0, "omap-lcdc", 0)) < 0) { printk(KERN_ERR "lcdc: unable to get IRQ\n"); return r; } spin_lock_init(&lcdc.spinlock); lcdc.owner = (void *)-1; lcdc.initialized = 1; printk(KERN_INFO "OMAP LCD controller initialized.\n"); return 0;}void omap_lcdc_cleanup(void){ clk_unuse(lcdc.lcd_ck); clk_put(lcdc.lcd_ck); free_irq(OMAP_LCDC_IRQ, 0);}EXPORT_SYMBOL(omap_lcdc_request);EXPORT_SYMBOL(omap_lcdc_release);EXPORT_SYMBOL(omap_lcdc_set_config);EXPORT_SYMBOL(omap_lcdc_set_video_mode);EXPORT_SYMBOL(omap_lcdc_set_load_mode);EXPORT_SYMBOL(omap_lcdc_set_line_irq);EXPORT_SYMBOL(omap_lcdc_init_palette);EXPORT_SYMBOL(omap_lcdc_enable_irqs);EXPORT_SYMBOL(omap_lcdc_disable_irqs);EXPORT_SYMBOL(omap_lcdc_get_palette_size);EXPORT_SYMBOL(omap_lcdc_enable);EXPORT_SYMBOL(omap_lcdc_disable);EXPORT_SYMBOL(omap_lcdc_disable_async);MODULE_DESCRIPTION("TI OMAP LCDC controller");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -