From: Anatolij Gustschin Date: Mon, 18 Mar 2019 22:29:32 +0000 (+0100) Subject: video: move ipuv3 files to subdirectory X-Git-Tag: v2019.07-rc1~21^2~46 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=bffd13144b1fef3ee93007b92263667704597544;p=oweals%2Fu-boot.git video: move ipuv3 files to subdirectory Place ipuv3 files and headers in custom driver subdirectory. Signed-off-by: Anatolij Gustschin --- diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 73a2402f41..53871f864f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -538,12 +538,7 @@ config VIDEO_TEGRA124 source "drivers/video/bridge/Kconfig" -config VIDEO_IPUV3 - bool "i.MX IPUv3 Core video support" - depends on (VIDEO || DM_VIDEO) && (MX5 || MX6) - help - This enables framebuffer driver for i.MX processors working - on the IPUv3(Image Processing Unit) internal graphic processor. +source "drivers/video/imx/Kconfig" config VIDEO bool "Enable legacy video support" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 671f037c35..349a207035 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o obj-$(CONFIG_VIDEO_EFI) += efi.o obj-$(CONFIG_VIDEO_FSL_DCU_FB) += fsl_dcu_fb.o videomodes.o -obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_VIDEO_IPUV3) += imx/ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o diff --git a/drivers/video/imx/Kconfig b/drivers/video/imx/Kconfig new file mode 100644 index 0000000000..c33620e075 --- /dev/null +++ b/drivers/video/imx/Kconfig @@ -0,0 +1,8 @@ + +config VIDEO_IPUV3 + bool "i.MX IPUv3 Core video support" + depends on (VIDEO || DM_VIDEO) && (MX5 || MX6) + help + This enables framebuffer driver for i.MX processors working + on the IPUv3(Image Processing Unit) internal graphic processor. + diff --git a/drivers/video/imx/Makefile b/drivers/video/imx/Makefile new file mode 100644 index 0000000000..179ea651fe --- /dev/null +++ b/drivers/video/imx/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +obj-y += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o diff --git a/drivers/video/imx/ipu.h b/drivers/video/imx/ipu.h new file mode 100644 index 0000000000..1e02c7ab6d --- /dev/null +++ b/drivers/video/imx/ipu.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + */ + +#ifndef __ASM_ARCH_IPU_H__ +#define __ASM_ARCH_IPU_H__ + +#include +#include + +#define IDMA_CHAN_INVALID 0xFF +#define HIGH_RESOLUTION_WIDTH 1024 + +struct clk { + const char *name; + int id; + /* Source clock this clk depends on */ + struct clk *parent; + /* Secondary clock to enable/disable with this clock */ + struct clk *secondary; + /* Current clock rate */ + unsigned long rate; + /* Reference count of clock enable/disable */ + __s8 usecount; + /* Register bit position for clock's enable/disable control. */ + u8 enable_shift; + /* Register address for clock's enable/disable control. */ + void *enable_reg; + u32 flags; + /* + * Function ptr to recalculate the clock's rate based on parent + * clock's rate + */ + void (*recalc) (struct clk *); + /* + * Function ptr to set the clock to a new rate. The rate must match a + * supported rate returned from round_rate. Leave blank if clock is not + * programmable + */ + int (*set_rate) (struct clk *, unsigned long); + /* + * Function ptr to round the requested clock rate to the nearest + * supported rate that is less than or equal to the requested rate. + */ + unsigned long (*round_rate) (struct clk *, unsigned long); + /* + * Function ptr to enable the clock. Leave blank if clock can not + * be gated. + */ + int (*enable) (struct clk *); + /* + * Function ptr to disable the clock. Leave blank if clock can not + * be gated. + */ + void (*disable) (struct clk *); + /* Function ptr to set the parent clock of the clock. */ + int (*set_parent) (struct clk *, struct clk *); +}; + +/* + * Enumeration of Synchronous (Memory-less) panel types + */ +typedef enum { + IPU_PANEL_SHARP_TFT, + IPU_PANEL_TFT, +} ipu_panel_t; + +/* + * IPU Driver channels definitions. + * Note these are different from IDMA channels + */ +#define IPU_MAX_CH 32 +#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ + ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out) +#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24)) +#define IPU_CHAN_ID(ch) (ch >> 24) +#define IPU_CHAN_ALT(ch) (ch & 0x02000000) +#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F) +#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F) +#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F) +#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F)) +#define NO_DMA 0x3F +#define ALT 1 + +/* + * Enumeration of IPU logical channels. An IPU logical channel is defined as a + * combination of an input (memory to IPU), output (IPU to memory), and/or + * secondary input IDMA channels and in some cases an Image Converter task. + * Some channels consist of only an input or output. + */ +typedef enum { + CHAN_NONE = -1, + + MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), + MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), + MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), + MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), + + MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), + MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), + MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), + MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), + + DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + +} ipu_channel_t; + +/* + * Enumeration of types of buffers for a logical channel. + */ +typedef enum { + IPU_OUTPUT_BUFFER = 0, /*< Buffer for output from IPU */ + IPU_ALPHA_IN_BUFFER = 1, /*< Buffer for input to IPU */ + IPU_GRAPH_IN_BUFFER = 2, /*< Buffer for input to IPU */ + IPU_VIDEO_IN_BUFFER = 3, /*< Buffer for input to IPU */ + IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER, + IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER, +} ipu_buffer_t; + +#define IPU_PANEL_SERIAL 1 +#define IPU_PANEL_PARALLEL 2 + +struct ipu_channel { + u8 video_in_dma; + u8 alpha_in_dma; + u8 graph_in_dma; + u8 out_dma; +}; + +enum ipu_dmfc_type { + DMFC_NORMAL = 0, + DMFC_HIGH_RESOLUTION_DC, + DMFC_HIGH_RESOLUTION_DP, + DMFC_HIGH_RESOLUTION_ONLY_DP, +}; + + +/* + * Union of initialization parameters for a logical channel. + */ +typedef union { + struct { + uint32_t di; + unsigned char interlaced; + } mem_dc_sync; + struct { + uint32_t temp; + } mem_sdc_fg; + struct { + uint32_t di; + unsigned char interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + unsigned char alpha_chan_en; + } mem_dp_bg_sync; + struct { + uint32_t temp; + } mem_sdc_bg; + struct { + uint32_t di; + unsigned char interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + unsigned char alpha_chan_en; + } mem_dp_fg_sync; +} ipu_channel_params_t; + +/* + * Enumeration of IPU interrupts. + */ +enum ipu_irq_line { + IPU_IRQ_DP_SF_END = 448 + 3, + IPU_IRQ_DC_FC_1 = 448 + 9, +}; + +/* + * Bitfield of Display Interface signal polarities. + */ +typedef struct { + unsigned datamask_en:1; + unsigned ext_clk:1; + unsigned interlaced:1; + unsigned odd_field_first:1; + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +} ipu_di_signal_cfg_t; + +typedef enum { + RGB, + YCbCr, + YUV +} ipu_color_space_t; + +/* Common IPU API */ +int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params); +void ipu_uninit_channel(ipu_channel_t channel); + +int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u_offset, uint32_t v_offset); + +int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum, dma_addr_t phyaddr); + +int32_t ipu_is_channel_busy(ipu_channel_t channel); +void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum); +int32_t ipu_enable_channel(ipu_channel_t channel); +int32_t ipu_disable_channel(ipu_channel_t channel); + +int32_t ipu_init_sync_panel(int disp, + uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig); + +int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, + uint8_t alpha); +int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, + uint32_t colorKey); + +uint32_t bytes_per_pixel(uint32_t fmt); + +void clk_enable(struct clk *clk); +void clk_disable(struct clk *clk); +u32 clk_get_rate(struct clk *clk); +int clk_set_rate(struct clk *clk, unsigned long rate); +long clk_round_rate(struct clk *clk, unsigned long rate); +int clk_set_parent(struct clk *clk, struct clk *parent); +int clk_get_usecount(struct clk *clk); +struct clk *clk_get_parent(struct clk *clk); + +void ipu_dump_registers(void); +int ipu_probe(void); +bool ipu_clk_enabled(void); + +void ipu_dmfc_init(int dmfc_type, int first); +void ipu_init_dc_mappings(void); +void ipu_dmfc_set_wait4eot(int dma_chan, int width); +void ipu_dc_init(int dc_chan, int di, unsigned char interlaced); +void ipu_dc_uninit(int dc_chan); +void ipu_dp_dc_enable(ipu_channel_t channel); +int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt); +void ipu_dp_uninit(ipu_channel_t channel); +void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap); +ipu_color_space_t format_to_colorspace(uint32_t fmt); +#endif diff --git a/drivers/video/imx/ipu_common.c b/drivers/video/imx/ipu_common.c new file mode 100644 index 0000000000..cbe1984e4f --- /dev/null +++ b/drivers/video/imx/ipu_common.c @@ -0,0 +1,1265 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + */ + +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipu.h" +#include "ipu_regs.h" + +extern struct mxc_ccm_reg *mxc_ccm; +extern u32 *ipu_cpmem_base; + +struct ipu_ch_param_word { + uint32_t data[5]; + uint32_t res[3]; +}; + +struct ipu_ch_param { + struct ipu_ch_param_word word[2]; +}; + +#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch)) + +#define _param_word(base, w) \ + (((struct ipu_ch_param *)(base))->word[(w)].data) + +#define ipu_ch_param_set_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + _param_word(base, w)[i] |= (v) << off; \ + if (((bit) + (size) - 1) / 32 > i) { \ + _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \ + } \ +} + +#define ipu_ch_param_mod_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp = _param_word(base, w)[i]; \ + temp &= ~(mask << off); \ + _param_word(base, w)[i] = temp | (v) << off; \ + if (((bit) + (size) - 1) / 32 > i) { \ + temp = _param_word(base, w)[i + 1]; \ + temp &= ~(mask >> (32 - off)); \ + _param_word(base, w)[i + 1] = \ + temp | ((v) >> (off ? (32 - off) : 0)); \ + } \ +} + +#define ipu_ch_param_read_field(base, w, bit, size) ({ \ + u32 temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp1 = _param_word(base, w)[i]; \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size) - 1) / 32 > i) { \ + temp2 = _param_word(base, w)[i + 1]; \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + +#define IPU_SW_RST_TOUT_USEC (10000) + +#define IPUV3_CLK_MX51 133000000 +#define IPUV3_CLK_MX53 200000000 +#define IPUV3_CLK_MX6Q 264000000 +#define IPUV3_CLK_MX6DL 198000000 + +void clk_enable(struct clk *clk) +{ + if (clk) { + if (clk->usecount++ == 0) { + clk->enable(clk); + } + } +} + +void clk_disable(struct clk *clk) +{ + if (clk) { + if (!(--clk->usecount)) { + if (clk->disable) + clk->disable(clk); + } + } +} + +int clk_get_usecount(struct clk *clk) +{ + if (clk == NULL) + return 0; + + return clk->usecount; +} + +u32 clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->rate; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->parent; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + if (!clk) + return 0; + + if (clk->set_rate) + clk->set_rate(clk, rate); + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk == NULL || !clk->round_rate) + return 0; + + return clk->round_rate(clk, rate); +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + clk->parent = parent; + if (clk->set_parent) + return clk->set_parent(clk, parent); + return 0; +} + +static int clk_ipu_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(clk->enable_reg); + reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift; + __raw_writel(reg, clk->enable_reg); + +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) + /* Handshake with IPU when certain clock rates are changed. */ + reg = __raw_readl(&mxc_ccm->ccdr); + reg &= ~MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, &mxc_ccm->ccdr); + + /* Handshake with IPU when LPM is entered as its enabled. */ + reg = __raw_readl(&mxc_ccm->clpcr); + reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, &mxc_ccm->clpcr); +#endif + return 0; +} + +static void clk_ipu_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(clk->enable_reg); + reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); + __raw_writel(reg, clk->enable_reg); + +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) + /* + * No handshake with IPU whe dividers are changed + * as its not enabled. + */ + reg = __raw_readl(&mxc_ccm->ccdr); + reg |= MXC_CCM_CCDR_IPU_HS_MASK; + __raw_writel(reg, &mxc_ccm->ccdr); + + /* No handshake with IPU when LPM is entered as its not enabled. */ + reg = __raw_readl(&mxc_ccm->clpcr); + reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; + __raw_writel(reg, &mxc_ccm->clpcr); +#endif +} + + +static struct clk ipu_clk = { + .name = "ipu_clk", +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) + .enable_reg = (u32 *)(CCM_BASE_ADDR + + offsetof(struct mxc_ccm_reg, CCGR5)), + .enable_shift = MXC_CCM_CCGR5_IPU_OFFSET, +#else + .enable_reg = (u32 *)(CCM_BASE_ADDR + + offsetof(struct mxc_ccm_reg, CCGR3)), + .enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET, +#endif + .enable = clk_ipu_enable, + .disable = clk_ipu_disable, + .usecount = 0, +}; + +#if !defined CONFIG_SYS_LDB_CLOCK +#define CONFIG_SYS_LDB_CLOCK 65000000 +#endif + +static struct clk ldb_clk = { + .name = "ldb_clk", + .rate = CONFIG_SYS_LDB_CLOCK, + .usecount = 0, +}; + +/* Globals */ +struct clk *g_ipu_clk; +struct clk *g_ldb_clk; +unsigned char g_ipu_clk_enabled; +struct clk *g_di_clk[2]; +struct clk *g_pixel_clk[2]; +unsigned char g_dc_di_assignment[10]; +uint32_t g_channel_init_mask; +uint32_t g_channel_enable_mask; + +static int ipu_dc_use_count; +static int ipu_dp_use_count; +static int ipu_dmfc_use_count; +static int ipu_di_use_count[2]; + +u32 *ipu_cpmem_base; +u32 *ipu_dc_tmpl_reg; + +/* Static functions */ + +static inline void ipu_ch_param_set_high_priority(uint32_t ch) +{ + ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 93, 2, 1); +}; + +static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) +{ + return ((uint32_t) ch >> (6 * type)) & 0x3F; +}; + +/* Either DP BG or DP FG can be graphic window */ +static inline int ipu_is_dp_graphic_chan(uint32_t dma_chan) +{ + return (dma_chan == 23 || dma_chan == 27); +} + +static inline int ipu_is_dmfc_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 23) && (dma_chan <= 29)); +} + + +static inline void ipu_ch_param_set_buffer(uint32_t ch, int bufNum, + dma_addr_t phyaddr) +{ + ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 29 * bufNum, 29, + phyaddr / 8); +}; + +#define idma_is_valid(ch) (ch != NO_DMA) +#define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) +#define idma_is_set(reg, dma) (__raw_readl(reg(dma)) & idma_mask(dma)) + +static void ipu_pixel_clk_recalc(struct clk *clk) +{ + u32 div; + u64 final_rate = (unsigned long long)clk->parent->rate * 16; + + div = __raw_readl(DI_BS_CLKGEN0(clk->id)); + debug("read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n", + div, final_rate, clk->parent->rate); + + clk->rate = 0; + if (div != 0) { + do_div(final_rate, div); + clk->rate = final_rate; + } +} + +static unsigned long ipu_pixel_clk_round_rate(struct clk *clk, + unsigned long rate) +{ + u64 div, final_rate; + u32 remainder; + u64 parent_rate = (unsigned long long)clk->parent->rate * 16; + + /* + * Calculate divider + * Fractional part is 4 bits, + * so simply multiply by 2^4 to get fractional part. + */ + div = parent_rate; + remainder = do_div(div, rate); + /* Round the divider value */ + if (remainder > (rate / 2)) + div++; + if (div < 0x10) /* Min DI disp clock divider is 1 */ + div = 0x10; + if (div & ~0xFEF) + div &= 0xFF8; + else { + /* Round up divider if it gets us closer to desired pix clk */ + if ((div & 0xC) == 0xC) { + div += 0x10; + div &= ~0xF; + } + } + final_rate = parent_rate; + do_div(final_rate, div); + + return final_rate; +} + +static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) +{ + u64 div, parent_rate; + u32 remainder; + + parent_rate = (unsigned long long)clk->parent->rate * 16; + div = parent_rate; + remainder = do_div(div, rate); + /* Round the divider value */ + if (remainder > (rate / 2)) + div++; + + /* Round up divider if it gets us closer to desired pix clk */ + if ((div & 0xC) == 0xC) { + div += 0x10; + div &= ~0xF; + } + if (div > 0x1000) + debug("Overflow, DI_BS_CLKGEN0 div:0x%x\n", (u32)div); + + __raw_writel(div, DI_BS_CLKGEN0(clk->id)); + + /* + * Setup pixel clock timing + * Down time is half of period + */ + __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id)); + + do_div(parent_rate, div); + + clk->rate = parent_rate; + + return 0; +} + +static int ipu_pixel_clk_enable(struct clk *clk) +{ + u32 disp_gen = __raw_readl(IPU_DISP_GEN); + disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE; + __raw_writel(disp_gen, IPU_DISP_GEN); + + return 0; +} + +static void ipu_pixel_clk_disable(struct clk *clk) +{ + u32 disp_gen = __raw_readl(IPU_DISP_GEN); + disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE; + __raw_writel(disp_gen, IPU_DISP_GEN); + +} + +static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) +{ + u32 di_gen = __raw_readl(DI_GENERAL(clk->id)); + + if (parent == g_ipu_clk) + di_gen &= ~DI_GEN_DI_CLK_EXT; + else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_ldb_clk) + di_gen |= DI_GEN_DI_CLK_EXT; + else + return -EINVAL; + + __raw_writel(di_gen, DI_GENERAL(clk->id)); + ipu_pixel_clk_recalc(clk); + return 0; +} + +static struct clk pixel_clk[] = { + { + .name = "pixel_clk", + .id = 0, + .recalc = ipu_pixel_clk_recalc, + .set_rate = ipu_pixel_clk_set_rate, + .round_rate = ipu_pixel_clk_round_rate, + .set_parent = ipu_pixel_clk_set_parent, + .enable = ipu_pixel_clk_enable, + .disable = ipu_pixel_clk_disable, + .usecount = 0, + }, + { + .name = "pixel_clk", + .id = 1, + .recalc = ipu_pixel_clk_recalc, + .set_rate = ipu_pixel_clk_set_rate, + .round_rate = ipu_pixel_clk_round_rate, + .set_parent = ipu_pixel_clk_set_parent, + .enable = ipu_pixel_clk_enable, + .disable = ipu_pixel_clk_disable, + .usecount = 0, + }, +}; + +/* + * This function resets IPU + */ +static void ipu_reset(void) +{ + u32 *reg; + u32 value; + int timeout = IPU_SW_RST_TOUT_USEC; + + reg = (u32 *)SRC_BASE_ADDR; + value = __raw_readl(reg); + value = value | SW_IPU_RST; + __raw_writel(value, reg); + + while (__raw_readl(reg) & SW_IPU_RST) { + udelay(1); + if (!(timeout--)) { + printf("ipu software reset timeout\n"); + break; + } + }; +} + +/* + * This function is called by the driver framework to initialize the IPU + * hardware. + * + * @param dev The device structure for the IPU passed in by the + * driver framework. + * + * @return Returns 0 on success or negative error code on error + */ +int ipu_probe(void) +{ + unsigned long ipu_base; +#if defined CONFIG_MX51 + u32 temp; + + u32 *reg_hsc_mcd = (u32 *)MIPI_HSC_BASE_ADDR; + u32 *reg_hsc_mxt_conf = (u32 *)(MIPI_HSC_BASE_ADDR + 0x800); + + __raw_writel(0xF00, reg_hsc_mcd); + + /* CSI mode reserved*/ + temp = __raw_readl(reg_hsc_mxt_conf); + __raw_writel(temp | 0x0FF, reg_hsc_mxt_conf); + + temp = __raw_readl(reg_hsc_mxt_conf); + __raw_writel(temp | 0x10000, reg_hsc_mxt_conf); +#endif + + ipu_base = IPU_CTRL_BASE_ADDR; + ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE); + ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE); + + g_pixel_clk[0] = &pixel_clk[0]; + g_pixel_clk[1] = &pixel_clk[1]; + + g_ipu_clk = &ipu_clk; +#if defined(CONFIG_MX51) + g_ipu_clk->rate = IPUV3_CLK_MX51; +#elif defined(CONFIG_MX53) + g_ipu_clk->rate = IPUV3_CLK_MX53; +#else + g_ipu_clk->rate = is_mx6sdl() ? IPUV3_CLK_MX6DL : IPUV3_CLK_MX6Q; +#endif + debug("ipu_clk = %u\n", clk_get_rate(g_ipu_clk)); + g_ldb_clk = &ldb_clk; + debug("ldb_clk = %u\n", clk_get_rate(g_ldb_clk)); + ipu_reset(); + + clk_set_parent(g_pixel_clk[0], g_ipu_clk); + clk_set_parent(g_pixel_clk[1], g_ipu_clk); + clk_enable(g_ipu_clk); + + g_di_clk[0] = NULL; + g_di_clk[1] = NULL; + + __raw_writel(0x807FFFFF, IPU_MEM_RST); + while (__raw_readl(IPU_MEM_RST) & 0x80000000) + ; + + ipu_init_dc_mappings(); + + __raw_writel(0, IPU_INT_CTRL(5)); + __raw_writel(0, IPU_INT_CTRL(6)); + __raw_writel(0, IPU_INT_CTRL(9)); + __raw_writel(0, IPU_INT_CTRL(10)); + + /* DMFC Init */ + ipu_dmfc_init(DMFC_NORMAL, 1); + + /* Set sync refresh channels as high priority */ + __raw_writel(0x18800000L, IDMAC_CHA_PRI(0)); + + /* Set MCU_T to divide MCU access window into 2 */ + __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); + + clk_disable(g_ipu_clk); + + return 0; +} + +void ipu_dump_registers(void) +{ + debug("IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF)); + debug("IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF)); + debug("IDMAC_CHA_EN1 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_EN(0))); + debug("IDMAC_CHA_EN2 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_EN(32))); + debug("IDMAC_CHA_PRI1 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_PRI(0))); + debug("IDMAC_CHA_PRI2 = \t0x%08X\n", + __raw_readl(IDMAC_CHA_PRI(32))); + debug("IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", + __raw_readl(IPU_CHA_DB_MODE_SEL(0))); + debug("IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", + __raw_readl(IPU_CHA_DB_MODE_SEL(32))); + debug("DMFC_WR_CHAN = \t0x%08X\n", + __raw_readl(DMFC_WR_CHAN)); + debug("DMFC_WR_CHAN_DEF = \t0x%08X\n", + __raw_readl(DMFC_WR_CHAN_DEF)); + debug("DMFC_DP_CHAN = \t0x%08X\n", + __raw_readl(DMFC_DP_CHAN)); + debug("DMFC_DP_CHAN_DEF = \t0x%08X\n", + __raw_readl(DMFC_DP_CHAN_DEF)); + debug("DMFC_IC_CTRL = \t0x%08X\n", + __raw_readl(DMFC_IC_CTRL)); + debug("IPU_FS_PROC_FLOW1 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW1)); + debug("IPU_FS_PROC_FLOW2 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW2)); + debug("IPU_FS_PROC_FLOW3 = \t0x%08X\n", + __raw_readl(IPU_FS_PROC_FLOW3)); + debug("IPU_FS_DISP_FLOW1 = \t0x%08X\n", + __raw_readl(IPU_FS_DISP_FLOW1)); +} + +/* + * This function is called to initialize a logical IPU channel. + * + * @param channel Input parameter for the logical channel ID to init. + * + * @param params Input parameter containing union of channel + * initialization parameters. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) +{ + int ret = 0; + uint32_t ipu_conf; + + debug("init channel = %d\n", IPU_CHAN_ID(channel)); + + if (g_ipu_clk_enabled == 0) { + g_ipu_clk_enabled = 1; + clk_enable(g_ipu_clk); + } + + + if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) { + printf("Warning: channel already initialized %d\n", + IPU_CHAN_ID(channel)); + } + + ipu_conf = __raw_readl(IPU_CONF); + + switch (channel) { + case MEM_DC_SYNC: + if (params->mem_dc_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + g_dc_di_assignment[1] = params->mem_dc_sync.di; + ipu_dc_init(1, params->mem_dc_sync.di, + params->mem_dc_sync.interlaced); + ipu_di_use_count[params->mem_dc_sync.di]++; + ipu_dc_use_count++; + ipu_dmfc_use_count++; + break; + case MEM_BG_SYNC: + if (params->mem_dp_bg_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + g_dc_di_assignment[5] = params->mem_dp_bg_sync.di; + ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt, + params->mem_dp_bg_sync.out_pixel_fmt); + ipu_dc_init(5, params->mem_dp_bg_sync.di, + params->mem_dp_bg_sync.interlaced); + ipu_di_use_count[params->mem_dp_bg_sync.di]++; + ipu_dc_use_count++; + ipu_dp_use_count++; + ipu_dmfc_use_count++; + break; + case MEM_FG_SYNC: + ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt, + params->mem_dp_fg_sync.out_pixel_fmt); + + ipu_dc_use_count++; + ipu_dp_use_count++; + ipu_dmfc_use_count++; + break; + default: + printf("Missing channel initialization\n"); + break; + } + + /* Enable IPU sub module */ + g_channel_init_mask |= 1L << IPU_CHAN_ID(channel); + if (ipu_dc_use_count == 1) + ipu_conf |= IPU_CONF_DC_EN; + if (ipu_dp_use_count == 1) + ipu_conf |= IPU_CONF_DP_EN; + if (ipu_dmfc_use_count == 1) + ipu_conf |= IPU_CONF_DMFC_EN; + if (ipu_di_use_count[0] == 1) { + ipu_conf |= IPU_CONF_DI0_EN; + } + if (ipu_di_use_count[1] == 1) { + ipu_conf |= IPU_CONF_DI1_EN; + } + + __raw_writel(ipu_conf, IPU_CONF); + +err: + return ret; +} + +/* + * This function is called to uninitialize a logical IPU channel. + * + * @param channel Input parameter for the logical channel ID to uninit. + */ +void ipu_uninit_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma, out_dma = 0; + uint32_t ipu_conf; + + if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + debug("Channel already uninitialized %d\n", + IPU_CHAN_ID(channel)); + return; + } + + /* + * Make sure channel is disabled + * Get input and output dma channels + */ + in_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + out_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if (idma_is_set(IDMAC_CHA_EN, in_dma) || + idma_is_set(IDMAC_CHA_EN, out_dma)) { + printf( + "Channel %d is not disabled, disable first\n", + IPU_CHAN_ID(channel)); + return; + } + + ipu_conf = __raw_readl(IPU_CONF); + + /* Reset the double buffer */ + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma)); + __raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma)); + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma)); + __raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma)); + + switch (channel) { + case MEM_DC_SYNC: + ipu_dc_uninit(1); + ipu_di_use_count[g_dc_di_assignment[1]]--; + ipu_dc_use_count--; + ipu_dmfc_use_count--; + break; + case MEM_BG_SYNC: + ipu_dp_uninit(channel); + ipu_dc_uninit(5); + ipu_di_use_count[g_dc_di_assignment[5]]--; + ipu_dc_use_count--; + ipu_dp_use_count--; + ipu_dmfc_use_count--; + break; + case MEM_FG_SYNC: + ipu_dp_uninit(channel); + ipu_dc_use_count--; + ipu_dp_use_count--; + ipu_dmfc_use_count--; + break; + default: + break; + } + + g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); + + if (ipu_dc_use_count == 0) + ipu_conf &= ~IPU_CONF_DC_EN; + if (ipu_dp_use_count == 0) + ipu_conf &= ~IPU_CONF_DP_EN; + if (ipu_dmfc_use_count == 0) + ipu_conf &= ~IPU_CONF_DMFC_EN; + if (ipu_di_use_count[0] == 0) { + ipu_conf &= ~IPU_CONF_DI0_EN; + } + if (ipu_di_use_count[1] == 0) { + ipu_conf &= ~IPU_CONF_DI1_EN; + } + + __raw_writel(ipu_conf, IPU_CONF); + + if (ipu_conf == 0) { + clk_disable(g_ipu_clk); + g_ipu_clk_enabled = 0; + } + +} + +static inline void ipu_ch_param_dump(int ch) +{ +#ifdef DEBUG + struct ipu_ch_param *p = ipu_ch_param_addr(ch); + debug("ch %d word 0 - %08X %08X %08X %08X %08X\n", ch, + p->word[0].data[0], p->word[0].data[1], p->word[0].data[2], + p->word[0].data[3], p->word[0].data[4]); + debug("ch %d word 1 - %08X %08X %08X %08X %08X\n", ch, + p->word[1].data[0], p->word[1].data[1], p->word[1].data[2], + p->word[1].data[3], p->word[1].data[4]); + debug("PFS 0x%x, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 85, 4)); + debug("BPP 0x%x, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3)); + debug("NPB 0x%x\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7)); + + debug("FW %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 125, 13)); + debug("FH %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 138, 12)); + debug("Stride %d\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14)); + + debug("Width0 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 116, 3)); + debug("Width1 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 119, 3)); + debug("Width2 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 122, 3)); + debug("Width3 %d+1, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 125, 3)); + debug("Offset0 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 128, 5)); + debug("Offset1 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 133, 5)); + debug("Offset2 %d, ", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 138, 5)); + debug("Offset3 %d\n", + ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 143, 5)); +#endif +} + +static inline void ipu_ch_params_set_packing(struct ipu_ch_param *p, + int red_width, int red_offset, + int green_width, int green_offset, + int blue_width, int blue_offset, + int alpha_width, int alpha_offset) +{ + /* Setup red width and offset */ + ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); + ipu_ch_param_set_field(p, 1, 128, 5, red_offset); + /* Setup green width and offset */ + ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); + ipu_ch_param_set_field(p, 1, 133, 5, green_offset); + /* Setup blue width and offset */ + ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); + ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); + /* Setup alpha width and offset */ + ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); + ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); +} + +static void ipu_ch_param_init(int ch, + uint32_t pixel_fmt, uint32_t width, + uint32_t height, uint32_t stride, + uint32_t u, uint32_t v, + uint32_t uv_stride, dma_addr_t addr0, + dma_addr_t addr1) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + struct ipu_ch_param params; + + memset(¶ms, 0, sizeof(params)); + + ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); + + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); + } else { + ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); + } + + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr0 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, addr1 >> 3); + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 5); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ + + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + break; + case IPU_PIX_FMT_RGB565: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16); + break; + case IPU_PIX_FMT_BGR24: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24); + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0); + break; + case IPU_PIX_FMT_ABGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + + ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_UYVY: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + break; + case IPU_PIX_FMT_YUYV: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + /* burst size */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + uv_stride = uv_stride*2; + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); + } + break; + case IPU_PIX_FMT_YVU422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = (v == 0) ? stride * height : v; + u_offset = (u == 0) ? v_offset + v_offset / 2 : u; + break; + case IPU_PIX_FMT_YUV422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = (u == 0) ? stride * height : u; + v_offset = (v == 0) ? u_offset + u_offset / 2 : v; + break; + case IPU_PIX_FMT_NV12: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + break; + default: + puts("mxc ipu: unimplemented pixel format\n"); + break; + } + + + if (uv_stride) + ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1); + + /* Get the uv offset from user when need cropping */ + if (u || v) { + u_offset = u; + v_offset = v; + } + + /* UBO and VBO are 22-bit */ + if (u_offset/8 > 0x3fffff) + puts("The value of U offset exceeds IPU limitation\n"); + if (v_offset/8 > 0x3fffff) + puts("The value of V offset exceeds IPU limitation\n"); + + ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8); + ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8); + + debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch)); + memcpy(ipu_ch_param_addr(ch), ¶ms, sizeof(params)); +}; + +/* + * This function is called to initialize a buffer for logical IPU channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width Input parameter for width of buffer in pixels. + * + * @param height Input parameter for height of buffer in pixels. + * + * @param stride Input parameter for stride length of buffer + * in pixels. + * + * @param phyaddr_0 Input parameter buffer 0 physical address. + * + * @param phyaddr_1 Input parameter buffer 1 physical address. + * Setting this to a value other than NULL enables + * double buffering mode. + * + * @param u private u offset for additional cropping, + * zero if not used. + * + * @param v private v offset for additional cropping, + * zero if not used. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u, uint32_t v) +{ + uint32_t reg; + uint32_t dma_chan; + + dma_chan = channel_2_dma(channel, type); + if (!idma_is_valid(dma_chan)) + return -EINVAL; + + if (stride < width * bytes_per_pixel(pixel_fmt)) + stride = width * bytes_per_pixel(pixel_fmt); + + if (stride % 4) { + printf( + "Stride not 32-bit aligned, stride = %d\n", stride); + return -EINVAL; + } + /* Build parameter memory data for DMA channel */ + ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0, + phyaddr_0, phyaddr_1); + + if (ipu_is_dmfc_chan(dma_chan)) { + ipu_dmfc_set_wait4eot(dma_chan, width); + } + + if (idma_is_set(IDMAC_CHA_PRI, dma_chan)) + ipu_ch_param_set_high_priority(dma_chan); + + ipu_ch_param_dump(dma_chan); + + reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan)); + if (phyaddr_1) + reg |= idma_mask(dma_chan); + else + reg &= ~idma_mask(dma_chan); + __raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan)); + + /* Reset to buffer 0 */ + __raw_writel(idma_mask(dma_chan), IPU_CHA_CUR_BUF(dma_chan)); + + return 0; +} + +/* + * This function enables a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_enable_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { + printf("Warning: channel already enabled %d\n", + IPU_CHAN_ID(channel)); + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if (idma_is_valid(in_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(in_dma)); + __raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(out_dma)); + __raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + } + + if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || + (channel == MEM_FG_SYNC)) + ipu_dp_dc_enable(channel); + + g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel); + + return 0; +} + +/* + * This function clear buffer ready for a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to clear. + * + * @param bufNum Input parameter for which buffer number clear + * ready state. + * + */ +void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum) +{ + uint32_t dma_ch = channel_2_dma(channel, type); + + if (!idma_is_valid(dma_ch)) + return; + + __raw_writel(0xF0000000, IPU_GPR); /* write one to clear */ + if (bufNum == 0) { + if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) { + __raw_writel(idma_mask(dma_ch), + IPU_CHA_BUF0_RDY(dma_ch)); + } + } else { + if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) { + __raw_writel(idma_mask(dma_ch), + IPU_CHA_BUF1_RDY(dma_ch)); + } + } + __raw_writel(0x0, IPU_GPR); /* write one to set */ +} + +/* + * This function disables a logical channel. + * + * @param channel Input parameter for the logical channel ID. + * + * @param wait_for_stop Flag to set whether to wait for channel end + * of frame or return immediately. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_disable_channel(ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + debug("Channel already disabled %d\n", + IPU_CHAN_ID(channel)); + return 0; + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if ((idma_is_valid(in_dma) && + !idma_is_set(IDMAC_CHA_EN, in_dma)) + && (idma_is_valid(out_dma) && + !idma_is_set(IDMAC_CHA_EN, out_dma))) + return -EINVAL; + + if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || + (channel == MEM_DC_SYNC)) { + ipu_dp_dc_disable(channel, 0); + } + + /* Disable DMA channel(s) */ + if (idma_is_valid(in_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(in_dma)); + __raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + __raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = __raw_readl(IDMAC_CHA_EN(out_dma)); + __raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma)); + } + + g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); + + /* Set channel buffers NOT to be ready */ + if (idma_is_valid(in_dma)) { + ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 0); + ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 1); + } + if (idma_is_valid(out_dma)) { + ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 0); + ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 1); + } + + return 0; +} + +uint32_t bytes_per_pixel(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: /*generic data */ + case IPU_PIX_FMT_RGB332: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YUV422P: + return 1; + break; + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + return 2; + break; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + return 3; + break; + case IPU_PIX_FMT_GENERIC_32: /*generic data */ + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + return 4; + break; + default: + return 1; + break; + } + return 0; +} + +ipu_color_space_t format_to_colorspace(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB666: + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + case IPU_PIX_FMT_LVDS666: + case IPU_PIX_FMT_LVDS888: + return RGB; + break; + + default: + return YCbCr; + break; + } + return RGB; +} + +/* should be removed when clk framework is availiable */ +int ipu_set_ldb_clock(int rate) +{ + ldb_clk.rate = rate; + + return 0; +} + +bool ipu_clk_enabled(void) +{ + return g_ipu_clk_enabled; +} diff --git a/drivers/video/imx/ipu_disp.c b/drivers/video/imx/ipu_disp.c new file mode 100644 index 0000000000..5c7722962d --- /dev/null +++ b/drivers/video/imx/ipu_disp.c @@ -0,0 +1,1291 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include "ipu.h" +#include "ipu_regs.h" + +enum csc_type_t { + RGB2YUV = 0, + YUV2RGB, + RGB2RGB, + YUV2YUV, + CSC_NONE, + CSC_NUM +}; + +struct dp_csc_param_t { + int mode; + const int (*coeff)[5][3]; +}; + +#define SYNC_WAVE 0 + +/* DC display ID assignments */ +#define DC_DISP_ID_SYNC(di) (di) +#define DC_DISP_ID_SERIAL 2 +#define DC_DISP_ID_ASYNC 3 + +int dmfc_type_setup; +static int dmfc_size_28, dmfc_size_29, dmfc_size_24, dmfc_size_27, dmfc_size_23; +int g_di1_tvout; + +extern struct clk *g_ipu_clk; +extern struct clk *g_ldb_clk; +extern struct clk *g_di_clk[2]; +extern struct clk *g_pixel_clk[2]; + +extern unsigned char g_ipu_clk_enabled; +extern unsigned char g_dc_di_assignment[]; + +void ipu_dmfc_init(int dmfc_type, int first) +{ + u32 dmfc_wr_chan, dmfc_dp_chan; + + if (first) { + if (dmfc_type_setup > dmfc_type) + dmfc_type = dmfc_type_setup; + else + dmfc_type_setup = dmfc_type; + + /* disable DMFC-IC channel*/ + __raw_writel(0x2, DMFC_IC_CTRL); + } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) { + printf("DMFC high resolution has set, will not change\n"); + return; + } else + dmfc_type_setup = dmfc_type; + + if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) { + /* 1 - segment 0~3; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC DC HIGH RES: 1(0~3), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000088; + dmfc_dp_chan = 0x00009694; + dmfc_size_28 = 256 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 128 * 4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) { + /* 1 - segment 0, 1; + * 5B - segement 2~5; + * 5F - segement 6,7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC DP HIGH RES: 1(0,1), 5B(2~5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x0000968a; + dmfc_size_28 = 128 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 256 * 4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) { + /* 5B - segement 0~3; + * 5F - segement 4~7; + * 1, 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC ONLY-DP HIGH RES: 5B(0~3), 5F(4~7)\n"); + dmfc_wr_chan = 0x00000000; + dmfc_dp_chan = 0x00008c88; + dmfc_size_28 = 0; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 256 * 4; + dmfc_size_23 = 256 * 4; + } else { + /* 1 - segment 0, 1; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + debug("IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x00009694; + dmfc_size_28 = 128 * 4; + dmfc_size_29 = 0; + dmfc_size_24 = 0; + dmfc_size_27 = 128 * 4; + dmfc_size_23 = 128 * 4; + } + __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN); + __raw_writel(0x202020F6, DMFC_WR_CHAN_DEF); + __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN); + /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */ + __raw_writel(0x2020F6F6, DMFC_DP_CHAN_DEF); +} + +void ipu_dmfc_set_wait4eot(int dma_chan, int width) +{ + u32 dmfc_gen1 = __raw_readl(DMFC_GENERAL1); + + if (width >= HIGH_RESOLUTION_WIDTH) { + if (dma_chan == 23) + ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DP, 0); + else if (dma_chan == 28) + ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DC, 0); + } + + if (dma_chan == 23) { /*5B*/ + if (dmfc_size_23 / width > 3) + dmfc_gen1 |= 1UL << 20; + else + dmfc_gen1 &= ~(1UL << 20); + } else if (dma_chan == 24) { /*6B*/ + if (dmfc_size_24 / width > 1) + dmfc_gen1 |= 1UL << 22; + else + dmfc_gen1 &= ~(1UL << 22); + } else if (dma_chan == 27) { /*5F*/ + if (dmfc_size_27 / width > 2) + dmfc_gen1 |= 1UL << 21; + else + dmfc_gen1 &= ~(1UL << 21); + } else if (dma_chan == 28) { /*1*/ + if (dmfc_size_28 / width > 2) + dmfc_gen1 |= 1UL << 16; + else + dmfc_gen1 &= ~(1UL << 16); + } else if (dma_chan == 29) { /*6F*/ + if (dmfc_size_29 / width > 1) + dmfc_gen1 |= 1UL << 23; + else + dmfc_gen1 &= ~(1UL << 23); + } + + __raw_writel(dmfc_gen1, DMFC_GENERAL1); +} + +static void ipu_di_data_wave_config(int di, + int wave_gen, + int access_size, int component_size) +{ + u32 reg; + reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | + (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); + __raw_writel(reg, DI_DW_GEN(di, wave_gen)); +} + +static void ipu_di_data_pin_config(int di, int wave_gen, int di_pin, int set, + int up, int down) +{ + u32 reg; + + reg = __raw_readl(DI_DW_GEN(di, wave_gen)); + reg &= ~(0x3 << (di_pin * 2)); + reg |= set << (di_pin * 2); + __raw_writel(reg, DI_DW_GEN(di, wave_gen)); + + __raw_writel((down << 16) | up, DI_DW_SET(di, wave_gen, set)); +} + +static void ipu_di_sync_config(int di, int wave_gen, + int run_count, int run_src, + int offset_count, int offset_src, + int repeat_count, int cnt_clr_src, + int cnt_polarity_gen_en, + int cnt_polarity_clr_src, + int cnt_polarity_trigger_src, + int cnt_up, int cnt_down) +{ + u32 reg; + + if ((run_count >= 0x1000) || (offset_count >= 0x1000) || + (repeat_count >= 0x1000) || + (cnt_up >= 0x400) || (cnt_down >= 0x400)) { + printf("DI%d counters out of range.\n", di); + return; + } + + reg = (run_count << 19) | (++run_src << 16) | + (offset_count << 3) | ++offset_src; + __raw_writel(reg, DI_SW_GEN0(di, wave_gen)); + reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) | + (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9); + reg |= (cnt_down << 16) | cnt_up; + if (repeat_count == 0) { + /* Enable auto reload */ + reg |= 0x10000000; + } + __raw_writel(reg, DI_SW_GEN1(di, wave_gen)); + reg = __raw_readl(DI_STP_REP(di, wave_gen)); + reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1))); + reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1)); + __raw_writel(reg, DI_STP_REP(di, wave_gen)); +} + +static void ipu_dc_map_config(int map, int byte_num, int offset, int mask) +{ + int ptr = map * 3 + byte_num; + u32 reg; + + reg = __raw_readl(DC_MAP_CONF_VAL(ptr)); + reg &= ~(0xFFFF << (16 * (ptr & 0x1))); + reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); + __raw_writel(reg, DC_MAP_CONF_VAL(ptr)); + + reg = __raw_readl(DC_MAP_CONF_PTR(map)); + reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num))); + reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); + __raw_writel(reg, DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_map_clear(int map) +{ + u32 reg = __raw_readl(DC_MAP_CONF_PTR(map)); + __raw_writel(reg & ~(0xFFFF << (16 * (map & 0x1))), + DC_MAP_CONF_PTR(map)); +} + +static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map, + int wave, int glue, int sync) +{ + u32 reg; + int stop = 1; + + reg = sync; + reg |= (glue << 4); + reg |= (++wave << 11); + reg |= (++map << 15); + reg |= (operand << 20) & 0xFFF00000; + __raw_writel(reg, ipu_dc_tmpl_reg + word * 2); + + reg = (operand >> 12); + reg |= opcode << 4; + reg |= (stop << 9); + __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1); +} + +static void ipu_dc_link_event(int chan, int event, int addr, int priority) +{ + u32 reg; + + reg = __raw_readl(DC_RL_CH(chan, event)); + reg &= ~(0xFFFF << (16 * (event & 0x1))); + reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); + __raw_writel(reg, DC_RL_CH(chan, event)); +} + +/* Y = R * 1.200 + G * 2.343 + B * .453 + 0.250; + * U = R * -.672 + G * -1.328 + B * 2.000 + 512.250.; + * V = R * 2.000 + G * -1.672 + B * -.328 + 512.250.; + */ +static const int rgb2ycbcr_coeff[5][3] = { + {0x4D, 0x96, 0x1D}, + {0x3D5, 0x3AB, 0x80}, + {0x80, 0x395, 0x3EB}, + {0x0000, 0x0200, 0x0200}, /* B0, B1, B2 */ + {0x2, 0x2, 0x2}, /* S0, S1, S2 */ +}; + +/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); + * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); + * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); + */ +static const int ycbcr2rgb_coeff[5][3] = { + {0x095, 0x000, 0x0CC}, + {0x095, 0x3CE, 0x398}, + {0x095, 0x0FF, 0x000}, + {0x3E42, 0x010A, 0x3DD6}, /*B0,B1,B2 */ + {0x1, 0x1, 0x1}, /*S0,S1,S2 */ +}; + +#define mask_a(a) ((u32)(a) & 0x3FF) +#define mask_b(b) ((u32)(b) & 0x3FFF) + +/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */ +static int rgb_to_yuv(int n, int red, int green, int blue) +{ + int c; + c = red * rgb2ycbcr_coeff[n][0]; + c += green * rgb2ycbcr_coeff[n][1]; + c += blue * rgb2ycbcr_coeff[n][2]; + c /= 16; + c += rgb2ycbcr_coeff[3][n] * 4; + c += 8; + c /= 16; + if (c < 0) + c = 0; + if (c > 255) + c = 255; + return c; +} + +/* + * Row is for BG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + * Column is for FG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + */ +static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = { + { + {DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, + {0, 0}, + {0, 0}, + {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, + {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} + }, + { + {0, 0}, + {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, + {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, + {0, 0}, + {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} + }, + { + {0, 0}, + {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, + {0, 0}, + {0, 0}, + {0, 0} + }, + { + {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }, + { + {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, + {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, + {0, 0}, + {0, 0}, + {0, 0} + } +}; + +static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE; +static int color_key_4rgb = 1; + +static void ipu_dp_csc_setup(int dp, struct dp_csc_param_t dp_csc_param, + unsigned char srm_mode_update) +{ + u32 reg; + const int (*coeff)[5][3]; + + if (dp_csc_param.mode >= 0) { + reg = __raw_readl(DP_COM_CONF()); + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + reg |= dp_csc_param.mode; + __raw_writel(reg, DP_COM_CONF()); + } + + coeff = dp_csc_param.coeff; + + if (coeff) { + __raw_writel(mask_a((*coeff)[0][0]) | + (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0()); + __raw_writel(mask_a((*coeff)[0][2]) | + (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1()); + __raw_writel(mask_a((*coeff)[1][1]) | + (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2()); + __raw_writel(mask_a((*coeff)[2][0]) | + (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3()); + __raw_writel(mask_a((*coeff)[2][2]) | + (mask_b((*coeff)[3][0]) << 16) | + ((*coeff)[4][0] << 30), DP_CSC_0()); + __raw_writel(mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) | + (mask_b((*coeff)[3][2]) << 16) | + ((*coeff)[4][2] << 30), DP_CSC_1()); + } + + if (srm_mode_update) { + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + } +} + +int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt) +{ + int in_fmt, out_fmt; + int dp; + int partial = 0; + uint32_t reg; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = 1; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = 0; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = 0; + } else { + return -EINVAL; + } + + in_fmt = format_to_colorspace(in_pixel_fmt); + out_fmt = format_to_colorspace(out_pixel_fmt); + + if (partial) { + if (in_fmt == RGB) { + if (out_fmt == RGB) + fg_csc_type = RGB2RGB; + else + fg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + fg_csc_type = YUV2RGB; + else + fg_csc_type = YUV2YUV; + } + } else { + if (in_fmt == RGB) { + if (out_fmt == RGB) + bg_csc_type = RGB2RGB; + else + bg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + bg_csc_type = YUV2RGB; + else + bg_csc_type = YUV2YUV; + } + } + + /* Transform color key from rgb to yuv if CSC is enabled */ + reg = __raw_readl(DP_COM_CONF()); + if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) && + (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) { + int red, green, blue; + int y, u, v; + uint32_t color_key = __raw_readl(DP_GRAPH_WIND_CTRL()) & + 0xFFFFFFL; + + debug("_ipu_dp_init color key 0x%x need change to yuv fmt!\n", + color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = rgb_to_yuv(0, red, green, blue); + u = rgb_to_yuv(1, red, green, blue); + v = rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0xFF000000L; + __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL()); + color_key_4rgb = 0; + + debug("_ipu_dp_init color key change to yuv fmt 0x%x!\n", + color_key); + } + + ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 1); + + return 0; +} + +void ipu_dp_uninit(ipu_channel_t channel) +{ + int dp; + int partial = 0; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = 1; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = 0; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = 0; + } else { + return; + } + + if (partial) + fg_csc_type = CSC_NONE; + else + bg_csc_type = CSC_NONE; + + ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 0); +} + +void ipu_dc_init(int dc_chan, int di, unsigned char interlaced) +{ + u32 reg = 0; + + if ((dc_chan == 1) || (dc_chan == 5)) { + if (interlaced) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1); + } else { + if (di) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, + 4, 1); + } else { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, + 7, 1); + } + } + ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); + + reg = 0x2; + reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + reg |= di << 2; + if (interlaced) + reg |= DC_WR_CH_CONF_FIELD_MODE; + } else if ((dc_chan == 8) || (dc_chan == 9)) { + /* async channels */ + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1); + + reg = 0x3; + reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + } + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan)); + + __raw_writel(0x00000084, DC_GEN); +} + +void ipu_dc_uninit(int dc_chan) +{ + if ((dc_chan == 1) || (dc_chan == 5)) { + ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); + } else if ((dc_chan == 8) || (dc_chan == 9)) { + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0); + ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0); + } +} + +void ipu_dp_dc_enable(ipu_channel_t channel) +{ + int di; + uint32_t reg; + uint32_t dc_chan; + + if (channel == MEM_DC_SYNC) + dc_chan = 1; + else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) + dc_chan = 5; + else + return; + + if (channel == MEM_FG_SYNC) { + /* Enable FG channel */ + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF()); + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + return; + } + + di = g_dc_di_assignment[dc_chan]; + + /* Make sure other DC sync channel is not assigned same DI */ + reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan)); + if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) { + reg &= ~DC_WR_CH_CONF_PROG_DI_ID; + reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID; + __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); + } + + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + clk_enable(g_pixel_clk[di]); +} + +static unsigned char dc_swap; + +void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) +{ + uint32_t reg; + uint32_t csc; + uint32_t dc_chan = 0; + int timeout = 50; + int irq = 0; + + dc_swap = swap; + + if (channel == MEM_DC_SYNC) { + dc_chan = 1; + irq = IPU_IRQ_DC_FC_1; + } else if (channel == MEM_BG_SYNC) { + dc_chan = 5; + irq = IPU_IRQ_DP_SF_END; + } else if (channel == MEM_FG_SYNC) { + /* Disable FG channel */ + dc_chan = 5; + + reg = __raw_readl(DP_COM_CONF()); + csc = reg & DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_FG) + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + reg &= ~DP_COM_CONF_FG_EN; + __raw_writel(reg, DP_COM_CONF()); + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + timeout = 50; + + /* + * Wait for DC triple buffer to empty, + * this check is useful for tv overlay. + */ + if (g_dc_di_assignment[dc_chan] == 0) + while ((__raw_readl(DC_STAT) & 0x00000002) + != 0x00000002) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + else if (g_dc_di_assignment[dc_chan] == 1) + while ((__raw_readl(DC_STAT) & 0x00000020) + != 0x00000020) { + udelay(2000); + timeout -= 2; + if (timeout <= 0) + break; + } + return; + } else { + return; + } + + if (dc_swap) { + /* Swap DC channel 1 and 5 settings, and disable old dc chan */ + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + reg ^= DC_WR_CH_CONF_PROG_DI_ID; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + } else { + /* Make sure that we leave at the irq starting edge */ + __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq)); + do { + reg = __raw_readl(IPUIRQ_2_STATREG(irq)); + } while (!(reg & IPUIRQ_2_MASK(irq))); + + reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); + + reg = __raw_readl(IPU_DISP_GEN); + if (g_dc_di_assignment[dc_chan]) + reg &= ~DI1_COUNTER_RELEASE; + else + reg &= ~DI0_COUNTER_RELEASE; + __raw_writel(reg, IPU_DISP_GEN); + + /* Clock is already off because it must be done quickly, but + we need to fix the ref count */ + clk_disable(g_pixel_clk[g_dc_di_assignment[dc_chan]]); + } +} + +void ipu_init_dc_mappings(void) +{ + /* IPU_PIX_FMT_RGB24 */ + ipu_dc_map_clear(0); + ipu_dc_map_config(0, 0, 7, 0xFF); + ipu_dc_map_config(0, 1, 15, 0xFF); + ipu_dc_map_config(0, 2, 23, 0xFF); + + /* IPU_PIX_FMT_RGB666 */ + ipu_dc_map_clear(1); + ipu_dc_map_config(1, 0, 5, 0xFC); + ipu_dc_map_config(1, 1, 11, 0xFC); + ipu_dc_map_config(1, 2, 17, 0xFC); + + /* IPU_PIX_FMT_YUV444 */ + ipu_dc_map_clear(2); + ipu_dc_map_config(2, 0, 15, 0xFF); + ipu_dc_map_config(2, 1, 23, 0xFF); + ipu_dc_map_config(2, 2, 7, 0xFF); + + /* IPU_PIX_FMT_RGB565 */ + ipu_dc_map_clear(3); + ipu_dc_map_config(3, 0, 4, 0xF8); + ipu_dc_map_config(3, 1, 10, 0xFC); + ipu_dc_map_config(3, 2, 15, 0xF8); + + /* IPU_PIX_FMT_LVDS666 */ + ipu_dc_map_clear(4); + ipu_dc_map_config(4, 0, 5, 0xFC); + ipu_dc_map_config(4, 1, 13, 0xFC); + ipu_dc_map_config(4, 2, 21, 0xFC); +} + +static int ipu_pixfmt_to_map(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_RGB24: + return 0; + case IPU_PIX_FMT_RGB666: + return 1; + case IPU_PIX_FMT_YUV444: + return 2; + case IPU_PIX_FMT_RGB565: + return 3; + case IPU_PIX_FMT_LVDS666: + return 4; + } + + return -1; +} + +/* + * This function is called to initialize a synchronous LCD panel. + * + * @param disp The DI the panel is attached to. + * + * @param pixel_clk Desired pixel clock frequency in Hz. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width The width of panel in pixels. + * + * @param height The height of panel in pixels. + * + * @param hStartWidth The number of pixel clocks between the HSYNC + * signal pulse and the start of valid data. + * + * @param hSyncWidth The width of the HSYNC signal in units of pixel + * clocks. + * + * @param hEndWidth The number of pixel clocks between the end of + * valid data and the HSYNC signal for next line. + * + * @param vStartWidth The number of lines between the VSYNC + * signal pulse and the start of valid data. + * + * @param vSyncWidth The width of the VSYNC signal in units of lines + * + * @param vEndWidth The number of lines between the end of valid + * data and the VSYNC signal for next frame. + * + * @param sig Bitfield of signal polarities for LCD interface. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ + +int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig) +{ + uint32_t reg; + uint32_t di_gen, vsync_cnt; + uint32_t div, rounded_pixel_clk; + uint32_t h_total, v_total; + int map; + struct clk *di_parent; + + debug("panel size = %d x %d\n", width, height); + + if ((v_sync_width == 0) || (h_sync_width == 0)) + return -EINVAL; + + /* adapt panel to ipu restricitions */ + if (v_end_width < 2) { + v_end_width = 2; + puts("WARNING: v_end_width (lower_margin) must be >= 2, adjusted\n"); + } + + h_total = width + h_sync_width + h_start_width + h_end_width; + v_total = height + v_sync_width + v_start_width + v_end_width; + + /* Init clocking */ + debug("pixel clk = %dHz\n", pixel_clk); + + if (sig.ext_clk) { + if (!(g_di1_tvout && (disp == 1))) { /*not round div for tvout*/ + /* + * Set the PLL to be an even multiple + * of the pixel clock. + */ + if ((clk_get_usecount(g_pixel_clk[0]) == 0) && + (clk_get_usecount(g_pixel_clk[1]) == 0)) { + di_parent = clk_get_parent(g_di_clk[disp]); + rounded_pixel_clk = + clk_round_rate(g_pixel_clk[disp], + pixel_clk); + div = clk_get_rate(di_parent) / + rounded_pixel_clk; + if (div % 2) + div++; + if (clk_get_rate(di_parent) != div * + rounded_pixel_clk) + clk_set_rate(di_parent, + div * rounded_pixel_clk); + udelay(10000); + clk_set_rate(g_di_clk[disp], + 2 * rounded_pixel_clk); + udelay(10000); + } + } + clk_set_parent(g_pixel_clk[disp], g_ldb_clk); + } else { + if (clk_get_usecount(g_pixel_clk[disp]) != 0) + clk_set_parent(g_pixel_clk[disp], g_ipu_clk); + } + rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk); + clk_set_rate(g_pixel_clk[disp], rounded_pixel_clk); + udelay(5000); + /* Get integer portion of divider */ + div = clk_get_rate(clk_get_parent(g_pixel_clk[disp])) / + rounded_pixel_clk; + + ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1); + ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); + + map = ipu_pixfmt_to_map(pixel_fmt); + if (map < 0) { + debug("IPU_DISP: No MAP\n"); + return -EINVAL; + } + + di_gen = __raw_readl(DI_GENERAL(disp)); + + if (sig.interlaced) { + /* Setup internal HSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 1, /* counter */ + h_total / 2 - 1,/* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 1 VSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 2, /* counter */ + h_total - 1, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* Setup internal HSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 3, /* counter */ + v_total * 2 - 1,/* run count */ + DI_SYNC_INT_HSYNC, /* run_resolution */ + 1, /* offset */ + DI_SYNC_INT_HSYNC, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* Active Field ? */ + ipu_di_sync_config( + disp, /* display */ + 4, /* counter */ + v_total / 2 - 1,/* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + v_start_width, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Active Line */ + ipu_di_sync_config( + disp, /* display */ + 5, /* counter */ + 0, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + height / 2, /* repeat count */ + 4, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 0 VSYNC waveform */ + ipu_di_sync_config( + disp, /* display */ + 6, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* DC VSYNC waveform */ + vsync_cnt = 7; + ipu_di_sync_config( + disp, /* display */ + 7, /* counter */ + v_total / 2 - 1,/* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 9, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* active pixel waveform */ + ipu_di_sync_config( + disp, /* display */ + 8, /* counter */ + 0, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + h_start_width, /* offset */ + DI_SYNC_CLK, /* offset resolution */ + width, /* repeat count */ + 5, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + ipu_di_sync_config( + disp, /* display */ + 9, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_INT_HSYNC,/* run_resolution */ + v_total / 2, /* offset */ + DI_SYNC_INT_HSYNC,/* offset resolution */ + 0, /* repeat count */ + DI_SYNC_HSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 4 /* COUNT DOWN */ + ); + + /* set gentime select and tag sel */ + reg = __raw_readl(DI_SW_GEN1(disp, 9)); + reg &= 0x1FFFFFFF; + reg |= (3 - 1)<<29 | 0x00008000; + __raw_writel(reg, DI_SW_GEN1(disp, 9)); + + __raw_writel(v_total / 2 - 1, DI_SCR_CONF(disp)); + + /* set y_sel = 1 */ + di_gen |= 0x10000000; + di_gen |= DI_GEN_POLARITY_5; + di_gen |= DI_GEN_POLARITY_8; + } else { + /* Setup internal HSYNC waveform */ + ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, + 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + /* Setup external (delayed) HSYNC waveform */ + ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1, + DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_CLK, 0, h_sync_width * 2); + /* Setup VSYNC waveform */ + vsync_cnt = DI_SYNC_VSYNC; + ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1, + DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0, + DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_INT_HSYNC, 0, v_sync_width * 2); + __raw_writel(v_total - 1, DI_SCR_CONF(disp)); + + /* Setup active data waveform to sync with DC */ + ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC, + v_sync_width + v_start_width, DI_SYNC_HSYNC, + height, + DI_SYNC_VSYNC, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK, + h_sync_width + h_start_width, DI_SYNC_CLK, + width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, + 0); + + /* reset all unused counters */ + __raw_writel(0, DI_SW_GEN0(disp, 6)); + __raw_writel(0, DI_SW_GEN1(disp, 6)); + __raw_writel(0, DI_SW_GEN0(disp, 7)); + __raw_writel(0, DI_SW_GEN1(disp, 7)); + __raw_writel(0, DI_SW_GEN0(disp, 8)); + __raw_writel(0, DI_SW_GEN1(disp, 8)); + __raw_writel(0, DI_SW_GEN0(disp, 9)); + __raw_writel(0, DI_SW_GEN1(disp, 9)); + + reg = __raw_readl(DI_STP_REP(disp, 6)); + reg &= 0x0000FFFF; + __raw_writel(reg, DI_STP_REP(disp, 6)); + __raw_writel(0, DI_STP_REP(disp, 7)); + __raw_writel(0, DI_STP_REP9(disp)); + + /* Init template microcode */ + if (disp) { + ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5); + ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5); + ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5); + } else { + ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5); + ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5); + ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5); + } + + if (sig.Hsync_pol) + di_gen |= DI_GEN_POLARITY_2; + if (sig.Vsync_pol) + di_gen |= DI_GEN_POLARITY_3; + + if (!sig.clk_pol) + di_gen |= DI_GEN_POL_CLK; + + } + + __raw_writel(di_gen, DI_GENERAL(disp)); + + __raw_writel((--vsync_cnt << DI_VSYNC_SEL_OFFSET) | + 0x00000002, DI_SYNC_AS_GEN(disp)); + + reg = __raw_readl(DI_POL(disp)); + reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); + if (sig.enable_pol) + reg |= DI_POL_DRDY_POLARITY_15; + if (sig.data_pol) + reg |= DI_POL_DRDY_DATA_POLARITY; + __raw_writel(reg, DI_POL(disp)); + + __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); + + return 0; +} + +/* + * This function sets the foreground and background plane global alpha blending + * modes. This function also sets the DP graphic plane according to the + * parameter of IPUv3 DP channel. + * + * @param channel IPUv3 DP channel + * + * @param enable Boolean to enable or disable global alpha + * blending. If disabled, local blending is used. + * + * @param alpha Global alpha value. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, + uint8_t alpha) +{ + uint32_t reg; + + unsigned char bg_chan; + + if (!((channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) || + (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) || + (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1))) + return -EINVAL; + + if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 || + channel == MEM_BG_ASYNC1) + bg_chan = 1; + else + bg_chan = 0; + + if (!g_ipu_clk_enabled) + clk_enable(g_ipu_clk); + + if (bg_chan) { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF()); + } else { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_GWSEL, DP_COM_CONF()); + } + + if (enable) { + reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0x00FFFFFFL; + __raw_writel(reg | ((uint32_t) alpha << 24), + DP_GRAPH_WIND_CTRL()); + + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF()); + } else { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF()); + } + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + if (!g_ipu_clk_enabled) + clk_disable(g_ipu_clk); + + return 0; +} + +/* + * This function sets the transparent color key for SDC graphic plane. + * + * @param channel Input parameter for the logical channel ID. + * + * @param enable Boolean to enable or disable color key + * + * @param colorKey 24-bit RGB color for transparent color key. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, + uint32_t color_key) +{ + uint32_t reg; + int y, u, v; + int red, green, blue; + + if (!((channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) || + (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) || + (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1))) + return -EINVAL; + + if (!g_ipu_clk_enabled) + clk_enable(g_ipu_clk); + + color_key_4rgb = 1; + /* Transform color key from rgb to yuv if CSC is enabled */ + if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || + ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || + ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB))) { + + debug("color key 0x%x need change to yuv fmt\n", color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = rgb_to_yuv(0, red, green, blue); + u = rgb_to_yuv(1, red, green, blue); + v = rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + color_key_4rgb = 0; + + debug("color key change to yuv fmt 0x%x\n", color_key); + } + + if (enable) { + reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0xFF000000L; + __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL()); + + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF()); + } else { + reg = __raw_readl(DP_COM_CONF()); + __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF()); + } + + reg = __raw_readl(IPU_SRM_PRI2) | 0x8; + __raw_writel(reg, IPU_SRM_PRI2); + + if (!g_ipu_clk_enabled) + clk_disable(g_ipu_clk); + + return 0; +} diff --git a/drivers/video/imx/ipu_regs.h b/drivers/video/imx/ipu_regs.h new file mode 100644 index 0000000000..deb44002d7 --- /dev/null +++ b/drivers/video/imx/ipu_regs.h @@ -0,0 +1,415 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Porting to u-boot: + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * Linux IPU driver for MX51: + * + * (C) Copyright 2005-2009 Freescale Semiconductor, Inc. + */ + +#ifndef __IPU_REGS_INCLUDED__ +#define __IPU_REGS_INCLUDED__ + +#define IPU_DISP0_BASE 0x00000000 +#define IPU_MCU_T_DEFAULT 8 +#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25) +#define IPU_CM_REG_BASE 0x00000000 +#define IPU_STAT_REG_BASE 0x00000200 +#define IPU_IDMAC_REG_BASE 0x00008000 +#define IPU_ISP_REG_BASE 0x00010000 +#define IPU_DP_REG_BASE 0x00018000 +#define IPU_IC_REG_BASE 0x00020000 +#define IPU_IRT_REG_BASE 0x00028000 +#define IPU_CSI0_REG_BASE 0x00030000 +#define IPU_CSI1_REG_BASE 0x00038000 +#define IPU_DI0_REG_BASE 0x00040000 +#define IPU_DI1_REG_BASE 0x00048000 +#define IPU_SMFC_REG_BASE 0x00050000 +#define IPU_DC_REG_BASE 0x00058000 +#define IPU_DMFC_REG_BASE 0x00060000 +#define IPU_VDI_REG_BASE 0x00680000 +#if defined(CONFIG_MX51) || defined(CONFIG_MX53) +#define IPU_CPMEM_REG_BASE 0x01000000 +#define IPU_LUT_REG_BASE 0x01020000 +#define IPU_SRM_REG_BASE 0x01040000 +#define IPU_TPM_REG_BASE 0x01060000 +#define IPU_DC_TMPL_REG_BASE 0x01080000 +#define IPU_ISP_TBPR_REG_BASE 0x010C0000 +#elif defined(CONFIG_MX6) +#define IPU_CPMEM_REG_BASE 0x00100000 +#define IPU_LUT_REG_BASE 0x00120000 +#define IPU_SRM_REG_BASE 0x00140000 +#define IPU_TPM_REG_BASE 0x00160000 +#define IPU_DC_TMPL_REG_BASE 0x00180000 +#define IPU_ISP_TBPR_REG_BASE 0x001C0000 +#endif + +#define IPU_CTRL_BASE_ADDR (IPU_SOC_BASE_ADDR + IPU_SOC_OFFSET) + +extern u32 *ipu_dc_tmpl_reg; + +#define DC_EVT_NF 0 +#define DC_EVT_NL 1 +#define DC_EVT_EOF 2 +#define DC_EVT_NFIELD 3 +#define DC_EVT_EOL 4 +#define DC_EVT_EOFIELD 5 +#define DC_EVT_NEW_ADDR 6 +#define DC_EVT_NEW_CHAN 7 +#define DC_EVT_NEW_DATA 8 + +#define DC_EVT_NEW_ADDR_W_0 0 +#define DC_EVT_NEW_ADDR_W_1 1 +#define DC_EVT_NEW_CHAN_W_0 2 +#define DC_EVT_NEW_CHAN_W_1 3 +#define DC_EVT_NEW_DATA_W_0 4 +#define DC_EVT_NEW_DATA_W_1 5 +#define DC_EVT_NEW_ADDR_R_0 6 +#define DC_EVT_NEW_ADDR_R_1 7 +#define DC_EVT_NEW_CHAN_R_0 8 +#define DC_EVT_NEW_CHAN_R_1 9 +#define DC_EVT_NEW_DATA_R_0 10 +#define DC_EVT_NEW_DATA_R_1 11 + +/* Software reset for ipu */ +#define SW_IPU_RST 8 + +enum { + IPU_CONF_DP_EN = 0x00000020, + IPU_CONF_DI0_EN = 0x00000040, + IPU_CONF_DI1_EN = 0x00000080, + IPU_CONF_DMFC_EN = 0x00000400, + IPU_CONF_DC_EN = 0x00000200, + + DI0_COUNTER_RELEASE = 0x01000000, + DI1_COUNTER_RELEASE = 0x02000000, + + DI_DW_GEN_ACCESS_SIZE_OFFSET = 24, + DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16, + + DI_GEN_DI_CLK_EXT = 0x100000, + DI_GEN_POLARITY_1 = 0x00000001, + DI_GEN_POLARITY_2 = 0x00000002, + DI_GEN_POLARITY_3 = 0x00000004, + DI_GEN_POLARITY_4 = 0x00000008, + DI_GEN_POLARITY_5 = 0x00000010, + DI_GEN_POLARITY_6 = 0x00000020, + DI_GEN_POLARITY_7 = 0x00000040, + DI_GEN_POLARITY_8 = 0x00000080, + DI_GEN_POL_CLK = 0x20000, + + DI_POL_DRDY_DATA_POLARITY = 0x00000080, + DI_POL_DRDY_POLARITY_15 = 0x00000010, + DI_VSYNC_SEL_OFFSET = 13, + + DC_WR_CH_CONF_FIELD_MODE = 0x00000200, + DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5, + DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0, + DC_WR_CH_CONF_PROG_DI_ID = 0x00000004, + DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3, + DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018, + + DP_COM_CONF_FG_EN = 0x00000001, + DP_COM_CONF_GWSEL = 0x00000002, + DP_COM_CONF_GWAM = 0x00000004, + DP_COM_CONF_GWCKE = 0x00000008, + DP_COM_CONF_CSC_DEF_MASK = 0x00000300, + DP_COM_CONF_CSC_DEF_OFFSET = 8, + DP_COM_CONF_CSC_DEF_FG = 0x00000300, + DP_COM_CONF_CSC_DEF_BG = 0x00000200, + DP_COM_CONF_CSC_DEF_BOTH = 0x00000100, + DP_COM_CONF_GAMMA_EN = 0x00001000, + DP_COM_CONF_GAMMA_YUV_EN = 0x00002000, +}; + +enum di_pins { + DI_PIN11 = 0, + DI_PIN12 = 1, + DI_PIN13 = 2, + DI_PIN14 = 3, + DI_PIN15 = 4, + DI_PIN16 = 5, + DI_PIN17 = 6, + DI_PIN_CS = 7, + + DI_PIN_SER_CLK = 0, + DI_PIN_SER_RS = 1, +}; + +enum di_sync_wave { + DI_SYNC_NONE = -1, + DI_SYNC_CLK = 0, + DI_SYNC_INT_HSYNC = 1, + DI_SYNC_HSYNC = 2, + DI_SYNC_VSYNC = 3, + DI_SYNC_DE = 5, +}; + +struct ipu_cm { + u32 conf; + u32 sisg_ctrl0; + u32 sisg_ctrl1; + u32 sisg_set[6]; + u32 sisg_clear[6]; + u32 int_ctrl[15]; + u32 sdma_event[10]; + u32 srm_pri1; + u32 srm_pri2; + u32 fs_proc_flow[3]; + u32 fs_disp_flow[2]; + u32 skip; + u32 disp_alt_conf; + u32 disp_gen; + u32 disp_alt[4]; + u32 snoop; + u32 mem_rst; + u32 pm; + u32 gpr; + u32 reserved0[26]; + u32 ch_db_mode_sel[2]; + u32 reserved1[4]; + u32 alt_ch_db_mode_sel[2]; + u32 reserved2[2]; + u32 ch_trb_mode_sel[2]; +}; + +struct ipu_idmac { + u32 conf; + u32 ch_en[2]; + u32 sep_alpha; + u32 alt_sep_alpha; + u32 ch_pri[2]; + u32 wm_en[2]; + u32 lock_en[2]; + u32 sub_addr[5]; + u32 bndm_en[2]; + u32 sc_cord[2]; + u32 reserved[44]; + u32 ch_busy[2]; +}; + +struct ipu_com_async { + u32 com_conf_async; + u32 graph_wind_ctrl_async; + u32 fg_pos_async; + u32 cur_pos_async; + u32 cur_map_async; + u32 gamma_c_async[8]; + u32 gamma_s_async[4]; + u32 dp_csca_async[4]; + u32 dp_csc_async[2]; +}; + +struct ipu_dp { + u32 com_conf_sync; + u32 graph_wind_ctrl_sync; + u32 fg_pos_sync; + u32 cur_pos_sync; + u32 cur_map_sync; + u32 gamma_c_sync[8]; + u32 gamma_s_sync[4]; + u32 csca_sync[4]; + u32 csc_sync[2]; + u32 cur_pos_alt; + struct ipu_com_async async[2]; +}; + +struct ipu_di { + u32 general; + u32 bs_clkgen0; + u32 bs_clkgen1; + u32 sw_gen0[9]; + u32 sw_gen1[9]; + u32 sync_as; + u32 dw_gen[12]; + u32 dw_set[48]; + u32 stp_rep[4]; + u32 stp_rep9; + u32 ser_conf; + u32 ssc; + u32 pol; + u32 aw0; + u32 aw1; + u32 scr_conf; + u32 stat; +}; + +struct ipu_stat { + u32 int_stat[15]; + u32 cur_buf[2]; + u32 alt_cur_buf_0; + u32 alt_cur_buf_1; + u32 srm_stat; + u32 proc_task_stat; + u32 disp_task_stat; + u32 triple_cur_buf[4]; + u32 ch_buf0_rdy[2]; + u32 ch_buf1_rdy[2]; + u32 alt_ch_buf0_rdy[2]; + u32 alt_ch_buf1_rdy[2]; + u32 ch_buf2_rdy[2]; +}; + +struct ipu_dc_ch { + u32 wr_ch_conf; + u32 wr_ch_addr; + u32 rl[5]; +}; + +struct ipu_dc { + struct ipu_dc_ch dc_ch0_1_2[3]; + u32 cmd_ch_conf_3; + u32 cmd_ch_conf_4; + struct ipu_dc_ch dc_ch5_6[2]; + struct ipu_dc_ch dc_ch8; + u32 rl6_ch_8; + struct ipu_dc_ch dc_ch9; + u32 rl6_ch_9; + u32 gen; + u32 disp_conf1[4]; + u32 disp_conf2[4]; + u32 di0_conf[2]; + u32 di1_conf[2]; + u32 dc_map_ptr[15]; + u32 dc_map_val[12]; + u32 udge[16]; + u32 lla[2]; + u32 r_lla[2]; + u32 wr_ch_addr_5_alt; + u32 stat; +}; + +struct ipu_dmfc { + u32 rd_chan; + u32 wr_chan; + u32 wr_chan_def; + u32 dp_chan; + u32 dp_chan_def; + u32 general[2]; + u32 ic_ctrl; + u32 wr_chan_alt; + u32 wr_chan_def_alt; + u32 general1_alt; + u32 stat; +}; + +#define IPU_CM_REG ((struct ipu_cm *)(IPU_CTRL_BASE_ADDR + \ + IPU_CM_REG_BASE)) +#define IPU_CONF (&IPU_CM_REG->conf) +#define IPU_SRM_PRI1 (&IPU_CM_REG->srm_pri1) +#define IPU_SRM_PRI2 (&IPU_CM_REG->srm_pri2) +#define IPU_FS_PROC_FLOW1 (&IPU_CM_REG->fs_proc_flow[0]) +#define IPU_FS_PROC_FLOW2 (&IPU_CM_REG->fs_proc_flow[1]) +#define IPU_FS_PROC_FLOW3 (&IPU_CM_REG->fs_proc_flow[2]) +#define IPU_FS_DISP_FLOW1 (&IPU_CM_REG->fs_disp_flow[0]) +#define IPU_DISP_GEN (&IPU_CM_REG->disp_gen) +#define IPU_MEM_RST (&IPU_CM_REG->mem_rst) +#define IPU_GPR (&IPU_CM_REG->gpr) +#define IPU_CHA_DB_MODE_SEL(ch) (&IPU_CM_REG->ch_db_mode_sel[ch / 32]) + +#define IPU_STAT ((struct ipu_stat *)(IPU_CTRL_BASE_ADDR + \ + IPU_STAT_REG_BASE)) +#define IPU_INT_STAT(n) (&IPU_STAT->int_stat[(n) - 1]) +#define IPU_CHA_CUR_BUF(ch) (&IPU_STAT->cur_buf[ch / 32]) +#define IPU_CHA_BUF0_RDY(ch) (&IPU_STAT->ch_buf0_rdy[ch / 32]) +#define IPU_CHA_BUF1_RDY(ch) (&IPU_STAT->ch_buf1_rdy[ch / 32]) +#define IPUIRQ_2_STATREG(irq) (IPU_INT_STAT(1) + ((irq) / 32)) +#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F)) + +#define IPU_INT_CTRL(n) (&IPU_CM_REG->int_ctrl[(n) - 1]) + +#define IDMAC_REG ((struct ipu_idmac *)(IPU_CTRL_BASE_ADDR + \ + IPU_IDMAC_REG_BASE)) +#define IDMAC_CONF (&IDMAC_REG->conf) +#define IDMAC_CHA_EN(ch) (&IDMAC_REG->ch_en[ch / 32]) +#define IDMAC_CHA_PRI(ch) (&IDMAC_REG->ch_pri[ch / 32]) + +#define DI_REG(di) ((struct ipu_di *)(IPU_CTRL_BASE_ADDR + \ + ((di == 1) ? IPU_DI1_REG_BASE : \ + IPU_DI0_REG_BASE))) +#define DI_GENERAL(di) (&DI_REG(di)->general) +#define DI_BS_CLKGEN0(di) (&DI_REG(di)->bs_clkgen0) +#define DI_BS_CLKGEN1(di) (&DI_REG(di)->bs_clkgen1) + +#define DI_SW_GEN0(di, gen) (&DI_REG(di)->sw_gen0[gen - 1]) +#define DI_SW_GEN1(di, gen) (&DI_REG(di)->sw_gen1[gen - 1]) +#define DI_STP_REP(di, gen) (&DI_REG(di)->stp_rep[(gen - 1) / 2]) +#define DI_STP_REP9(di) (&DI_REG(di)->stp_rep9) +#define DI_SYNC_AS_GEN(di) (&DI_REG(di)->sync_as) +#define DI_DW_GEN(di, gen) (&DI_REG(di)->dw_gen[gen]) +#define DI_DW_SET(di, gen, set) (&DI_REG(di)->dw_set[gen + 12 * set]) +#define DI_POL(di) (&DI_REG(di)->pol) +#define DI_SCR_CONF(di) (&DI_REG(di)->scr_conf) + +#define DMFC_REG ((struct ipu_dmfc *)(IPU_CTRL_BASE_ADDR + \ + IPU_DMFC_REG_BASE)) +#define DMFC_WR_CHAN (&DMFC_REG->wr_chan) +#define DMFC_WR_CHAN_DEF (&DMFC_REG->wr_chan_def) +#define DMFC_DP_CHAN (&DMFC_REG->dp_chan) +#define DMFC_DP_CHAN_DEF (&DMFC_REG->dp_chan_def) +#define DMFC_GENERAL1 (&DMFC_REG->general[0]) +#define DMFC_IC_CTRL (&DMFC_REG->ic_ctrl) + + +#define DC_REG ((struct ipu_dc *)(IPU_CTRL_BASE_ADDR + \ + IPU_DC_REG_BASE)) +#define DC_MAP_CONF_PTR(n) (&DC_REG->dc_map_ptr[n / 2]) +#define DC_MAP_CONF_VAL(n) (&DC_REG->dc_map_val[n / 2]) + + +static inline struct ipu_dc_ch *dc_ch_offset(int ch) +{ + switch (ch) { + case 0: + case 1: + case 2: + return &DC_REG->dc_ch0_1_2[ch]; + case 5: + case 6: + return &DC_REG->dc_ch5_6[ch - 5]; + case 8: + return &DC_REG->dc_ch8; + case 9: + return &DC_REG->dc_ch9; + default: + printf("%s: invalid channel %d\n", __func__, ch); + return NULL; + } + +} + +#define DC_RL_CH(ch, evt) (&dc_ch_offset(ch)->rl[evt / 2]) + +#define DC_WR_CH_CONF(ch) (&dc_ch_offset(ch)->wr_ch_conf) +#define DC_WR_CH_ADDR(ch) (&dc_ch_offset(ch)->wr_ch_addr) + +#define DC_WR_CH_CONF_1 DC_WR_CH_CONF(1) +#define DC_WR_CH_CONF_5 DC_WR_CH_CONF(5) + +#define DC_GEN (&DC_REG->gen) +#define DC_DISP_CONF2(disp) (&DC_REG->disp_conf2[disp]) +#define DC_STAT (&DC_REG->stat) + +#define DP_SYNC 0 +#define DP_ASYNC0 0x60 +#define DP_ASYNC1 0xBC + +#define DP_REG ((struct ipu_dp *)(IPU_CTRL_BASE_ADDR + \ + IPU_DP_REG_BASE)) +#define DP_COM_CONF() (&DP_REG->com_conf_sync) +#define DP_GRAPH_WIND_CTRL() (&DP_REG->graph_wind_ctrl_sync) +#define DP_CSC_A_0() (&DP_REG->csca_sync[0]) +#define DP_CSC_A_1() (&DP_REG->csca_sync[1]) +#define DP_CSC_A_2() (&DP_REG->csca_sync[2]) +#define DP_CSC_A_3() (&DP_REG->csca_sync[3]) + +#define DP_CSC_0() (&DP_REG->csc_sync[0]) +#define DP_CSC_1() (&DP_REG->csc_sync[1]) + +/* DC template opcodes */ +#define WROD(lf) (0x18 | (lf << 1)) + +#endif diff --git a/drivers/video/imx/mxc_ipuv3_fb.c b/drivers/video/imx/mxc_ipuv3_fb.c new file mode 100644 index 0000000000..3e38d4bdcc --- /dev/null +++ b/drivers/video/imx/mxc_ipuv3_fb.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../videomodes.h" +#include "ipu.h" +#include "mxcfb.h" +#include "ipu_regs.h" + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static int mxcfb_map_video_memory(struct fb_info *fbi); +static int mxcfb_unmap_video_memory(struct fb_info *fbi); + +/* graphics setup */ +static GraphicDevice panel; +static struct fb_videomode const *gmode; +static uint8_t gdisp; +static uint32_t gpixfmt; + +static 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 = %lu Hz\n", 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->fix.smem_len = roundup(fbi->fix.smem_len, ARCH_DMA_MINALIGN); + +#if CONFIG_IS_ENABLED(DM_VIDEO) + fbi->screen_base = (char *)gd->video_bottom; +#else + fbi->screen_base = (char *)memalign(ARCH_DMA_MINALIGN, + fbi->fix.smem_len); +#endif + + fbi->fix.smem_start = (unsigned long)fbi->screen_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; + +#if CONFIG_IS_ENABLED(VIDEO) + gd->fb_base = fbi->fix.smem_start; +#endif + + /* 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, uint8_t disp, + struct fb_videomode const *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 = disp; + + 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 */ + + mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; + fb_videomode_to_var(&fbi->var, mode); + fbi->var.bits_per_pixel = 16; + fbi->fix.line_length = fbi->var.xres * (fbi->var.bits_per_pixel / 8); + fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length; + + 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); + + /* allocate fb first */ + if (mxcfb_map_video_memory(fbi) < 0) + return -ENOMEM; + + mxcfb_set_par(fbi); + + panel.winSizeX = mode->xres; + panel.winSizeY = mode->yres; + panel.plnSizeX = mode->xres; + panel.plnSizeY = mode->yres; + + panel.frameAdrs = (u32)fbi->screen_base; + panel.memSize = fbi->screen_size; + + panel.gdfBytesPP = 2; + panel.gdfIndex = GDF_16BIT_565RGB; + + ipu_dump_registers(); + + return 0; + +err0: + return ret; +} + +void ipuv3_fb_shutdown(void) +{ + int i; + struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT; + + if (!ipu_clk_enabled()) + return; + + for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) { + struct fb_info *fbi = mxcfb_info[i]; + if (fbi) { + struct mxcfb_info *mxc_fbi = fbi->par; + ipu_disable_channel(mxc_fbi->ipu_ch); + ipu_uninit_channel(mxc_fbi->ipu_ch); + } + } + for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) { + __raw_writel(__raw_readl(&stat->int_stat[i]), + &stat->int_stat[i]); + } +} + +void *video_hw_init(void) +{ + int ret; + + ret = ipu_probe(); + if (ret) + puts("Error initializing IPU\n"); + + ret = mxcfb_probe(gpixfmt, gdisp, gmode); + debug("Framebuffer at 0x%x\n", (unsigned int)panel.frameAdrs); + + return (void *)&panel; +} + +int ipuv3_fb_init(struct fb_videomode const *mode, + uint8_t disp, + uint32_t pixfmt) +{ + gmode = mode; + gdisp = disp; + gpixfmt = pixfmt; + + return 0; +} + +#if CONFIG_IS_ENABLED(DM_VIDEO) +enum { + /* Maximum display size we support */ + LCD_MAX_WIDTH = 1920, + LCD_MAX_HEIGHT = 1080, + LCD_MAX_LOG2_BPP = VIDEO_BPP16, +}; + +static int ipuv3_video_probe(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + u32 fb_start, fb_end; + int ret; + + debug("%s() plat: base 0x%lx, size 0x%x\n", + __func__, plat->base, plat->size); + + ret = ipu_probe(); + if (ret) + return ret; + + ret = ipu_displays_init(); + if (ret < 0) + return ret; + + ret = mxcfb_probe(gpixfmt, gdisp, gmode); + if (ret < 0) + return ret; + + uc_priv->xsize = gmode->xres; + uc_priv->ysize = gmode->yres; + uc_priv->bpix = LCD_MAX_LOG2_BPP; + + /* Enable dcache for the frame buffer */ + fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); + fb_end = plat->base + plat->size; + fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT); + mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, + DCACHE_WRITEBACK); + video_set_flush_dcache(dev, true); + + return 0; +} + +struct ipuv3_video_priv { + ulong regs; +}; + +static int ipuv3_video_bind(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + + plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * + (1 << LCD_MAX_LOG2_BPP) / 8; + + return 0; +} + +static const struct udevice_id ipuv3_video_ids[] = { + { .compatible = "fsl,imx6q-ipu" }, + { } +}; + +U_BOOT_DRIVER(ipuv3_video) = { + .name = "ipuv3_video", + .id = UCLASS_VIDEO, + .of_match = ipuv3_video_ids, + .bind = ipuv3_video_bind, + .probe = ipuv3_video_probe, + .priv_auto_alloc_size = sizeof(struct ipuv3_video_priv), + .flags = DM_FLAG_PRE_RELOC, +}; +#endif /* CONFIG_DM_VIDEO */ diff --git a/drivers/video/imx/mxcfb.h b/drivers/video/imx/mxcfb.h new file mode 100644 index 0000000000..0dc3886193 --- /dev/null +++ b/drivers/video/imx/mxcfb.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 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. + */ + +#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 diff --git a/drivers/video/ipu.h b/drivers/video/ipu.h deleted file mode 100644 index 1e02c7ab6d..0000000000 --- a/drivers/video/ipu.h +++ /dev/null @@ -1,268 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Porting to u-boot: - * - * (C) Copyright 2010 - * Stefano Babic, DENX Software Engineering, sbabic@denx.de - * - * Linux IPU driver for MX51: - * - * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. - */ - -#ifndef __ASM_ARCH_IPU_H__ -#define __ASM_ARCH_IPU_H__ - -#include -#include - -#define IDMA_CHAN_INVALID 0xFF -#define HIGH_RESOLUTION_WIDTH 1024 - -struct clk { - const char *name; - int id; - /* Source clock this clk depends on */ - struct clk *parent; - /* Secondary clock to enable/disable with this clock */ - struct clk *secondary; - /* Current clock rate */ - unsigned long rate; - /* Reference count of clock enable/disable */ - __s8 usecount; - /* Register bit position for clock's enable/disable control. */ - u8 enable_shift; - /* Register address for clock's enable/disable control. */ - void *enable_reg; - u32 flags; - /* - * Function ptr to recalculate the clock's rate based on parent - * clock's rate - */ - void (*recalc) (struct clk *); - /* - * Function ptr to set the clock to a new rate. The rate must match a - * supported rate returned from round_rate. Leave blank if clock is not - * programmable - */ - int (*set_rate) (struct clk *, unsigned long); - /* - * Function ptr to round the requested clock rate to the nearest - * supported rate that is less than or equal to the requested rate. - */ - unsigned long (*round_rate) (struct clk *, unsigned long); - /* - * Function ptr to enable the clock. Leave blank if clock can not - * be gated. - */ - int (*enable) (struct clk *); - /* - * Function ptr to disable the clock. Leave blank if clock can not - * be gated. - */ - void (*disable) (struct clk *); - /* Function ptr to set the parent clock of the clock. */ - int (*set_parent) (struct clk *, struct clk *); -}; - -/* - * Enumeration of Synchronous (Memory-less) panel types - */ -typedef enum { - IPU_PANEL_SHARP_TFT, - IPU_PANEL_TFT, -} ipu_panel_t; - -/* - * IPU Driver channels definitions. - * Note these are different from IDMA channels - */ -#define IPU_MAX_CH 32 -#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ - ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out) -#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24)) -#define IPU_CHAN_ID(ch) (ch >> 24) -#define IPU_CHAN_ALT(ch) (ch & 0x02000000) -#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F) -#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F) -#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F) -#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F)) -#define NO_DMA 0x3F -#define ALT 1 - -/* - * Enumeration of IPU logical channels. An IPU logical channel is defined as a - * combination of an input (memory to IPU), output (IPU to memory), and/or - * secondary input IDMA channels and in some cases an Image Converter task. - * Some channels consist of only an input or output. - */ -typedef enum { - CHAN_NONE = -1, - - MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), - MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), - MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), - MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), - - MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), - MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), - MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), - MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), - - DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), - DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), - -} ipu_channel_t; - -/* - * Enumeration of types of buffers for a logical channel. - */ -typedef enum { - IPU_OUTPUT_BUFFER = 0, /*< Buffer for output from IPU */ - IPU_ALPHA_IN_BUFFER = 1, /*< Buffer for input to IPU */ - IPU_GRAPH_IN_BUFFER = 2, /*< Buffer for input to IPU */ - IPU_VIDEO_IN_BUFFER = 3, /*< Buffer for input to IPU */ - IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER, - IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER, -} ipu_buffer_t; - -#define IPU_PANEL_SERIAL 1 -#define IPU_PANEL_PARALLEL 2 - -struct ipu_channel { - u8 video_in_dma; - u8 alpha_in_dma; - u8 graph_in_dma; - u8 out_dma; -}; - -enum ipu_dmfc_type { - DMFC_NORMAL = 0, - DMFC_HIGH_RESOLUTION_DC, - DMFC_HIGH_RESOLUTION_DP, - DMFC_HIGH_RESOLUTION_ONLY_DP, -}; - - -/* - * Union of initialization parameters for a logical channel. - */ -typedef union { - struct { - uint32_t di; - unsigned char interlaced; - } mem_dc_sync; - struct { - uint32_t temp; - } mem_sdc_fg; - struct { - uint32_t di; - unsigned char interlaced; - uint32_t in_pixel_fmt; - uint32_t out_pixel_fmt; - unsigned char alpha_chan_en; - } mem_dp_bg_sync; - struct { - uint32_t temp; - } mem_sdc_bg; - struct { - uint32_t di; - unsigned char interlaced; - uint32_t in_pixel_fmt; - uint32_t out_pixel_fmt; - unsigned char alpha_chan_en; - } mem_dp_fg_sync; -} ipu_channel_params_t; - -/* - * Enumeration of IPU interrupts. - */ -enum ipu_irq_line { - IPU_IRQ_DP_SF_END = 448 + 3, - IPU_IRQ_DC_FC_1 = 448 + 9, -}; - -/* - * Bitfield of Display Interface signal polarities. - */ -typedef struct { - unsigned datamask_en:1; - unsigned ext_clk:1; - unsigned interlaced:1; - unsigned odd_field_first:1; - unsigned clksel_en:1; - unsigned clkidle_en:1; - unsigned data_pol:1; /* true = inverted */ - unsigned clk_pol:1; /* true = rising edge */ - unsigned enable_pol:1; - unsigned Hsync_pol:1; /* true = active high */ - unsigned Vsync_pol:1; -} ipu_di_signal_cfg_t; - -typedef enum { - RGB, - YCbCr, - YUV -} ipu_color_space_t; - -/* Common IPU API */ -int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params); -void ipu_uninit_channel(ipu_channel_t channel); - -int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, - uint32_t pixel_fmt, - uint16_t width, uint16_t height, - uint32_t stride, - dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, - uint32_t u_offset, uint32_t v_offset); - -int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, - uint32_t bufNum, dma_addr_t phyaddr); - -int32_t ipu_is_channel_busy(ipu_channel_t channel); -void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, - uint32_t bufNum); -int32_t ipu_enable_channel(ipu_channel_t channel); -int32_t ipu_disable_channel(ipu_channel_t channel); - -int32_t ipu_init_sync_panel(int disp, - uint32_t pixel_clk, - uint16_t width, uint16_t height, - uint32_t pixel_fmt, - uint16_t h_start_width, uint16_t h_sync_width, - uint16_t h_end_width, uint16_t v_start_width, - uint16_t v_sync_width, uint16_t v_end_width, - uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig); - -int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, - uint8_t alpha); -int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, - uint32_t colorKey); - -uint32_t bytes_per_pixel(uint32_t fmt); - -void clk_enable(struct clk *clk); -void clk_disable(struct clk *clk); -u32 clk_get_rate(struct clk *clk); -int clk_set_rate(struct clk *clk, unsigned long rate); -long clk_round_rate(struct clk *clk, unsigned long rate); -int clk_set_parent(struct clk *clk, struct clk *parent); -int clk_get_usecount(struct clk *clk); -struct clk *clk_get_parent(struct clk *clk); - -void ipu_dump_registers(void); -int ipu_probe(void); -bool ipu_clk_enabled(void); - -void ipu_dmfc_init(int dmfc_type, int first); -void ipu_init_dc_mappings(void); -void ipu_dmfc_set_wait4eot(int dma_chan, int width); -void ipu_dc_init(int dc_chan, int di, unsigned char interlaced); -void ipu_dc_uninit(int dc_chan); -void ipu_dp_dc_enable(ipu_channel_t channel); -int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, - uint32_t out_pixel_fmt); -void ipu_dp_uninit(ipu_channel_t channel); -void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap); -ipu_color_space_t format_to_colorspace(uint32_t fmt); -#endif diff --git a/drivers/video/ipu_common.c b/drivers/video/ipu_common.c deleted file mode 100644 index cbe1984e4f..0000000000 --- a/drivers/video/ipu_common.c +++ /dev/null @@ -1,1265 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Porting to u-boot: - * - * (C) Copyright 2010 - * Stefano Babic, DENX Software Engineering, sbabic@denx.de - * - * Linux IPU driver for MX51: - * - * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. - */ - -/* #define DEBUG */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ipu.h" -#include "ipu_regs.h" - -extern struct mxc_ccm_reg *mxc_ccm; -extern u32 *ipu_cpmem_base; - -struct ipu_ch_param_word { - uint32_t data[5]; - uint32_t res[3]; -}; - -struct ipu_ch_param { - struct ipu_ch_param_word word[2]; -}; - -#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch)) - -#define _param_word(base, w) \ - (((struct ipu_ch_param *)(base))->word[(w)].data) - -#define ipu_ch_param_set_field(base, w, bit, size, v) { \ - int i = (bit) / 32; \ - int off = (bit) % 32; \ - _param_word(base, w)[i] |= (v) << off; \ - if (((bit) + (size) - 1) / 32 > i) { \ - _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \ - } \ -} - -#define ipu_ch_param_mod_field(base, w, bit, size, v) { \ - int i = (bit) / 32; \ - int off = (bit) % 32; \ - u32 mask = (1UL << size) - 1; \ - u32 temp = _param_word(base, w)[i]; \ - temp &= ~(mask << off); \ - _param_word(base, w)[i] = temp | (v) << off; \ - if (((bit) + (size) - 1) / 32 > i) { \ - temp = _param_word(base, w)[i + 1]; \ - temp &= ~(mask >> (32 - off)); \ - _param_word(base, w)[i + 1] = \ - temp | ((v) >> (off ? (32 - off) : 0)); \ - } \ -} - -#define ipu_ch_param_read_field(base, w, bit, size) ({ \ - u32 temp2; \ - int i = (bit) / 32; \ - int off = (bit) % 32; \ - u32 mask = (1UL << size) - 1; \ - u32 temp1 = _param_word(base, w)[i]; \ - temp1 = mask & (temp1 >> off); \ - if (((bit)+(size) - 1) / 32 > i) { \ - temp2 = _param_word(base, w)[i + 1]; \ - temp2 &= mask >> (off ? (32 - off) : 0); \ - temp1 |= temp2 << (off ? (32 - off) : 0); \ - } \ - temp1; \ -}) - -#define IPU_SW_RST_TOUT_USEC (10000) - -#define IPUV3_CLK_MX51 133000000 -#define IPUV3_CLK_MX53 200000000 -#define IPUV3_CLK_MX6Q 264000000 -#define IPUV3_CLK_MX6DL 198000000 - -void clk_enable(struct clk *clk) -{ - if (clk) { - if (clk->usecount++ == 0) { - clk->enable(clk); - } - } -} - -void clk_disable(struct clk *clk) -{ - if (clk) { - if (!(--clk->usecount)) { - if (clk->disable) - clk->disable(clk); - } - } -} - -int clk_get_usecount(struct clk *clk) -{ - if (clk == NULL) - return 0; - - return clk->usecount; -} - -u32 clk_get_rate(struct clk *clk) -{ - if (!clk) - return 0; - - return clk->rate; -} - -struct clk *clk_get_parent(struct clk *clk) -{ - if (!clk) - return 0; - - return clk->parent; -} - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - if (!clk) - return 0; - - if (clk->set_rate) - clk->set_rate(clk, rate); - - return clk->rate; -} - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (clk == NULL || !clk->round_rate) - return 0; - - return clk->round_rate(clk, rate); -} - -int clk_set_parent(struct clk *clk, struct clk *parent) -{ - clk->parent = parent; - if (clk->set_parent) - return clk->set_parent(clk, parent); - return 0; -} - -static int clk_ipu_enable(struct clk *clk) -{ - u32 reg; - - reg = __raw_readl(clk->enable_reg); - reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift; - __raw_writel(reg, clk->enable_reg); - -#if defined(CONFIG_MX51) || defined(CONFIG_MX53) - /* Handshake with IPU when certain clock rates are changed. */ - reg = __raw_readl(&mxc_ccm->ccdr); - reg &= ~MXC_CCM_CCDR_IPU_HS_MASK; - __raw_writel(reg, &mxc_ccm->ccdr); - - /* Handshake with IPU when LPM is entered as its enabled. */ - reg = __raw_readl(&mxc_ccm->clpcr); - reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; - __raw_writel(reg, &mxc_ccm->clpcr); -#endif - return 0; -} - -static void clk_ipu_disable(struct clk *clk) -{ - u32 reg; - - reg = __raw_readl(clk->enable_reg); - reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); - __raw_writel(reg, clk->enable_reg); - -#if defined(CONFIG_MX51) || defined(CONFIG_MX53) - /* - * No handshake with IPU whe dividers are changed - * as its not enabled. - */ - reg = __raw_readl(&mxc_ccm->ccdr); - reg |= MXC_CCM_CCDR_IPU_HS_MASK; - __raw_writel(reg, &mxc_ccm->ccdr); - - /* No handshake with IPU when LPM is entered as its not enabled. */ - reg = __raw_readl(&mxc_ccm->clpcr); - reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS; - __raw_writel(reg, &mxc_ccm->clpcr); -#endif -} - - -static struct clk ipu_clk = { - .name = "ipu_clk", -#if defined(CONFIG_MX51) || defined(CONFIG_MX53) - .enable_reg = (u32 *)(CCM_BASE_ADDR + - offsetof(struct mxc_ccm_reg, CCGR5)), - .enable_shift = MXC_CCM_CCGR5_IPU_OFFSET, -#else - .enable_reg = (u32 *)(CCM_BASE_ADDR + - offsetof(struct mxc_ccm_reg, CCGR3)), - .enable_shift = MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET, -#endif - .enable = clk_ipu_enable, - .disable = clk_ipu_disable, - .usecount = 0, -}; - -#if !defined CONFIG_SYS_LDB_CLOCK -#define CONFIG_SYS_LDB_CLOCK 65000000 -#endif - -static struct clk ldb_clk = { - .name = "ldb_clk", - .rate = CONFIG_SYS_LDB_CLOCK, - .usecount = 0, -}; - -/* Globals */ -struct clk *g_ipu_clk; -struct clk *g_ldb_clk; -unsigned char g_ipu_clk_enabled; -struct clk *g_di_clk[2]; -struct clk *g_pixel_clk[2]; -unsigned char g_dc_di_assignment[10]; -uint32_t g_channel_init_mask; -uint32_t g_channel_enable_mask; - -static int ipu_dc_use_count; -static int ipu_dp_use_count; -static int ipu_dmfc_use_count; -static int ipu_di_use_count[2]; - -u32 *ipu_cpmem_base; -u32 *ipu_dc_tmpl_reg; - -/* Static functions */ - -static inline void ipu_ch_param_set_high_priority(uint32_t ch) -{ - ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 93, 2, 1); -}; - -static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) -{ - return ((uint32_t) ch >> (6 * type)) & 0x3F; -}; - -/* Either DP BG or DP FG can be graphic window */ -static inline int ipu_is_dp_graphic_chan(uint32_t dma_chan) -{ - return (dma_chan == 23 || dma_chan == 27); -} - -static inline int ipu_is_dmfc_chan(uint32_t dma_chan) -{ - return ((dma_chan >= 23) && (dma_chan <= 29)); -} - - -static inline void ipu_ch_param_set_buffer(uint32_t ch, int bufNum, - dma_addr_t phyaddr) -{ - ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 29 * bufNum, 29, - phyaddr / 8); -}; - -#define idma_is_valid(ch) (ch != NO_DMA) -#define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) -#define idma_is_set(reg, dma) (__raw_readl(reg(dma)) & idma_mask(dma)) - -static void ipu_pixel_clk_recalc(struct clk *clk) -{ - u32 div; - u64 final_rate = (unsigned long long)clk->parent->rate * 16; - - div = __raw_readl(DI_BS_CLKGEN0(clk->id)); - debug("read BS_CLKGEN0 div:%d, final_rate:%lld, prate:%ld\n", - div, final_rate, clk->parent->rate); - - clk->rate = 0; - if (div != 0) { - do_div(final_rate, div); - clk->rate = final_rate; - } -} - -static unsigned long ipu_pixel_clk_round_rate(struct clk *clk, - unsigned long rate) -{ - u64 div, final_rate; - u32 remainder; - u64 parent_rate = (unsigned long long)clk->parent->rate * 16; - - /* - * Calculate divider - * Fractional part is 4 bits, - * so simply multiply by 2^4 to get fractional part. - */ - div = parent_rate; - remainder = do_div(div, rate); - /* Round the divider value */ - if (remainder > (rate / 2)) - div++; - if (div < 0x10) /* Min DI disp clock divider is 1 */ - div = 0x10; - if (div & ~0xFEF) - div &= 0xFF8; - else { - /* Round up divider if it gets us closer to desired pix clk */ - if ((div & 0xC) == 0xC) { - div += 0x10; - div &= ~0xF; - } - } - final_rate = parent_rate; - do_div(final_rate, div); - - return final_rate; -} - -static int ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) -{ - u64 div, parent_rate; - u32 remainder; - - parent_rate = (unsigned long long)clk->parent->rate * 16; - div = parent_rate; - remainder = do_div(div, rate); - /* Round the divider value */ - if (remainder > (rate / 2)) - div++; - - /* Round up divider if it gets us closer to desired pix clk */ - if ((div & 0xC) == 0xC) { - div += 0x10; - div &= ~0xF; - } - if (div > 0x1000) - debug("Overflow, DI_BS_CLKGEN0 div:0x%x\n", (u32)div); - - __raw_writel(div, DI_BS_CLKGEN0(clk->id)); - - /* - * Setup pixel clock timing - * Down time is half of period - */ - __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id)); - - do_div(parent_rate, div); - - clk->rate = parent_rate; - - return 0; -} - -static int ipu_pixel_clk_enable(struct clk *clk) -{ - u32 disp_gen = __raw_readl(IPU_DISP_GEN); - disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE; - __raw_writel(disp_gen, IPU_DISP_GEN); - - return 0; -} - -static void ipu_pixel_clk_disable(struct clk *clk) -{ - u32 disp_gen = __raw_readl(IPU_DISP_GEN); - disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE; - __raw_writel(disp_gen, IPU_DISP_GEN); - -} - -static int ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) -{ - u32 di_gen = __raw_readl(DI_GENERAL(clk->id)); - - if (parent == g_ipu_clk) - di_gen &= ~DI_GEN_DI_CLK_EXT; - else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_ldb_clk) - di_gen |= DI_GEN_DI_CLK_EXT; - else - return -EINVAL; - - __raw_writel(di_gen, DI_GENERAL(clk->id)); - ipu_pixel_clk_recalc(clk); - return 0; -} - -static struct clk pixel_clk[] = { - { - .name = "pixel_clk", - .id = 0, - .recalc = ipu_pixel_clk_recalc, - .set_rate = ipu_pixel_clk_set_rate, - .round_rate = ipu_pixel_clk_round_rate, - .set_parent = ipu_pixel_clk_set_parent, - .enable = ipu_pixel_clk_enable, - .disable = ipu_pixel_clk_disable, - .usecount = 0, - }, - { - .name = "pixel_clk", - .id = 1, - .recalc = ipu_pixel_clk_recalc, - .set_rate = ipu_pixel_clk_set_rate, - .round_rate = ipu_pixel_clk_round_rate, - .set_parent = ipu_pixel_clk_set_parent, - .enable = ipu_pixel_clk_enable, - .disable = ipu_pixel_clk_disable, - .usecount = 0, - }, -}; - -/* - * This function resets IPU - */ -static void ipu_reset(void) -{ - u32 *reg; - u32 value; - int timeout = IPU_SW_RST_TOUT_USEC; - - reg = (u32 *)SRC_BASE_ADDR; - value = __raw_readl(reg); - value = value | SW_IPU_RST; - __raw_writel(value, reg); - - while (__raw_readl(reg) & SW_IPU_RST) { - udelay(1); - if (!(timeout--)) { - printf("ipu software reset timeout\n"); - break; - } - }; -} - -/* - * This function is called by the driver framework to initialize the IPU - * hardware. - * - * @param dev The device structure for the IPU passed in by the - * driver framework. - * - * @return Returns 0 on success or negative error code on error - */ -int ipu_probe(void) -{ - unsigned long ipu_base; -#if defined CONFIG_MX51 - u32 temp; - - u32 *reg_hsc_mcd = (u32 *)MIPI_HSC_BASE_ADDR; - u32 *reg_hsc_mxt_conf = (u32 *)(MIPI_HSC_BASE_ADDR + 0x800); - - __raw_writel(0xF00, reg_hsc_mcd); - - /* CSI mode reserved*/ - temp = __raw_readl(reg_hsc_mxt_conf); - __raw_writel(temp | 0x0FF, reg_hsc_mxt_conf); - - temp = __raw_readl(reg_hsc_mxt_conf); - __raw_writel(temp | 0x10000, reg_hsc_mxt_conf); -#endif - - ipu_base = IPU_CTRL_BASE_ADDR; - ipu_cpmem_base = (u32 *)(ipu_base + IPU_CPMEM_REG_BASE); - ipu_dc_tmpl_reg = (u32 *)(ipu_base + IPU_DC_TMPL_REG_BASE); - - g_pixel_clk[0] = &pixel_clk[0]; - g_pixel_clk[1] = &pixel_clk[1]; - - g_ipu_clk = &ipu_clk; -#if defined(CONFIG_MX51) - g_ipu_clk->rate = IPUV3_CLK_MX51; -#elif defined(CONFIG_MX53) - g_ipu_clk->rate = IPUV3_CLK_MX53; -#else - g_ipu_clk->rate = is_mx6sdl() ? IPUV3_CLK_MX6DL : IPUV3_CLK_MX6Q; -#endif - debug("ipu_clk = %u\n", clk_get_rate(g_ipu_clk)); - g_ldb_clk = &ldb_clk; - debug("ldb_clk = %u\n", clk_get_rate(g_ldb_clk)); - ipu_reset(); - - clk_set_parent(g_pixel_clk[0], g_ipu_clk); - clk_set_parent(g_pixel_clk[1], g_ipu_clk); - clk_enable(g_ipu_clk); - - g_di_clk[0] = NULL; - g_di_clk[1] = NULL; - - __raw_writel(0x807FFFFF, IPU_MEM_RST); - while (__raw_readl(IPU_MEM_RST) & 0x80000000) - ; - - ipu_init_dc_mappings(); - - __raw_writel(0, IPU_INT_CTRL(5)); - __raw_writel(0, IPU_INT_CTRL(6)); - __raw_writel(0, IPU_INT_CTRL(9)); - __raw_writel(0, IPU_INT_CTRL(10)); - - /* DMFC Init */ - ipu_dmfc_init(DMFC_NORMAL, 1); - - /* Set sync refresh channels as high priority */ - __raw_writel(0x18800000L, IDMAC_CHA_PRI(0)); - - /* Set MCU_T to divide MCU access window into 2 */ - __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); - - clk_disable(g_ipu_clk); - - return 0; -} - -void ipu_dump_registers(void) -{ - debug("IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF)); - debug("IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF)); - debug("IDMAC_CHA_EN1 = \t0x%08X\n", - __raw_readl(IDMAC_CHA_EN(0))); - debug("IDMAC_CHA_EN2 = \t0x%08X\n", - __raw_readl(IDMAC_CHA_EN(32))); - debug("IDMAC_CHA_PRI1 = \t0x%08X\n", - __raw_readl(IDMAC_CHA_PRI(0))); - debug("IDMAC_CHA_PRI2 = \t0x%08X\n", - __raw_readl(IDMAC_CHA_PRI(32))); - debug("IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", - __raw_readl(IPU_CHA_DB_MODE_SEL(0))); - debug("IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", - __raw_readl(IPU_CHA_DB_MODE_SEL(32))); - debug("DMFC_WR_CHAN = \t0x%08X\n", - __raw_readl(DMFC_WR_CHAN)); - debug("DMFC_WR_CHAN_DEF = \t0x%08X\n", - __raw_readl(DMFC_WR_CHAN_DEF)); - debug("DMFC_DP_CHAN = \t0x%08X\n", - __raw_readl(DMFC_DP_CHAN)); - debug("DMFC_DP_CHAN_DEF = \t0x%08X\n", - __raw_readl(DMFC_DP_CHAN_DEF)); - debug("DMFC_IC_CTRL = \t0x%08X\n", - __raw_readl(DMFC_IC_CTRL)); - debug("IPU_FS_PROC_FLOW1 = \t0x%08X\n", - __raw_readl(IPU_FS_PROC_FLOW1)); - debug("IPU_FS_PROC_FLOW2 = \t0x%08X\n", - __raw_readl(IPU_FS_PROC_FLOW2)); - debug("IPU_FS_PROC_FLOW3 = \t0x%08X\n", - __raw_readl(IPU_FS_PROC_FLOW3)); - debug("IPU_FS_DISP_FLOW1 = \t0x%08X\n", - __raw_readl(IPU_FS_DISP_FLOW1)); -} - -/* - * This function is called to initialize a logical IPU channel. - * - * @param channel Input parameter for the logical channel ID to init. - * - * @param params Input parameter containing union of channel - * initialization parameters. - * - * @return Returns 0 on success or negative error code on fail - */ -int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) -{ - int ret = 0; - uint32_t ipu_conf; - - debug("init channel = %d\n", IPU_CHAN_ID(channel)); - - if (g_ipu_clk_enabled == 0) { - g_ipu_clk_enabled = 1; - clk_enable(g_ipu_clk); - } - - - if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) { - printf("Warning: channel already initialized %d\n", - IPU_CHAN_ID(channel)); - } - - ipu_conf = __raw_readl(IPU_CONF); - - switch (channel) { - case MEM_DC_SYNC: - if (params->mem_dc_sync.di > 1) { - ret = -EINVAL; - goto err; - } - - g_dc_di_assignment[1] = params->mem_dc_sync.di; - ipu_dc_init(1, params->mem_dc_sync.di, - params->mem_dc_sync.interlaced); - ipu_di_use_count[params->mem_dc_sync.di]++; - ipu_dc_use_count++; - ipu_dmfc_use_count++; - break; - case MEM_BG_SYNC: - if (params->mem_dp_bg_sync.di > 1) { - ret = -EINVAL; - goto err; - } - - g_dc_di_assignment[5] = params->mem_dp_bg_sync.di; - ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt, - params->mem_dp_bg_sync.out_pixel_fmt); - ipu_dc_init(5, params->mem_dp_bg_sync.di, - params->mem_dp_bg_sync.interlaced); - ipu_di_use_count[params->mem_dp_bg_sync.di]++; - ipu_dc_use_count++; - ipu_dp_use_count++; - ipu_dmfc_use_count++; - break; - case MEM_FG_SYNC: - ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt, - params->mem_dp_fg_sync.out_pixel_fmt); - - ipu_dc_use_count++; - ipu_dp_use_count++; - ipu_dmfc_use_count++; - break; - default: - printf("Missing channel initialization\n"); - break; - } - - /* Enable IPU sub module */ - g_channel_init_mask |= 1L << IPU_CHAN_ID(channel); - if (ipu_dc_use_count == 1) - ipu_conf |= IPU_CONF_DC_EN; - if (ipu_dp_use_count == 1) - ipu_conf |= IPU_CONF_DP_EN; - if (ipu_dmfc_use_count == 1) - ipu_conf |= IPU_CONF_DMFC_EN; - if (ipu_di_use_count[0] == 1) { - ipu_conf |= IPU_CONF_DI0_EN; - } - if (ipu_di_use_count[1] == 1) { - ipu_conf |= IPU_CONF_DI1_EN; - } - - __raw_writel(ipu_conf, IPU_CONF); - -err: - return ret; -} - -/* - * This function is called to uninitialize a logical IPU channel. - * - * @param channel Input parameter for the logical channel ID to uninit. - */ -void ipu_uninit_channel(ipu_channel_t channel) -{ - uint32_t reg; - uint32_t in_dma, out_dma = 0; - uint32_t ipu_conf; - - if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { - debug("Channel already uninitialized %d\n", - IPU_CHAN_ID(channel)); - return; - } - - /* - * Make sure channel is disabled - * Get input and output dma channels - */ - in_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); - out_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); - - if (idma_is_set(IDMAC_CHA_EN, in_dma) || - idma_is_set(IDMAC_CHA_EN, out_dma)) { - printf( - "Channel %d is not disabled, disable first\n", - IPU_CHAN_ID(channel)); - return; - } - - ipu_conf = __raw_readl(IPU_CONF); - - /* Reset the double buffer */ - reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma)); - __raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma)); - reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma)); - __raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma)); - - switch (channel) { - case MEM_DC_SYNC: - ipu_dc_uninit(1); - ipu_di_use_count[g_dc_di_assignment[1]]--; - ipu_dc_use_count--; - ipu_dmfc_use_count--; - break; - case MEM_BG_SYNC: - ipu_dp_uninit(channel); - ipu_dc_uninit(5); - ipu_di_use_count[g_dc_di_assignment[5]]--; - ipu_dc_use_count--; - ipu_dp_use_count--; - ipu_dmfc_use_count--; - break; - case MEM_FG_SYNC: - ipu_dp_uninit(channel); - ipu_dc_use_count--; - ipu_dp_use_count--; - ipu_dmfc_use_count--; - break; - default: - break; - } - - g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); - - if (ipu_dc_use_count == 0) - ipu_conf &= ~IPU_CONF_DC_EN; - if (ipu_dp_use_count == 0) - ipu_conf &= ~IPU_CONF_DP_EN; - if (ipu_dmfc_use_count == 0) - ipu_conf &= ~IPU_CONF_DMFC_EN; - if (ipu_di_use_count[0] == 0) { - ipu_conf &= ~IPU_CONF_DI0_EN; - } - if (ipu_di_use_count[1] == 0) { - ipu_conf &= ~IPU_CONF_DI1_EN; - } - - __raw_writel(ipu_conf, IPU_CONF); - - if (ipu_conf == 0) { - clk_disable(g_ipu_clk); - g_ipu_clk_enabled = 0; - } - -} - -static inline void ipu_ch_param_dump(int ch) -{ -#ifdef DEBUG - struct ipu_ch_param *p = ipu_ch_param_addr(ch); - debug("ch %d word 0 - %08X %08X %08X %08X %08X\n", ch, - p->word[0].data[0], p->word[0].data[1], p->word[0].data[2], - p->word[0].data[3], p->word[0].data[4]); - debug("ch %d word 1 - %08X %08X %08X %08X %08X\n", ch, - p->word[1].data[0], p->word[1].data[1], p->word[1].data[2], - p->word[1].data[3], p->word[1].data[4]); - debug("PFS 0x%x, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 85, 4)); - debug("BPP 0x%x, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3)); - debug("NPB 0x%x\n", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7)); - - debug("FW %d, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 125, 13)); - debug("FH %d, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 138, 12)); - debug("Stride %d\n", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14)); - - debug("Width0 %d+1, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 116, 3)); - debug("Width1 %d+1, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 119, 3)); - debug("Width2 %d+1, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 122, 3)); - debug("Width3 %d+1, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 125, 3)); - debug("Offset0 %d, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 128, 5)); - debug("Offset1 %d, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 133, 5)); - debug("Offset2 %d, ", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 138, 5)); - debug("Offset3 %d\n", - ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 143, 5)); -#endif -} - -static inline void ipu_ch_params_set_packing(struct ipu_ch_param *p, - int red_width, int red_offset, - int green_width, int green_offset, - int blue_width, int blue_offset, - int alpha_width, int alpha_offset) -{ - /* Setup red width and offset */ - ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); - ipu_ch_param_set_field(p, 1, 128, 5, red_offset); - /* Setup green width and offset */ - ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); - ipu_ch_param_set_field(p, 1, 133, 5, green_offset); - /* Setup blue width and offset */ - ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); - ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); - /* Setup alpha width and offset */ - ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); - ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); -} - -static void ipu_ch_param_init(int ch, - uint32_t pixel_fmt, uint32_t width, - uint32_t height, uint32_t stride, - uint32_t u, uint32_t v, - uint32_t uv_stride, dma_addr_t addr0, - dma_addr_t addr1) -{ - uint32_t u_offset = 0; - uint32_t v_offset = 0; - struct ipu_ch_param params; - - memset(¶ms, 0, sizeof(params)); - - ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); - - if ((ch == 8) || (ch == 9) || (ch == 10)) { - ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); - ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); - } else { - ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); - ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); - } - - ipu_ch_param_set_field(¶ms, 1, 0, 29, addr0 >> 3); - ipu_ch_param_set_field(¶ms, 1, 29, 29, addr1 >> 3); - - switch (pixel_fmt) { - case IPU_PIX_FMT_GENERIC: - /*Represents 8-bit Generic data */ - ipu_ch_param_set_field(¶ms, 0, 107, 3, 5); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ - - break; - case IPU_PIX_FMT_GENERIC_32: - /*Represents 32-bit Generic data */ - break; - case IPU_PIX_FMT_RGB565: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ - - ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16); - break; - case IPU_PIX_FMT_BGR24: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ - - ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); - break; - case IPU_PIX_FMT_RGB24: - case IPU_PIX_FMT_YUV444: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ - - ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24); - break; - case IPU_PIX_FMT_BGRA32: - case IPU_PIX_FMT_BGR32: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ - - ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); - break; - case IPU_PIX_FMT_RGBA32: - case IPU_PIX_FMT_RGB32: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ - - ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0); - break; - case IPU_PIX_FMT_ABGR32: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ - - ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); - break; - case IPU_PIX_FMT_UYVY: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ - break; - case IPU_PIX_FMT_YUYV: - ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ - break; - case IPU_PIX_FMT_YUV420P2: - case IPU_PIX_FMT_YUV420P: - ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ - - if (uv_stride < stride / 2) - uv_stride = stride / 2; - - u_offset = stride * height; - v_offset = u_offset + (uv_stride * height / 2); - /* burst size */ - if ((ch == 8) || (ch == 9) || (ch == 10)) { - ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); - uv_stride = uv_stride*2; - } else { - ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); - } - break; - case IPU_PIX_FMT_YVU422P: - /* BPP & pixel format */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ - - if (uv_stride < stride / 2) - uv_stride = stride / 2; - - v_offset = (v == 0) ? stride * height : v; - u_offset = (u == 0) ? v_offset + v_offset / 2 : u; - break; - case IPU_PIX_FMT_YUV422P: - /* BPP & pixel format */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ - - if (uv_stride < stride / 2) - uv_stride = stride / 2; - - u_offset = (u == 0) ? stride * height : u; - v_offset = (v == 0) ? u_offset + u_offset / 2 : v; - break; - case IPU_PIX_FMT_NV12: - /* BPP & pixel format */ - ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */ - ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ - uv_stride = stride; - u_offset = (u == 0) ? stride * height : u; - break; - default: - puts("mxc ipu: unimplemented pixel format\n"); - break; - } - - - if (uv_stride) - ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1); - - /* Get the uv offset from user when need cropping */ - if (u || v) { - u_offset = u; - v_offset = v; - } - - /* UBO and VBO are 22-bit */ - if (u_offset/8 > 0x3fffff) - puts("The value of U offset exceeds IPU limitation\n"); - if (v_offset/8 > 0x3fffff) - puts("The value of V offset exceeds IPU limitation\n"); - - ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8); - ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8); - - debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch)); - memcpy(ipu_ch_param_addr(ch), ¶ms, sizeof(params)); -}; - -/* - * This function is called to initialize a buffer for logical IPU channel. - * - * @param channel Input parameter for the logical channel ID. - * - * @param type Input parameter which buffer to initialize. - * - * @param pixel_fmt Input parameter for pixel format of buffer. - * Pixel format is a FOURCC ASCII code. - * - * @param width Input parameter for width of buffer in pixels. - * - * @param height Input parameter for height of buffer in pixels. - * - * @param stride Input parameter for stride length of buffer - * in pixels. - * - * @param phyaddr_0 Input parameter buffer 0 physical address. - * - * @param phyaddr_1 Input parameter buffer 1 physical address. - * Setting this to a value other than NULL enables - * double buffering mode. - * - * @param u private u offset for additional cropping, - * zero if not used. - * - * @param v private v offset for additional cropping, - * zero if not used. - * - * @return Returns 0 on success or negative error code on fail - */ -int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, - uint32_t pixel_fmt, - uint16_t width, uint16_t height, - uint32_t stride, - dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, - uint32_t u, uint32_t v) -{ - uint32_t reg; - uint32_t dma_chan; - - dma_chan = channel_2_dma(channel, type); - if (!idma_is_valid(dma_chan)) - return -EINVAL; - - if (stride < width * bytes_per_pixel(pixel_fmt)) - stride = width * bytes_per_pixel(pixel_fmt); - - if (stride % 4) { - printf( - "Stride not 32-bit aligned, stride = %d\n", stride); - return -EINVAL; - } - /* Build parameter memory data for DMA channel */ - ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0, - phyaddr_0, phyaddr_1); - - if (ipu_is_dmfc_chan(dma_chan)) { - ipu_dmfc_set_wait4eot(dma_chan, width); - } - - if (idma_is_set(IDMAC_CHA_PRI, dma_chan)) - ipu_ch_param_set_high_priority(dma_chan); - - ipu_ch_param_dump(dma_chan); - - reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan)); - if (phyaddr_1) - reg |= idma_mask(dma_chan); - else - reg &= ~idma_mask(dma_chan); - __raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan)); - - /* Reset to buffer 0 */ - __raw_writel(idma_mask(dma_chan), IPU_CHA_CUR_BUF(dma_chan)); - - return 0; -} - -/* - * This function enables a logical channel. - * - * @param channel Input parameter for the logical channel ID. - * - * @return This function returns 0 on success or negative error code on - * fail. - */ -int32_t ipu_enable_channel(ipu_channel_t channel) -{ - uint32_t reg; - uint32_t in_dma; - uint32_t out_dma; - - if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { - printf("Warning: channel already enabled %d\n", - IPU_CHAN_ID(channel)); - } - - /* Get input and output dma channels */ - out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); - in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); - - if (idma_is_valid(in_dma)) { - reg = __raw_readl(IDMAC_CHA_EN(in_dma)); - __raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); - } - if (idma_is_valid(out_dma)) { - reg = __raw_readl(IDMAC_CHA_EN(out_dma)); - __raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); - } - - if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || - (channel == MEM_FG_SYNC)) - ipu_dp_dc_enable(channel); - - g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel); - - return 0; -} - -/* - * This function clear buffer ready for a logical channel. - * - * @param channel Input parameter for the logical channel ID. - * - * @param type Input parameter which buffer to clear. - * - * @param bufNum Input parameter for which buffer number clear - * ready state. - * - */ -void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type, - uint32_t bufNum) -{ - uint32_t dma_ch = channel_2_dma(channel, type); - - if (!idma_is_valid(dma_ch)) - return; - - __raw_writel(0xF0000000, IPU_GPR); /* write one to clear */ - if (bufNum == 0) { - if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) { - __raw_writel(idma_mask(dma_ch), - IPU_CHA_BUF0_RDY(dma_ch)); - } - } else { - if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) { - __raw_writel(idma_mask(dma_ch), - IPU_CHA_BUF1_RDY(dma_ch)); - } - } - __raw_writel(0x0, IPU_GPR); /* write one to set */ -} - -/* - * This function disables a logical channel. - * - * @param channel Input parameter for the logical channel ID. - * - * @param wait_for_stop Flag to set whether to wait for channel end - * of frame or return immediately. - * - * @return This function returns 0 on success or negative error code on - * fail. - */ -int32_t ipu_disable_channel(ipu_channel_t channel) -{ - uint32_t reg; - uint32_t in_dma; - uint32_t out_dma; - - if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { - debug("Channel already disabled %d\n", - IPU_CHAN_ID(channel)); - return 0; - } - - /* Get input and output dma channels */ - out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); - in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); - - if ((idma_is_valid(in_dma) && - !idma_is_set(IDMAC_CHA_EN, in_dma)) - && (idma_is_valid(out_dma) && - !idma_is_set(IDMAC_CHA_EN, out_dma))) - return -EINVAL; - - if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || - (channel == MEM_DC_SYNC)) { - ipu_dp_dc_disable(channel, 0); - } - - /* Disable DMA channel(s) */ - if (idma_is_valid(in_dma)) { - reg = __raw_readl(IDMAC_CHA_EN(in_dma)); - __raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); - __raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma)); - } - if (idma_is_valid(out_dma)) { - reg = __raw_readl(IDMAC_CHA_EN(out_dma)); - __raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); - __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma)); - } - - g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); - - /* Set channel buffers NOT to be ready */ - if (idma_is_valid(in_dma)) { - ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 0); - ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 1); - } - if (idma_is_valid(out_dma)) { - ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 0); - ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 1); - } - - return 0; -} - -uint32_t bytes_per_pixel(uint32_t fmt) -{ - switch (fmt) { - case IPU_PIX_FMT_GENERIC: /*generic data */ - case IPU_PIX_FMT_RGB332: - case IPU_PIX_FMT_YUV420P: - case IPU_PIX_FMT_YUV422P: - return 1; - break; - case IPU_PIX_FMT_RGB565: - case IPU_PIX_FMT_YUYV: - case IPU_PIX_FMT_UYVY: - return 2; - break; - case IPU_PIX_FMT_BGR24: - case IPU_PIX_FMT_RGB24: - return 3; - break; - case IPU_PIX_FMT_GENERIC_32: /*generic data */ - case IPU_PIX_FMT_BGR32: - case IPU_PIX_FMT_BGRA32: - case IPU_PIX_FMT_RGB32: - case IPU_PIX_FMT_RGBA32: - case IPU_PIX_FMT_ABGR32: - return 4; - break; - default: - return 1; - break; - } - return 0; -} - -ipu_color_space_t format_to_colorspace(uint32_t fmt) -{ - switch (fmt) { - case IPU_PIX_FMT_RGB666: - case IPU_PIX_FMT_RGB565: - case IPU_PIX_FMT_BGR24: - case IPU_PIX_FMT_RGB24: - case IPU_PIX_FMT_BGR32: - case IPU_PIX_FMT_BGRA32: - case IPU_PIX_FMT_RGB32: - case IPU_PIX_FMT_RGBA32: - case IPU_PIX_FMT_ABGR32: - case IPU_PIX_FMT_LVDS666: - case IPU_PIX_FMT_LVDS888: - return RGB; - break; - - default: - return YCbCr; - break; - } - return RGB; -} - -/* should be removed when clk framework is availiable */ -int ipu_set_ldb_clock(int rate) -{ - ldb_clk.rate = rate; - - return 0; -} - -bool ipu_clk_enabled(void) -{ - return g_ipu_clk_enabled; -} diff --git a/drivers/video/ipu_disp.c b/drivers/video/ipu_disp.c deleted file mode 100644 index 5c7722962d..0000000000 --- a/drivers/video/ipu_disp.c +++ /dev/null @@ -1,1291 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Porting to u-boot: - * - * (C) Copyright 2010 - * Stefano Babic, DENX Software Engineering, sbabic@denx.de - * - * Linux IPU driver for MX51: - * - * (C) Copyright 2005-2010 Freescale Semiconductor, Inc. - */ - -/* #define DEBUG */ - -#include -#include -#include -#include -#include -#include -#include "ipu.h" -#include "ipu_regs.h" - -enum csc_type_t { - RGB2YUV = 0, - YUV2RGB, - RGB2RGB, - YUV2YUV, - CSC_NONE, - CSC_NUM -}; - -struct dp_csc_param_t { - int mode; - const int (*coeff)[5][3]; -}; - -#define SYNC_WAVE 0 - -/* DC display ID assignments */ -#define DC_DISP_ID_SYNC(di) (di) -#define DC_DISP_ID_SERIAL 2 -#define DC_DISP_ID_ASYNC 3 - -int dmfc_type_setup; -static int dmfc_size_28, dmfc_size_29, dmfc_size_24, dmfc_size_27, dmfc_size_23; -int g_di1_tvout; - -extern struct clk *g_ipu_clk; -extern struct clk *g_ldb_clk; -extern struct clk *g_di_clk[2]; -extern struct clk *g_pixel_clk[2]; - -extern unsigned char g_ipu_clk_enabled; -extern unsigned char g_dc_di_assignment[]; - -void ipu_dmfc_init(int dmfc_type, int first) -{ - u32 dmfc_wr_chan, dmfc_dp_chan; - - if (first) { - if (dmfc_type_setup > dmfc_type) - dmfc_type = dmfc_type_setup; - else - dmfc_type_setup = dmfc_type; - - /* disable DMFC-IC channel*/ - __raw_writel(0x2, DMFC_IC_CTRL); - } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) { - printf("DMFC high resolution has set, will not change\n"); - return; - } else - dmfc_type_setup = dmfc_type; - - if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) { - /* 1 - segment 0~3; - * 5B - segement 4, 5; - * 5F - segement 6, 7; - * 1C, 2C and 6B, 6F unused; - */ - debug("IPU DMFC DC HIGH RES: 1(0~3), 5B(4,5), 5F(6,7)\n"); - dmfc_wr_chan = 0x00000088; - dmfc_dp_chan = 0x00009694; - dmfc_size_28 = 256 * 4; - dmfc_size_29 = 0; - dmfc_size_24 = 0; - dmfc_size_27 = 128 * 4; - dmfc_size_23 = 128 * 4; - } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) { - /* 1 - segment 0, 1; - * 5B - segement 2~5; - * 5F - segement 6,7; - * 1C, 2C and 6B, 6F unused; - */ - debug("IPU DMFC DP HIGH RES: 1(0,1), 5B(2~5), 5F(6,7)\n"); - dmfc_wr_chan = 0x00000090; - dmfc_dp_chan = 0x0000968a; - dmfc_size_28 = 128 * 4; - dmfc_size_29 = 0; - dmfc_size_24 = 0; - dmfc_size_27 = 128 * 4; - dmfc_size_23 = 256 * 4; - } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) { - /* 5B - segement 0~3; - * 5F - segement 4~7; - * 1, 1C, 2C and 6B, 6F unused; - */ - debug("IPU DMFC ONLY-DP HIGH RES: 5B(0~3), 5F(4~7)\n"); - dmfc_wr_chan = 0x00000000; - dmfc_dp_chan = 0x00008c88; - dmfc_size_28 = 0; - dmfc_size_29 = 0; - dmfc_size_24 = 0; - dmfc_size_27 = 256 * 4; - dmfc_size_23 = 256 * 4; - } else { - /* 1 - segment 0, 1; - * 5B - segement 4, 5; - * 5F - segement 6, 7; - * 1C, 2C and 6B, 6F unused; - */ - debug("IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n"); - dmfc_wr_chan = 0x00000090; - dmfc_dp_chan = 0x00009694; - dmfc_size_28 = 128 * 4; - dmfc_size_29 = 0; - dmfc_size_24 = 0; - dmfc_size_27 = 128 * 4; - dmfc_size_23 = 128 * 4; - } - __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN); - __raw_writel(0x202020F6, DMFC_WR_CHAN_DEF); - __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN); - /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */ - __raw_writel(0x2020F6F6, DMFC_DP_CHAN_DEF); -} - -void ipu_dmfc_set_wait4eot(int dma_chan, int width) -{ - u32 dmfc_gen1 = __raw_readl(DMFC_GENERAL1); - - if (width >= HIGH_RESOLUTION_WIDTH) { - if (dma_chan == 23) - ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DP, 0); - else if (dma_chan == 28) - ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DC, 0); - } - - if (dma_chan == 23) { /*5B*/ - if (dmfc_size_23 / width > 3) - dmfc_gen1 |= 1UL << 20; - else - dmfc_gen1 &= ~(1UL << 20); - } else if (dma_chan == 24) { /*6B*/ - if (dmfc_size_24 / width > 1) - dmfc_gen1 |= 1UL << 22; - else - dmfc_gen1 &= ~(1UL << 22); - } else if (dma_chan == 27) { /*5F*/ - if (dmfc_size_27 / width > 2) - dmfc_gen1 |= 1UL << 21; - else - dmfc_gen1 &= ~(1UL << 21); - } else if (dma_chan == 28) { /*1*/ - if (dmfc_size_28 / width > 2) - dmfc_gen1 |= 1UL << 16; - else - dmfc_gen1 &= ~(1UL << 16); - } else if (dma_chan == 29) { /*6F*/ - if (dmfc_size_29 / width > 1) - dmfc_gen1 |= 1UL << 23; - else - dmfc_gen1 &= ~(1UL << 23); - } - - __raw_writel(dmfc_gen1, DMFC_GENERAL1); -} - -static void ipu_di_data_wave_config(int di, - int wave_gen, - int access_size, int component_size) -{ - u32 reg; - reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | - (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); - __raw_writel(reg, DI_DW_GEN(di, wave_gen)); -} - -static void ipu_di_data_pin_config(int di, int wave_gen, int di_pin, int set, - int up, int down) -{ - u32 reg; - - reg = __raw_readl(DI_DW_GEN(di, wave_gen)); - reg &= ~(0x3 << (di_pin * 2)); - reg |= set << (di_pin * 2); - __raw_writel(reg, DI_DW_GEN(di, wave_gen)); - - __raw_writel((down << 16) | up, DI_DW_SET(di, wave_gen, set)); -} - -static void ipu_di_sync_config(int di, int wave_gen, - int run_count, int run_src, - int offset_count, int offset_src, - int repeat_count, int cnt_clr_src, - int cnt_polarity_gen_en, - int cnt_polarity_clr_src, - int cnt_polarity_trigger_src, - int cnt_up, int cnt_down) -{ - u32 reg; - - if ((run_count >= 0x1000) || (offset_count >= 0x1000) || - (repeat_count >= 0x1000) || - (cnt_up >= 0x400) || (cnt_down >= 0x400)) { - printf("DI%d counters out of range.\n", di); - return; - } - - reg = (run_count << 19) | (++run_src << 16) | - (offset_count << 3) | ++offset_src; - __raw_writel(reg, DI_SW_GEN0(di, wave_gen)); - reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) | - (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9); - reg |= (cnt_down << 16) | cnt_up; - if (repeat_count == 0) { - /* Enable auto reload */ - reg |= 0x10000000; - } - __raw_writel(reg, DI_SW_GEN1(di, wave_gen)); - reg = __raw_readl(DI_STP_REP(di, wave_gen)); - reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1))); - reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1)); - __raw_writel(reg, DI_STP_REP(di, wave_gen)); -} - -static void ipu_dc_map_config(int map, int byte_num, int offset, int mask) -{ - int ptr = map * 3 + byte_num; - u32 reg; - - reg = __raw_readl(DC_MAP_CONF_VAL(ptr)); - reg &= ~(0xFFFF << (16 * (ptr & 0x1))); - reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); - __raw_writel(reg, DC_MAP_CONF_VAL(ptr)); - - reg = __raw_readl(DC_MAP_CONF_PTR(map)); - reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num))); - reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); - __raw_writel(reg, DC_MAP_CONF_PTR(map)); -} - -static void ipu_dc_map_clear(int map) -{ - u32 reg = __raw_readl(DC_MAP_CONF_PTR(map)); - __raw_writel(reg & ~(0xFFFF << (16 * (map & 0x1))), - DC_MAP_CONF_PTR(map)); -} - -static void ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map, - int wave, int glue, int sync) -{ - u32 reg; - int stop = 1; - - reg = sync; - reg |= (glue << 4); - reg |= (++wave << 11); - reg |= (++map << 15); - reg |= (operand << 20) & 0xFFF00000; - __raw_writel(reg, ipu_dc_tmpl_reg + word * 2); - - reg = (operand >> 12); - reg |= opcode << 4; - reg |= (stop << 9); - __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1); -} - -static void ipu_dc_link_event(int chan, int event, int addr, int priority) -{ - u32 reg; - - reg = __raw_readl(DC_RL_CH(chan, event)); - reg &= ~(0xFFFF << (16 * (event & 0x1))); - reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); - __raw_writel(reg, DC_RL_CH(chan, event)); -} - -/* Y = R * 1.200 + G * 2.343 + B * .453 + 0.250; - * U = R * -.672 + G * -1.328 + B * 2.000 + 512.250.; - * V = R * 2.000 + G * -1.672 + B * -.328 + 512.250.; - */ -static const int rgb2ycbcr_coeff[5][3] = { - {0x4D, 0x96, 0x1D}, - {0x3D5, 0x3AB, 0x80}, - {0x80, 0x395, 0x3EB}, - {0x0000, 0x0200, 0x0200}, /* B0, B1, B2 */ - {0x2, 0x2, 0x2}, /* S0, S1, S2 */ -}; - -/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); - * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); - * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); - */ -static const int ycbcr2rgb_coeff[5][3] = { - {0x095, 0x000, 0x0CC}, - {0x095, 0x3CE, 0x398}, - {0x095, 0x0FF, 0x000}, - {0x3E42, 0x010A, 0x3DD6}, /*B0,B1,B2 */ - {0x1, 0x1, 0x1}, /*S0,S1,S2 */ -}; - -#define mask_a(a) ((u32)(a) & 0x3FF) -#define mask_b(b) ((u32)(b) & 0x3FFF) - -/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */ -static int rgb_to_yuv(int n, int red, int green, int blue) -{ - int c; - c = red * rgb2ycbcr_coeff[n][0]; - c += green * rgb2ycbcr_coeff[n][1]; - c += blue * rgb2ycbcr_coeff[n][2]; - c /= 16; - c += rgb2ycbcr_coeff[3][n] * 4; - c += 8; - c /= 16; - if (c < 0) - c = 0; - if (c > 255) - c = 255; - return c; -} - -/* - * Row is for BG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE - * Column is for FG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE - */ -static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = { - { - {DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, - {0, 0}, - {0, 0}, - {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, - {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} - }, - { - {0, 0}, - {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, - {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, - {0, 0}, - {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} - }, - { - {0, 0}, - {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, - {0, 0}, - {0, 0}, - {0, 0} - }, - { - {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} - }, - { - {DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, - {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, - {0, 0}, - {0, 0}, - {0, 0} - } -}; - -static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE; -static int color_key_4rgb = 1; - -static void ipu_dp_csc_setup(int dp, struct dp_csc_param_t dp_csc_param, - unsigned char srm_mode_update) -{ - u32 reg; - const int (*coeff)[5][3]; - - if (dp_csc_param.mode >= 0) { - reg = __raw_readl(DP_COM_CONF()); - reg &= ~DP_COM_CONF_CSC_DEF_MASK; - reg |= dp_csc_param.mode; - __raw_writel(reg, DP_COM_CONF()); - } - - coeff = dp_csc_param.coeff; - - if (coeff) { - __raw_writel(mask_a((*coeff)[0][0]) | - (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0()); - __raw_writel(mask_a((*coeff)[0][2]) | - (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1()); - __raw_writel(mask_a((*coeff)[1][1]) | - (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2()); - __raw_writel(mask_a((*coeff)[2][0]) | - (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3()); - __raw_writel(mask_a((*coeff)[2][2]) | - (mask_b((*coeff)[3][0]) << 16) | - ((*coeff)[4][0] << 30), DP_CSC_0()); - __raw_writel(mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) | - (mask_b((*coeff)[3][2]) << 16) | - ((*coeff)[4][2] << 30), DP_CSC_1()); - } - - if (srm_mode_update) { - reg = __raw_readl(IPU_SRM_PRI2) | 0x8; - __raw_writel(reg, IPU_SRM_PRI2); - } -} - -int ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt, - uint32_t out_pixel_fmt) -{ - int in_fmt, out_fmt; - int dp; - int partial = 0; - uint32_t reg; - - if (channel == MEM_FG_SYNC) { - dp = DP_SYNC; - partial = 1; - } else if (channel == MEM_BG_SYNC) { - dp = DP_SYNC; - partial = 0; - } else if (channel == MEM_BG_ASYNC0) { - dp = DP_ASYNC0; - partial = 0; - } else { - return -EINVAL; - } - - in_fmt = format_to_colorspace(in_pixel_fmt); - out_fmt = format_to_colorspace(out_pixel_fmt); - - if (partial) { - if (in_fmt == RGB) { - if (out_fmt == RGB) - fg_csc_type = RGB2RGB; - else - fg_csc_type = RGB2YUV; - } else { - if (out_fmt == RGB) - fg_csc_type = YUV2RGB; - else - fg_csc_type = YUV2YUV; - } - } else { - if (in_fmt == RGB) { - if (out_fmt == RGB) - bg_csc_type = RGB2RGB; - else - bg_csc_type = RGB2YUV; - } else { - if (out_fmt == RGB) - bg_csc_type = YUV2RGB; - else - bg_csc_type = YUV2YUV; - } - } - - /* Transform color key from rgb to yuv if CSC is enabled */ - reg = __raw_readl(DP_COM_CONF()); - if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) && - (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || - ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || - ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || - ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) { - int red, green, blue; - int y, u, v; - uint32_t color_key = __raw_readl(DP_GRAPH_WIND_CTRL()) & - 0xFFFFFFL; - - debug("_ipu_dp_init color key 0x%x need change to yuv fmt!\n", - color_key); - - red = (color_key >> 16) & 0xFF; - green = (color_key >> 8) & 0xFF; - blue = color_key & 0xFF; - - y = rgb_to_yuv(0, red, green, blue); - u = rgb_to_yuv(1, red, green, blue); - v = rgb_to_yuv(2, red, green, blue); - color_key = (y << 16) | (u << 8) | v; - - reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0xFF000000L; - __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL()); - color_key_4rgb = 0; - - debug("_ipu_dp_init color key change to yuv fmt 0x%x!\n", - color_key); - } - - ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 1); - - return 0; -} - -void ipu_dp_uninit(ipu_channel_t channel) -{ - int dp; - int partial = 0; - - if (channel == MEM_FG_SYNC) { - dp = DP_SYNC; - partial = 1; - } else if (channel == MEM_BG_SYNC) { - dp = DP_SYNC; - partial = 0; - } else if (channel == MEM_BG_ASYNC0) { - dp = DP_ASYNC0; - partial = 0; - } else { - return; - } - - if (partial) - fg_csc_type = CSC_NONE; - else - bg_csc_type = CSC_NONE; - - ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], 0); -} - -void ipu_dc_init(int dc_chan, int di, unsigned char interlaced) -{ - u32 reg = 0; - - if ((dc_chan == 1) || (dc_chan == 5)) { - if (interlaced) { - ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3); - ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1); - } else { - if (di) { - ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3); - ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, - 4, 1); - } else { - ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3); - ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, - 7, 1); - } - } - ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); - - reg = 0x2; - reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; - reg |= di << 2; - if (interlaced) - reg |= DC_WR_CH_CONF_FIELD_MODE; - } else if ((dc_chan == 8) || (dc_chan == 9)) { - /* async channels */ - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1); - - reg = 0x3; - reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; - } - __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); - - __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan)); - - __raw_writel(0x00000084, DC_GEN); -} - -void ipu_dc_uninit(int dc_chan) -{ - if ((dc_chan == 1) || (dc_chan == 5)) { - ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0); - } else if ((dc_chan == 8) || (dc_chan == 9)) { - ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0); - ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0); - } -} - -void ipu_dp_dc_enable(ipu_channel_t channel) -{ - int di; - uint32_t reg; - uint32_t dc_chan; - - if (channel == MEM_DC_SYNC) - dc_chan = 1; - else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) - dc_chan = 5; - else - return; - - if (channel == MEM_FG_SYNC) { - /* Enable FG channel */ - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF()); - - reg = __raw_readl(IPU_SRM_PRI2) | 0x8; - __raw_writel(reg, IPU_SRM_PRI2); - return; - } - - di = g_dc_di_assignment[dc_chan]; - - /* Make sure other DC sync channel is not assigned same DI */ - reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan)); - if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) { - reg &= ~DC_WR_CH_CONF_PROG_DI_ID; - reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID; - __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); - } - - reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); - reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET; - __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); - - clk_enable(g_pixel_clk[di]); -} - -static unsigned char dc_swap; - -void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) -{ - uint32_t reg; - uint32_t csc; - uint32_t dc_chan = 0; - int timeout = 50; - int irq = 0; - - dc_swap = swap; - - if (channel == MEM_DC_SYNC) { - dc_chan = 1; - irq = IPU_IRQ_DC_FC_1; - } else if (channel == MEM_BG_SYNC) { - dc_chan = 5; - irq = IPU_IRQ_DP_SF_END; - } else if (channel == MEM_FG_SYNC) { - /* Disable FG channel */ - dc_chan = 5; - - reg = __raw_readl(DP_COM_CONF()); - csc = reg & DP_COM_CONF_CSC_DEF_MASK; - if (csc == DP_COM_CONF_CSC_DEF_FG) - reg &= ~DP_COM_CONF_CSC_DEF_MASK; - - reg &= ~DP_COM_CONF_FG_EN; - __raw_writel(reg, DP_COM_CONF()); - - reg = __raw_readl(IPU_SRM_PRI2) | 0x8; - __raw_writel(reg, IPU_SRM_PRI2); - - timeout = 50; - - /* - * Wait for DC triple buffer to empty, - * this check is useful for tv overlay. - */ - if (g_dc_di_assignment[dc_chan] == 0) - while ((__raw_readl(DC_STAT) & 0x00000002) - != 0x00000002) { - udelay(2000); - timeout -= 2; - if (timeout <= 0) - break; - } - else if (g_dc_di_assignment[dc_chan] == 1) - while ((__raw_readl(DC_STAT) & 0x00000020) - != 0x00000020) { - udelay(2000); - timeout -= 2; - if (timeout <= 0) - break; - } - return; - } else { - return; - } - - if (dc_swap) { - /* Swap DC channel 1 and 5 settings, and disable old dc chan */ - reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); - __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan)); - reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - reg ^= DC_WR_CH_CONF_PROG_DI_ID; - __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); - } else { - /* Make sure that we leave at the irq starting edge */ - __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq)); - do { - reg = __raw_readl(IPUIRQ_2_STATREG(irq)); - } while (!(reg & IPUIRQ_2_MASK(irq))); - - reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); - reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; - __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); - - reg = __raw_readl(IPU_DISP_GEN); - if (g_dc_di_assignment[dc_chan]) - reg &= ~DI1_COUNTER_RELEASE; - else - reg &= ~DI0_COUNTER_RELEASE; - __raw_writel(reg, IPU_DISP_GEN); - - /* Clock is already off because it must be done quickly, but - we need to fix the ref count */ - clk_disable(g_pixel_clk[g_dc_di_assignment[dc_chan]]); - } -} - -void ipu_init_dc_mappings(void) -{ - /* IPU_PIX_FMT_RGB24 */ - ipu_dc_map_clear(0); - ipu_dc_map_config(0, 0, 7, 0xFF); - ipu_dc_map_config(0, 1, 15, 0xFF); - ipu_dc_map_config(0, 2, 23, 0xFF); - - /* IPU_PIX_FMT_RGB666 */ - ipu_dc_map_clear(1); - ipu_dc_map_config(1, 0, 5, 0xFC); - ipu_dc_map_config(1, 1, 11, 0xFC); - ipu_dc_map_config(1, 2, 17, 0xFC); - - /* IPU_PIX_FMT_YUV444 */ - ipu_dc_map_clear(2); - ipu_dc_map_config(2, 0, 15, 0xFF); - ipu_dc_map_config(2, 1, 23, 0xFF); - ipu_dc_map_config(2, 2, 7, 0xFF); - - /* IPU_PIX_FMT_RGB565 */ - ipu_dc_map_clear(3); - ipu_dc_map_config(3, 0, 4, 0xF8); - ipu_dc_map_config(3, 1, 10, 0xFC); - ipu_dc_map_config(3, 2, 15, 0xF8); - - /* IPU_PIX_FMT_LVDS666 */ - ipu_dc_map_clear(4); - ipu_dc_map_config(4, 0, 5, 0xFC); - ipu_dc_map_config(4, 1, 13, 0xFC); - ipu_dc_map_config(4, 2, 21, 0xFC); -} - -static int ipu_pixfmt_to_map(uint32_t fmt) -{ - switch (fmt) { - case IPU_PIX_FMT_GENERIC: - case IPU_PIX_FMT_RGB24: - return 0; - case IPU_PIX_FMT_RGB666: - return 1; - case IPU_PIX_FMT_YUV444: - return 2; - case IPU_PIX_FMT_RGB565: - return 3; - case IPU_PIX_FMT_LVDS666: - return 4; - } - - return -1; -} - -/* - * This function is called to initialize a synchronous LCD panel. - * - * @param disp The DI the panel is attached to. - * - * @param pixel_clk Desired pixel clock frequency in Hz. - * - * @param pixel_fmt Input parameter for pixel format of buffer. - * Pixel format is a FOURCC ASCII code. - * - * @param width The width of panel in pixels. - * - * @param height The height of panel in pixels. - * - * @param hStartWidth The number of pixel clocks between the HSYNC - * signal pulse and the start of valid data. - * - * @param hSyncWidth The width of the HSYNC signal in units of pixel - * clocks. - * - * @param hEndWidth The number of pixel clocks between the end of - * valid data and the HSYNC signal for next line. - * - * @param vStartWidth The number of lines between the VSYNC - * signal pulse and the start of valid data. - * - * @param vSyncWidth The width of the VSYNC signal in units of lines - * - * @param vEndWidth The number of lines between the end of valid - * data and the VSYNC signal for next frame. - * - * @param sig Bitfield of signal polarities for LCD interface. - * - * @return This function returns 0 on success or negative error code on - * fail. - */ - -int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk, - uint16_t width, uint16_t height, - uint32_t pixel_fmt, - uint16_t h_start_width, uint16_t h_sync_width, - uint16_t h_end_width, uint16_t v_start_width, - uint16_t v_sync_width, uint16_t v_end_width, - uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig) -{ - uint32_t reg; - uint32_t di_gen, vsync_cnt; - uint32_t div, rounded_pixel_clk; - uint32_t h_total, v_total; - int map; - struct clk *di_parent; - - debug("panel size = %d x %d\n", width, height); - - if ((v_sync_width == 0) || (h_sync_width == 0)) - return -EINVAL; - - /* adapt panel to ipu restricitions */ - if (v_end_width < 2) { - v_end_width = 2; - puts("WARNING: v_end_width (lower_margin) must be >= 2, adjusted\n"); - } - - h_total = width + h_sync_width + h_start_width + h_end_width; - v_total = height + v_sync_width + v_start_width + v_end_width; - - /* Init clocking */ - debug("pixel clk = %dHz\n", pixel_clk); - - if (sig.ext_clk) { - if (!(g_di1_tvout && (disp == 1))) { /*not round div for tvout*/ - /* - * Set the PLL to be an even multiple - * of the pixel clock. - */ - if ((clk_get_usecount(g_pixel_clk[0]) == 0) && - (clk_get_usecount(g_pixel_clk[1]) == 0)) { - di_parent = clk_get_parent(g_di_clk[disp]); - rounded_pixel_clk = - clk_round_rate(g_pixel_clk[disp], - pixel_clk); - div = clk_get_rate(di_parent) / - rounded_pixel_clk; - if (div % 2) - div++; - if (clk_get_rate(di_parent) != div * - rounded_pixel_clk) - clk_set_rate(di_parent, - div * rounded_pixel_clk); - udelay(10000); - clk_set_rate(g_di_clk[disp], - 2 * rounded_pixel_clk); - udelay(10000); - } - } - clk_set_parent(g_pixel_clk[disp], g_ldb_clk); - } else { - if (clk_get_usecount(g_pixel_clk[disp]) != 0) - clk_set_parent(g_pixel_clk[disp], g_ipu_clk); - } - rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk); - clk_set_rate(g_pixel_clk[disp], rounded_pixel_clk); - udelay(5000); - /* Get integer portion of divider */ - div = clk_get_rate(clk_get_parent(g_pixel_clk[disp])) / - rounded_pixel_clk; - - ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1); - ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); - - map = ipu_pixfmt_to_map(pixel_fmt); - if (map < 0) { - debug("IPU_DISP: No MAP\n"); - return -EINVAL; - } - - di_gen = __raw_readl(DI_GENERAL(disp)); - - if (sig.interlaced) { - /* Setup internal HSYNC waveform */ - ipu_di_sync_config( - disp, /* display */ - 1, /* counter */ - h_total / 2 - 1,/* run count */ - DI_SYNC_CLK, /* run_resolution */ - 0, /* offset */ - DI_SYNC_NONE, /* offset resolution */ - 0, /* repeat count */ - DI_SYNC_NONE, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 0 /* COUNT DOWN */ - ); - - /* Field 1 VSYNC waveform */ - ipu_di_sync_config( - disp, /* display */ - 2, /* counter */ - h_total - 1, /* run count */ - DI_SYNC_CLK, /* run_resolution */ - 0, /* offset */ - DI_SYNC_NONE, /* offset resolution */ - 0, /* repeat count */ - DI_SYNC_NONE, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 4 /* COUNT DOWN */ - ); - - /* Setup internal HSYNC waveform */ - ipu_di_sync_config( - disp, /* display */ - 3, /* counter */ - v_total * 2 - 1,/* run count */ - DI_SYNC_INT_HSYNC, /* run_resolution */ - 1, /* offset */ - DI_SYNC_INT_HSYNC, /* offset resolution */ - 0, /* repeat count */ - DI_SYNC_NONE, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 4 /* COUNT DOWN */ - ); - - /* Active Field ? */ - ipu_di_sync_config( - disp, /* display */ - 4, /* counter */ - v_total / 2 - 1,/* run count */ - DI_SYNC_HSYNC, /* run_resolution */ - v_start_width, /* offset */ - DI_SYNC_HSYNC, /* offset resolution */ - 2, /* repeat count */ - DI_SYNC_VSYNC, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 0 /* COUNT DOWN */ - ); - - /* Active Line */ - ipu_di_sync_config( - disp, /* display */ - 5, /* counter */ - 0, /* run count */ - DI_SYNC_HSYNC, /* run_resolution */ - 0, /* offset */ - DI_SYNC_NONE, /* offset resolution */ - height / 2, /* repeat count */ - 4, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 0 /* COUNT DOWN */ - ); - - /* Field 0 VSYNC waveform */ - ipu_di_sync_config( - disp, /* display */ - 6, /* counter */ - v_total - 1, /* run count */ - DI_SYNC_HSYNC, /* run_resolution */ - 0, /* offset */ - DI_SYNC_NONE, /* offset resolution */ - 0, /* repeat count */ - DI_SYNC_NONE, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 0 /* COUNT DOWN */ - ); - - /* DC VSYNC waveform */ - vsync_cnt = 7; - ipu_di_sync_config( - disp, /* display */ - 7, /* counter */ - v_total / 2 - 1,/* run count */ - DI_SYNC_HSYNC, /* run_resolution */ - 9, /* offset */ - DI_SYNC_HSYNC, /* offset resolution */ - 2, /* repeat count */ - DI_SYNC_VSYNC, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 0 /* COUNT DOWN */ - ); - - /* active pixel waveform */ - ipu_di_sync_config( - disp, /* display */ - 8, /* counter */ - 0, /* run count */ - DI_SYNC_CLK, /* run_resolution */ - h_start_width, /* offset */ - DI_SYNC_CLK, /* offset resolution */ - width, /* repeat count */ - 5, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 0 /* COUNT DOWN */ - ); - - ipu_di_sync_config( - disp, /* display */ - 9, /* counter */ - v_total - 1, /* run count */ - DI_SYNC_INT_HSYNC,/* run_resolution */ - v_total / 2, /* offset */ - DI_SYNC_INT_HSYNC,/* offset resolution */ - 0, /* repeat count */ - DI_SYNC_HSYNC, /* CNT_CLR_SEL */ - 0, /* CNT_POLARITY_GEN_EN */ - DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ - DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ - 0, /* COUNT UP */ - 4 /* COUNT DOWN */ - ); - - /* set gentime select and tag sel */ - reg = __raw_readl(DI_SW_GEN1(disp, 9)); - reg &= 0x1FFFFFFF; - reg |= (3 - 1)<<29 | 0x00008000; - __raw_writel(reg, DI_SW_GEN1(disp, 9)); - - __raw_writel(v_total / 2 - 1, DI_SCR_CONF(disp)); - - /* set y_sel = 1 */ - di_gen |= 0x10000000; - di_gen |= DI_GEN_POLARITY_5; - di_gen |= DI_GEN_POLARITY_8; - } else { - /* Setup internal HSYNC waveform */ - ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK, - 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, - 0, DI_SYNC_NONE, - DI_SYNC_NONE, 0, 0); - - /* Setup external (delayed) HSYNC waveform */ - ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1, - DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK, - 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, - DI_SYNC_CLK, 0, h_sync_width * 2); - /* Setup VSYNC waveform */ - vsync_cnt = DI_SYNC_VSYNC; - ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1, - DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0, - DI_SYNC_NONE, 1, DI_SYNC_NONE, - DI_SYNC_INT_HSYNC, 0, v_sync_width * 2); - __raw_writel(v_total - 1, DI_SCR_CONF(disp)); - - /* Setup active data waveform to sync with DC */ - ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC, - v_sync_width + v_start_width, DI_SYNC_HSYNC, - height, - DI_SYNC_VSYNC, 0, DI_SYNC_NONE, - DI_SYNC_NONE, 0, 0); - ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK, - h_sync_width + h_start_width, DI_SYNC_CLK, - width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, - 0); - - /* reset all unused counters */ - __raw_writel(0, DI_SW_GEN0(disp, 6)); - __raw_writel(0, DI_SW_GEN1(disp, 6)); - __raw_writel(0, DI_SW_GEN0(disp, 7)); - __raw_writel(0, DI_SW_GEN1(disp, 7)); - __raw_writel(0, DI_SW_GEN0(disp, 8)); - __raw_writel(0, DI_SW_GEN1(disp, 8)); - __raw_writel(0, DI_SW_GEN0(disp, 9)); - __raw_writel(0, DI_SW_GEN1(disp, 9)); - - reg = __raw_readl(DI_STP_REP(disp, 6)); - reg &= 0x0000FFFF; - __raw_writel(reg, DI_STP_REP(disp, 6)); - __raw_writel(0, DI_STP_REP(disp, 7)); - __raw_writel(0, DI_STP_REP9(disp)); - - /* Init template microcode */ - if (disp) { - ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5); - ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5); - ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5); - } else { - ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5); - ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5); - ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5); - } - - if (sig.Hsync_pol) - di_gen |= DI_GEN_POLARITY_2; - if (sig.Vsync_pol) - di_gen |= DI_GEN_POLARITY_3; - - if (!sig.clk_pol) - di_gen |= DI_GEN_POL_CLK; - - } - - __raw_writel(di_gen, DI_GENERAL(disp)); - - __raw_writel((--vsync_cnt << DI_VSYNC_SEL_OFFSET) | - 0x00000002, DI_SYNC_AS_GEN(disp)); - - reg = __raw_readl(DI_POL(disp)); - reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); - if (sig.enable_pol) - reg |= DI_POL_DRDY_POLARITY_15; - if (sig.data_pol) - reg |= DI_POL_DRDY_DATA_POLARITY; - __raw_writel(reg, DI_POL(disp)); - - __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); - - return 0; -} - -/* - * This function sets the foreground and background plane global alpha blending - * modes. This function also sets the DP graphic plane according to the - * parameter of IPUv3 DP channel. - * - * @param channel IPUv3 DP channel - * - * @param enable Boolean to enable or disable global alpha - * blending. If disabled, local blending is used. - * - * @param alpha Global alpha value. - * - * @return Returns 0 on success or negative error code on fail - */ -int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, unsigned char enable, - uint8_t alpha) -{ - uint32_t reg; - - unsigned char bg_chan; - - if (!((channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) || - (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) || - (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1))) - return -EINVAL; - - if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 || - channel == MEM_BG_ASYNC1) - bg_chan = 1; - else - bg_chan = 0; - - if (!g_ipu_clk_enabled) - clk_enable(g_ipu_clk); - - if (bg_chan) { - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF()); - } else { - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg | DP_COM_CONF_GWSEL, DP_COM_CONF()); - } - - if (enable) { - reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0x00FFFFFFL; - __raw_writel(reg | ((uint32_t) alpha << 24), - DP_GRAPH_WIND_CTRL()); - - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF()); - } else { - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF()); - } - - reg = __raw_readl(IPU_SRM_PRI2) | 0x8; - __raw_writel(reg, IPU_SRM_PRI2); - - if (!g_ipu_clk_enabled) - clk_disable(g_ipu_clk); - - return 0; -} - -/* - * This function sets the transparent color key for SDC graphic plane. - * - * @param channel Input parameter for the logical channel ID. - * - * @param enable Boolean to enable or disable color key - * - * @param colorKey 24-bit RGB color for transparent color key. - * - * @return Returns 0 on success or negative error code on fail - */ -int32_t ipu_disp_set_color_key(ipu_channel_t channel, unsigned char enable, - uint32_t color_key) -{ - uint32_t reg; - int y, u, v; - int red, green, blue; - - if (!((channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) || - (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) || - (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1))) - return -EINVAL; - - if (!g_ipu_clk_enabled) - clk_enable(g_ipu_clk); - - color_key_4rgb = 1; - /* Transform color key from rgb to yuv if CSC is enabled */ - if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) || - ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) || - ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) || - ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB))) { - - debug("color key 0x%x need change to yuv fmt\n", color_key); - - red = (color_key >> 16) & 0xFF; - green = (color_key >> 8) & 0xFF; - blue = color_key & 0xFF; - - y = rgb_to_yuv(0, red, green, blue); - u = rgb_to_yuv(1, red, green, blue); - v = rgb_to_yuv(2, red, green, blue); - color_key = (y << 16) | (u << 8) | v; - - color_key_4rgb = 0; - - debug("color key change to yuv fmt 0x%x\n", color_key); - } - - if (enable) { - reg = __raw_readl(DP_GRAPH_WIND_CTRL()) & 0xFF000000L; - __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL()); - - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF()); - } else { - reg = __raw_readl(DP_COM_CONF()); - __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF()); - } - - reg = __raw_readl(IPU_SRM_PRI2) | 0x8; - __raw_writel(reg, IPU_SRM_PRI2); - - if (!g_ipu_clk_enabled) - clk_disable(g_ipu_clk); - - return 0; -} diff --git a/drivers/video/ipu_regs.h b/drivers/video/ipu_regs.h deleted file mode 100644 index deb44002d7..0000000000 --- a/drivers/video/ipu_regs.h +++ /dev/null @@ -1,415 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Porting to u-boot: - * - * (C) Copyright 2010 - * Stefano Babic, DENX Software Engineering, sbabic@denx.de - * - * Linux IPU driver for MX51: - * - * (C) Copyright 2005-2009 Freescale Semiconductor, Inc. - */ - -#ifndef __IPU_REGS_INCLUDED__ -#define __IPU_REGS_INCLUDED__ - -#define IPU_DISP0_BASE 0x00000000 -#define IPU_MCU_T_DEFAULT 8 -#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25) -#define IPU_CM_REG_BASE 0x00000000 -#define IPU_STAT_REG_BASE 0x00000200 -#define IPU_IDMAC_REG_BASE 0x00008000 -#define IPU_ISP_REG_BASE 0x00010000 -#define IPU_DP_REG_BASE 0x00018000 -#define IPU_IC_REG_BASE 0x00020000 -#define IPU_IRT_REG_BASE 0x00028000 -#define IPU_CSI0_REG_BASE 0x00030000 -#define IPU_CSI1_REG_BASE 0x00038000 -#define IPU_DI0_REG_BASE 0x00040000 -#define IPU_DI1_REG_BASE 0x00048000 -#define IPU_SMFC_REG_BASE 0x00050000 -#define IPU_DC_REG_BASE 0x00058000 -#define IPU_DMFC_REG_BASE 0x00060000 -#define IPU_VDI_REG_BASE 0x00680000 -#if defined(CONFIG_MX51) || defined(CONFIG_MX53) -#define IPU_CPMEM_REG_BASE 0x01000000 -#define IPU_LUT_REG_BASE 0x01020000 -#define IPU_SRM_REG_BASE 0x01040000 -#define IPU_TPM_REG_BASE 0x01060000 -#define IPU_DC_TMPL_REG_BASE 0x01080000 -#define IPU_ISP_TBPR_REG_BASE 0x010C0000 -#elif defined(CONFIG_MX6) -#define IPU_CPMEM_REG_BASE 0x00100000 -#define IPU_LUT_REG_BASE 0x00120000 -#define IPU_SRM_REG_BASE 0x00140000 -#define IPU_TPM_REG_BASE 0x00160000 -#define IPU_DC_TMPL_REG_BASE 0x00180000 -#define IPU_ISP_TBPR_REG_BASE 0x001C0000 -#endif - -#define IPU_CTRL_BASE_ADDR (IPU_SOC_BASE_ADDR + IPU_SOC_OFFSET) - -extern u32 *ipu_dc_tmpl_reg; - -#define DC_EVT_NF 0 -#define DC_EVT_NL 1 -#define DC_EVT_EOF 2 -#define DC_EVT_NFIELD 3 -#define DC_EVT_EOL 4 -#define DC_EVT_EOFIELD 5 -#define DC_EVT_NEW_ADDR 6 -#define DC_EVT_NEW_CHAN 7 -#define DC_EVT_NEW_DATA 8 - -#define DC_EVT_NEW_ADDR_W_0 0 -#define DC_EVT_NEW_ADDR_W_1 1 -#define DC_EVT_NEW_CHAN_W_0 2 -#define DC_EVT_NEW_CHAN_W_1 3 -#define DC_EVT_NEW_DATA_W_0 4 -#define DC_EVT_NEW_DATA_W_1 5 -#define DC_EVT_NEW_ADDR_R_0 6 -#define DC_EVT_NEW_ADDR_R_1 7 -#define DC_EVT_NEW_CHAN_R_0 8 -#define DC_EVT_NEW_CHAN_R_1 9 -#define DC_EVT_NEW_DATA_R_0 10 -#define DC_EVT_NEW_DATA_R_1 11 - -/* Software reset for ipu */ -#define SW_IPU_RST 8 - -enum { - IPU_CONF_DP_EN = 0x00000020, - IPU_CONF_DI0_EN = 0x00000040, - IPU_CONF_DI1_EN = 0x00000080, - IPU_CONF_DMFC_EN = 0x00000400, - IPU_CONF_DC_EN = 0x00000200, - - DI0_COUNTER_RELEASE = 0x01000000, - DI1_COUNTER_RELEASE = 0x02000000, - - DI_DW_GEN_ACCESS_SIZE_OFFSET = 24, - DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16, - - DI_GEN_DI_CLK_EXT = 0x100000, - DI_GEN_POLARITY_1 = 0x00000001, - DI_GEN_POLARITY_2 = 0x00000002, - DI_GEN_POLARITY_3 = 0x00000004, - DI_GEN_POLARITY_4 = 0x00000008, - DI_GEN_POLARITY_5 = 0x00000010, - DI_GEN_POLARITY_6 = 0x00000020, - DI_GEN_POLARITY_7 = 0x00000040, - DI_GEN_POLARITY_8 = 0x00000080, - DI_GEN_POL_CLK = 0x20000, - - DI_POL_DRDY_DATA_POLARITY = 0x00000080, - DI_POL_DRDY_POLARITY_15 = 0x00000010, - DI_VSYNC_SEL_OFFSET = 13, - - DC_WR_CH_CONF_FIELD_MODE = 0x00000200, - DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5, - DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0, - DC_WR_CH_CONF_PROG_DI_ID = 0x00000004, - DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3, - DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018, - - DP_COM_CONF_FG_EN = 0x00000001, - DP_COM_CONF_GWSEL = 0x00000002, - DP_COM_CONF_GWAM = 0x00000004, - DP_COM_CONF_GWCKE = 0x00000008, - DP_COM_CONF_CSC_DEF_MASK = 0x00000300, - DP_COM_CONF_CSC_DEF_OFFSET = 8, - DP_COM_CONF_CSC_DEF_FG = 0x00000300, - DP_COM_CONF_CSC_DEF_BG = 0x00000200, - DP_COM_CONF_CSC_DEF_BOTH = 0x00000100, - DP_COM_CONF_GAMMA_EN = 0x00001000, - DP_COM_CONF_GAMMA_YUV_EN = 0x00002000, -}; - -enum di_pins { - DI_PIN11 = 0, - DI_PIN12 = 1, - DI_PIN13 = 2, - DI_PIN14 = 3, - DI_PIN15 = 4, - DI_PIN16 = 5, - DI_PIN17 = 6, - DI_PIN_CS = 7, - - DI_PIN_SER_CLK = 0, - DI_PIN_SER_RS = 1, -}; - -enum di_sync_wave { - DI_SYNC_NONE = -1, - DI_SYNC_CLK = 0, - DI_SYNC_INT_HSYNC = 1, - DI_SYNC_HSYNC = 2, - DI_SYNC_VSYNC = 3, - DI_SYNC_DE = 5, -}; - -struct ipu_cm { - u32 conf; - u32 sisg_ctrl0; - u32 sisg_ctrl1; - u32 sisg_set[6]; - u32 sisg_clear[6]; - u32 int_ctrl[15]; - u32 sdma_event[10]; - u32 srm_pri1; - u32 srm_pri2; - u32 fs_proc_flow[3]; - u32 fs_disp_flow[2]; - u32 skip; - u32 disp_alt_conf; - u32 disp_gen; - u32 disp_alt[4]; - u32 snoop; - u32 mem_rst; - u32 pm; - u32 gpr; - u32 reserved0[26]; - u32 ch_db_mode_sel[2]; - u32 reserved1[4]; - u32 alt_ch_db_mode_sel[2]; - u32 reserved2[2]; - u32 ch_trb_mode_sel[2]; -}; - -struct ipu_idmac { - u32 conf; - u32 ch_en[2]; - u32 sep_alpha; - u32 alt_sep_alpha; - u32 ch_pri[2]; - u32 wm_en[2]; - u32 lock_en[2]; - u32 sub_addr[5]; - u32 bndm_en[2]; - u32 sc_cord[2]; - u32 reserved[44]; - u32 ch_busy[2]; -}; - -struct ipu_com_async { - u32 com_conf_async; - u32 graph_wind_ctrl_async; - u32 fg_pos_async; - u32 cur_pos_async; - u32 cur_map_async; - u32 gamma_c_async[8]; - u32 gamma_s_async[4]; - u32 dp_csca_async[4]; - u32 dp_csc_async[2]; -}; - -struct ipu_dp { - u32 com_conf_sync; - u32 graph_wind_ctrl_sync; - u32 fg_pos_sync; - u32 cur_pos_sync; - u32 cur_map_sync; - u32 gamma_c_sync[8]; - u32 gamma_s_sync[4]; - u32 csca_sync[4]; - u32 csc_sync[2]; - u32 cur_pos_alt; - struct ipu_com_async async[2]; -}; - -struct ipu_di { - u32 general; - u32 bs_clkgen0; - u32 bs_clkgen1; - u32 sw_gen0[9]; - u32 sw_gen1[9]; - u32 sync_as; - u32 dw_gen[12]; - u32 dw_set[48]; - u32 stp_rep[4]; - u32 stp_rep9; - u32 ser_conf; - u32 ssc; - u32 pol; - u32 aw0; - u32 aw1; - u32 scr_conf; - u32 stat; -}; - -struct ipu_stat { - u32 int_stat[15]; - u32 cur_buf[2]; - u32 alt_cur_buf_0; - u32 alt_cur_buf_1; - u32 srm_stat; - u32 proc_task_stat; - u32 disp_task_stat; - u32 triple_cur_buf[4]; - u32 ch_buf0_rdy[2]; - u32 ch_buf1_rdy[2]; - u32 alt_ch_buf0_rdy[2]; - u32 alt_ch_buf1_rdy[2]; - u32 ch_buf2_rdy[2]; -}; - -struct ipu_dc_ch { - u32 wr_ch_conf; - u32 wr_ch_addr; - u32 rl[5]; -}; - -struct ipu_dc { - struct ipu_dc_ch dc_ch0_1_2[3]; - u32 cmd_ch_conf_3; - u32 cmd_ch_conf_4; - struct ipu_dc_ch dc_ch5_6[2]; - struct ipu_dc_ch dc_ch8; - u32 rl6_ch_8; - struct ipu_dc_ch dc_ch9; - u32 rl6_ch_9; - u32 gen; - u32 disp_conf1[4]; - u32 disp_conf2[4]; - u32 di0_conf[2]; - u32 di1_conf[2]; - u32 dc_map_ptr[15]; - u32 dc_map_val[12]; - u32 udge[16]; - u32 lla[2]; - u32 r_lla[2]; - u32 wr_ch_addr_5_alt; - u32 stat; -}; - -struct ipu_dmfc { - u32 rd_chan; - u32 wr_chan; - u32 wr_chan_def; - u32 dp_chan; - u32 dp_chan_def; - u32 general[2]; - u32 ic_ctrl; - u32 wr_chan_alt; - u32 wr_chan_def_alt; - u32 general1_alt; - u32 stat; -}; - -#define IPU_CM_REG ((struct ipu_cm *)(IPU_CTRL_BASE_ADDR + \ - IPU_CM_REG_BASE)) -#define IPU_CONF (&IPU_CM_REG->conf) -#define IPU_SRM_PRI1 (&IPU_CM_REG->srm_pri1) -#define IPU_SRM_PRI2 (&IPU_CM_REG->srm_pri2) -#define IPU_FS_PROC_FLOW1 (&IPU_CM_REG->fs_proc_flow[0]) -#define IPU_FS_PROC_FLOW2 (&IPU_CM_REG->fs_proc_flow[1]) -#define IPU_FS_PROC_FLOW3 (&IPU_CM_REG->fs_proc_flow[2]) -#define IPU_FS_DISP_FLOW1 (&IPU_CM_REG->fs_disp_flow[0]) -#define IPU_DISP_GEN (&IPU_CM_REG->disp_gen) -#define IPU_MEM_RST (&IPU_CM_REG->mem_rst) -#define IPU_GPR (&IPU_CM_REG->gpr) -#define IPU_CHA_DB_MODE_SEL(ch) (&IPU_CM_REG->ch_db_mode_sel[ch / 32]) - -#define IPU_STAT ((struct ipu_stat *)(IPU_CTRL_BASE_ADDR + \ - IPU_STAT_REG_BASE)) -#define IPU_INT_STAT(n) (&IPU_STAT->int_stat[(n) - 1]) -#define IPU_CHA_CUR_BUF(ch) (&IPU_STAT->cur_buf[ch / 32]) -#define IPU_CHA_BUF0_RDY(ch) (&IPU_STAT->ch_buf0_rdy[ch / 32]) -#define IPU_CHA_BUF1_RDY(ch) (&IPU_STAT->ch_buf1_rdy[ch / 32]) -#define IPUIRQ_2_STATREG(irq) (IPU_INT_STAT(1) + ((irq) / 32)) -#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F)) - -#define IPU_INT_CTRL(n) (&IPU_CM_REG->int_ctrl[(n) - 1]) - -#define IDMAC_REG ((struct ipu_idmac *)(IPU_CTRL_BASE_ADDR + \ - IPU_IDMAC_REG_BASE)) -#define IDMAC_CONF (&IDMAC_REG->conf) -#define IDMAC_CHA_EN(ch) (&IDMAC_REG->ch_en[ch / 32]) -#define IDMAC_CHA_PRI(ch) (&IDMAC_REG->ch_pri[ch / 32]) - -#define DI_REG(di) ((struct ipu_di *)(IPU_CTRL_BASE_ADDR + \ - ((di == 1) ? IPU_DI1_REG_BASE : \ - IPU_DI0_REG_BASE))) -#define DI_GENERAL(di) (&DI_REG(di)->general) -#define DI_BS_CLKGEN0(di) (&DI_REG(di)->bs_clkgen0) -#define DI_BS_CLKGEN1(di) (&DI_REG(di)->bs_clkgen1) - -#define DI_SW_GEN0(di, gen) (&DI_REG(di)->sw_gen0[gen - 1]) -#define DI_SW_GEN1(di, gen) (&DI_REG(di)->sw_gen1[gen - 1]) -#define DI_STP_REP(di, gen) (&DI_REG(di)->stp_rep[(gen - 1) / 2]) -#define DI_STP_REP9(di) (&DI_REG(di)->stp_rep9) -#define DI_SYNC_AS_GEN(di) (&DI_REG(di)->sync_as) -#define DI_DW_GEN(di, gen) (&DI_REG(di)->dw_gen[gen]) -#define DI_DW_SET(di, gen, set) (&DI_REG(di)->dw_set[gen + 12 * set]) -#define DI_POL(di) (&DI_REG(di)->pol) -#define DI_SCR_CONF(di) (&DI_REG(di)->scr_conf) - -#define DMFC_REG ((struct ipu_dmfc *)(IPU_CTRL_BASE_ADDR + \ - IPU_DMFC_REG_BASE)) -#define DMFC_WR_CHAN (&DMFC_REG->wr_chan) -#define DMFC_WR_CHAN_DEF (&DMFC_REG->wr_chan_def) -#define DMFC_DP_CHAN (&DMFC_REG->dp_chan) -#define DMFC_DP_CHAN_DEF (&DMFC_REG->dp_chan_def) -#define DMFC_GENERAL1 (&DMFC_REG->general[0]) -#define DMFC_IC_CTRL (&DMFC_REG->ic_ctrl) - - -#define DC_REG ((struct ipu_dc *)(IPU_CTRL_BASE_ADDR + \ - IPU_DC_REG_BASE)) -#define DC_MAP_CONF_PTR(n) (&DC_REG->dc_map_ptr[n / 2]) -#define DC_MAP_CONF_VAL(n) (&DC_REG->dc_map_val[n / 2]) - - -static inline struct ipu_dc_ch *dc_ch_offset(int ch) -{ - switch (ch) { - case 0: - case 1: - case 2: - return &DC_REG->dc_ch0_1_2[ch]; - case 5: - case 6: - return &DC_REG->dc_ch5_6[ch - 5]; - case 8: - return &DC_REG->dc_ch8; - case 9: - return &DC_REG->dc_ch9; - default: - printf("%s: invalid channel %d\n", __func__, ch); - return NULL; - } - -} - -#define DC_RL_CH(ch, evt) (&dc_ch_offset(ch)->rl[evt / 2]) - -#define DC_WR_CH_CONF(ch) (&dc_ch_offset(ch)->wr_ch_conf) -#define DC_WR_CH_ADDR(ch) (&dc_ch_offset(ch)->wr_ch_addr) - -#define DC_WR_CH_CONF_1 DC_WR_CH_CONF(1) -#define DC_WR_CH_CONF_5 DC_WR_CH_CONF(5) - -#define DC_GEN (&DC_REG->gen) -#define DC_DISP_CONF2(disp) (&DC_REG->disp_conf2[disp]) -#define DC_STAT (&DC_REG->stat) - -#define DP_SYNC 0 -#define DP_ASYNC0 0x60 -#define DP_ASYNC1 0xBC - -#define DP_REG ((struct ipu_dp *)(IPU_CTRL_BASE_ADDR + \ - IPU_DP_REG_BASE)) -#define DP_COM_CONF() (&DP_REG->com_conf_sync) -#define DP_GRAPH_WIND_CTRL() (&DP_REG->graph_wind_ctrl_sync) -#define DP_CSC_A_0() (&DP_REG->csca_sync[0]) -#define DP_CSC_A_1() (&DP_REG->csca_sync[1]) -#define DP_CSC_A_2() (&DP_REG->csca_sync[2]) -#define DP_CSC_A_3() (&DP_REG->csca_sync[3]) - -#define DP_CSC_0() (&DP_REG->csc_sync[0]) -#define DP_CSC_1() (&DP_REG->csc_sync[1]) - -/* DC template opcodes */ -#define WROD(lf) (0x18 | (lf << 1)) - -#endif diff --git a/drivers/video/mxc_ipuv3_fb.c b/drivers/video/mxc_ipuv3_fb.c deleted file mode 100644 index 5b3ba7b3a9..0000000000 --- a/drivers/video/mxc_ipuv3_fb.c +++ /dev/null @@ -1,700 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "videomodes.h" -#include "ipu.h" -#include "mxcfb.h" -#include "ipu_regs.h" - -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -static int mxcfb_map_video_memory(struct fb_info *fbi); -static int mxcfb_unmap_video_memory(struct fb_info *fbi); - -/* graphics setup */ -static GraphicDevice panel; -static struct fb_videomode const *gmode; -static uint8_t gdisp; -static uint32_t gpixfmt; - -static 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 = %lu Hz\n", 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->fix.smem_len = roundup(fbi->fix.smem_len, ARCH_DMA_MINALIGN); - -#if CONFIG_IS_ENABLED(DM_VIDEO) - fbi->screen_base = (char *)gd->video_bottom; -#else - fbi->screen_base = (char *)memalign(ARCH_DMA_MINALIGN, - fbi->fix.smem_len); -#endif - - fbi->fix.smem_start = (unsigned long)fbi->screen_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; - -#if CONFIG_IS_ENABLED(VIDEO) - gd->fb_base = fbi->fix.smem_start; -#endif - - /* 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, uint8_t disp, - struct fb_videomode const *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 = disp; - - 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 */ - - mxcfbi->ipu_di_pix_fmt = interface_pix_fmt; - fb_videomode_to_var(&fbi->var, mode); - fbi->var.bits_per_pixel = 16; - fbi->fix.line_length = fbi->var.xres * (fbi->var.bits_per_pixel / 8); - fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length; - - 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); - - /* allocate fb first */ - if (mxcfb_map_video_memory(fbi) < 0) - return -ENOMEM; - - mxcfb_set_par(fbi); - - panel.winSizeX = mode->xres; - panel.winSizeY = mode->yres; - panel.plnSizeX = mode->xres; - panel.plnSizeY = mode->yres; - - panel.frameAdrs = (u32)fbi->screen_base; - panel.memSize = fbi->screen_size; - - panel.gdfBytesPP = 2; - panel.gdfIndex = GDF_16BIT_565RGB; - - ipu_dump_registers(); - - return 0; - -err0: - return ret; -} - -void ipuv3_fb_shutdown(void) -{ - int i; - struct ipu_stat *stat = (struct ipu_stat *)IPU_STAT; - - if (!ipu_clk_enabled()) - return; - - for (i = 0; i < ARRAY_SIZE(mxcfb_info); i++) { - struct fb_info *fbi = mxcfb_info[i]; - if (fbi) { - struct mxcfb_info *mxc_fbi = fbi->par; - ipu_disable_channel(mxc_fbi->ipu_ch); - ipu_uninit_channel(mxc_fbi->ipu_ch); - } - } - for (i = 0; i < ARRAY_SIZE(stat->int_stat); i++) { - __raw_writel(__raw_readl(&stat->int_stat[i]), - &stat->int_stat[i]); - } -} - -void *video_hw_init(void) -{ - int ret; - - ret = ipu_probe(); - if (ret) - puts("Error initializing IPU\n"); - - ret = mxcfb_probe(gpixfmt, gdisp, gmode); - debug("Framebuffer at 0x%x\n", (unsigned int)panel.frameAdrs); - - return (void *)&panel; -} - -int ipuv3_fb_init(struct fb_videomode const *mode, - uint8_t disp, - uint32_t pixfmt) -{ - gmode = mode; - gdisp = disp; - gpixfmt = pixfmt; - - return 0; -} - -#if CONFIG_IS_ENABLED(DM_VIDEO) -enum { - /* Maximum display size we support */ - LCD_MAX_WIDTH = 1920, - LCD_MAX_HEIGHT = 1080, - LCD_MAX_LOG2_BPP = VIDEO_BPP16, -}; - -static int ipuv3_video_probe(struct udevice *dev) -{ - struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); - struct video_priv *uc_priv = dev_get_uclass_priv(dev); - u32 fb_start, fb_end; - int ret; - - debug("%s() plat: base 0x%lx, size 0x%x\n", - __func__, plat->base, plat->size); - - ret = ipu_probe(); - if (ret) - return ret; - - ret = ipu_displays_init(); - if (ret < 0) - return ret; - - ret = mxcfb_probe(gpixfmt, gdisp, gmode); - if (ret < 0) - return ret; - - uc_priv->xsize = gmode->xres; - uc_priv->ysize = gmode->yres; - uc_priv->bpix = LCD_MAX_LOG2_BPP; - - /* Enable dcache for the frame buffer */ - fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); - fb_end = plat->base + plat->size; - fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT); - mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, - DCACHE_WRITEBACK); - video_set_flush_dcache(dev, true); - - return 0; -} - -struct ipuv3_video_priv { - ulong regs; -}; - -static int ipuv3_video_bind(struct udevice *dev) -{ - struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); - - plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * - (1 << LCD_MAX_LOG2_BPP) / 8; - - return 0; -} - -static const struct udevice_id ipuv3_video_ids[] = { - { .compatible = "fsl,imx6q-ipu" }, - { } -}; - -U_BOOT_DRIVER(ipuv3_video) = { - .name = "ipuv3_video", - .id = UCLASS_VIDEO, - .of_match = ipuv3_video_ids, - .bind = ipuv3_video_bind, - .probe = ipuv3_video_probe, - .priv_auto_alloc_size = sizeof(struct ipuv3_video_priv), - .flags = DM_FLAG_PRE_RELOC, -}; -#endif /* CONFIG_DM_VIDEO */ diff --git a/drivers/video/mxcfb.h b/drivers/video/mxcfb.h deleted file mode 100644 index 0dc3886193..0000000000 --- a/drivers/video/mxcfb.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * 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. - */ - -#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