diff options
Diffstat (limited to 'arch/arm/mach-stmp378x/stmp378x_lcdif.c')
-rw-r--r-- | arch/arm/mach-stmp378x/stmp378x_lcdif.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/arch/arm/mach-stmp378x/stmp378x_lcdif.c b/arch/arm/mach-stmp378x/stmp378x_lcdif.c new file mode 100644 index 000000000000..a88684c3684f --- /dev/null +++ b/arch/arm/mach-stmp378x/stmp378x_lcdif.c @@ -0,0 +1,210 @@ +/* + * Freescale STMP378X LCDIF low-level routines + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +/* #define DEBUG */ + +#include <linux/dma-mapping.h> +#include <linux/delay.h> + +#include <mach/hardware.h> +#include <mach/dma.h> +#include <mach/dma.h> +#include <mach/regs-lcdif.h> +#include <mach/regs-pinctrl.h> +#include <mach/lcdif.h> + +#define MAX_CHAIN_LEN 10 + +static struct stmp3xxx_dma_descriptor video_dma_descriptor[MAX_CHAIN_LEN]; +static struct stmp3xxx_lcd_dma_chain_info dma_chain_info[MAX_CHAIN_LEN]; +static unsigned dma_chain_info_pos; + +void stmp3xxx_init_lcdif(void) +{ + stmp3xxx_clearl(BM_LCDIF_CTRL_CLKGATE, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + /* Reset controller */ + stmp3xxx_setl(BM_LCDIF_CTRL_SFTRST, REGS_LCDIF_BASE + HW_LCDIF_CTRL); + udelay(10); + + /* Take controller out of reset */ + stmp3xxx_clearl(BM_LCDIF_CTRL_SFTRST | BM_LCDIF_CTRL_CLKGATE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + /* Setup the bus protocol */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_MODE86, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + stmp3xxx_clearl(BM_LCDIF_CTRL1_BUSY_ENABLE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + /* Take display out of reset */ + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + + /* VSYNC is an input by default */ + stmp3xxx_setl(BM_LCDIF_VDCTRL0_VSYNC_OEB, + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0); + + /* Reset display */ + stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + udelay(10); + stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); + udelay(10); +} + +EXPORT_SYMBOL(stmp3xxx_init_lcdif); + +static int stmp378x_lcd_master = 1; +int stmp3xxx_lcdif_dma_init(struct device *dev, dma_addr_t phys, int memsize, + int lcd_master) +{ + int ret = 0; + + stmp378x_lcd_master = lcd_master; + if (lcd_master) { + stmp3xxx_setl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + + __raw_writel(phys, REGS_LCDIF_BASE + HW_LCDIF_CUR_BUF); + __raw_writel(phys, REGS_LCDIF_BASE + HW_LCDIF_NEXT_BUF); + } else { + ret = + stmp3xxx_dma_request(STMP3XXX_DMA + (LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH), + dev, "lcdif"); + if (ret) { + dev_err(dev, + "stmp3xxx_dma_request failed: error %d\n", ret); + goto out; + } + + stmp3xxx_dma_reset_channel(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH)); + + stmp3xxx_dma_clear_interrupt(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH)); + stmp3xxx_dma_enable_interrupt(STMP3XXX_DMA + (LCD_DMA_CHANNEL, + STMP3XXX_BUS_APBH)); + + dotclk_dma_chain_init(memsize, phys, video_dma_descriptor, + dma_chain_info, &dma_chain_info_pos); + } +out: + return ret; +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_dma_init); + +void stmp3xxx_lcdif_dma_release(void) +{ + int i; + + if (stmp378x_lcd_master) { + stmp3xxx_clearl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + return; + } + + for (i = 0; i < dma_chain_info_pos; i++) + stmp3xxx_dma_free_command(STMP3XXX_DMA + (LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH), + &video_dma_descriptor[i]); + stmp3xxx_dma_release(STMP3XXX_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH)); + + dma_chain_info_pos = 0; +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_dma_release); + +void stmp3xxx_lcdif_run(void) +{ + if (stmp378x_lcd_master) { + stmp3xxx_setl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_setl(BM_LCDIF_CTRL_RUN, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + } else { + video_dma_descriptor[dma_chain_info_pos - 1].command->cmd &= + ~BM_APBH_CHn_CMD_SEMAPHORE; + stmp3xxx_dma_go(STMP3XXX_DMA + (LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH), + video_dma_descriptor, 1); + } +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_run); + +void stmp3xxx_lcdif_stop(void) +{ + if (stmp378x_lcd_master) { + stmp3xxx_clearl(BM_LCDIF_CTRL_RUN, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + stmp3xxx_clearl(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL); + udelay(100); + } else { + video_dma_descriptor[dma_chain_info_pos - 1].command->cmd |= + BM_APBH_CHn_CMD_SEMAPHORE; + udelay(100); + } + stmp3xxx_setl(BM_LCDIF_CTRL_CLKGATE, REGS_LCDIF_BASE + HW_LCDIF_CTRL); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_stop); + +int stmp3xxx_lcdif_pan_display(dma_addr_t addr) +{ + if (stmp378x_lcd_master) + __raw_writel(addr, REGS_LCDIF_BASE + HW_LCDIF_NEXT_BUF); + else { + int i; + /* Modify the chain addresses */ + for (i = 0; i < dma_chain_info_pos; ++i) { + *dma_chain_info[i].dma_addr_p = addr + + dma_chain_info[i].offset; + barrier(); + } + } + return 0; +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_pan_display); + +static BLOCKING_NOTIFIER_HEAD(lcdif_client_list); + +int stmp3xxx_lcdif_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&lcdif_client_list, nb); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_register_client); + +void stmp3xxx_lcdif_unregister_client(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&lcdif_client_list, nb); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_unregister_client); + +void stmp3xxx_lcdif_notify_clients(unsigned long event, + struct stmp3xxx_platform_fb_entry *pentry) +{ + blocking_notifier_call_chain(&lcdif_client_list, event, pentry); +} + +EXPORT_SYMBOL(stmp3xxx_lcdif_notify_clients); |