diff options
Diffstat (limited to 'arch/arm/mach-stmp3xxx/stmp378x_lcdif.c')
-rw-r--r-- | arch/arm/mach-stmp3xxx/stmp378x_lcdif.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/arch/arm/mach-stmp3xxx/stmp378x_lcdif.c b/arch/arm/mach-stmp3xxx/stmp378x_lcdif.c new file mode 100644 index 000000000000..54aeb455c8b4 --- /dev/null +++ b/arch/arm/mach-stmp3xxx/stmp378x_lcdif.c @@ -0,0 +1,188 @@ +/* + * 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) +{ + HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_CLKGATE); + /* Reset controller */ + HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_SFTRST); + udelay(10); + + /* Take controller out of reset */ + HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_SFTRST | BM_LCDIF_CTRL_CLKGATE); + + /* Setup the bus protocol */ + HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_MODE86); + HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_BUSY_ENABLE); + + /* Take display out of reset */ + HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET); + + /* VSYNC is an input by default */ + HW_LCDIF_VDCTRL0_SET(BM_LCDIF_VDCTRL0_VSYNC_OEB); + + /* Reset display */ + HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_RESET); + udelay(10); + HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET); + 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) { + HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_LCDIF_MASTER); + + HW_LCDIF_CUR_BUF_WR(phys); + HW_LCDIF_NEXT_BUF_WR(phys); + } 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) { + HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_LCDIF_MASTER); + 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) { + HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_LCDIF_MASTER); + HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_RUN); + } 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) { + HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_RUN); + HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_LCDIF_MASTER); + udelay(100); + } else { + video_dma_descriptor[dma_chain_info_pos - 1].command->cmd |= + BM_APBH_CHn_CMD_SEMAPHORE; + udelay(100); + } + HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_CLKGATE); +} +EXPORT_SYMBOL(stmp3xxx_lcdif_stop); + +int stmp3xxx_lcdif_pan_display(dma_addr_t addr) +{ + if (stmp378x_lcd_master) + HW_LCDIF_NEXT_BUF_WR(addr); + 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); |