diff options
Diffstat (limited to 'drivers/video/nxp/imx')
27 files changed, 15800 insertions, 0 deletions
diff --git a/drivers/video/nxp/imx/Kconfig b/drivers/video/nxp/imx/Kconfig new file mode 100644 index 00000000000..c02014826f8 --- /dev/null +++ b/drivers/video/nxp/imx/Kconfig @@ -0,0 +1,98 @@ + +config VIDEO_IPUV3 + bool "i.MX IPUv3 Core video support" + depends on DM_VIDEO && (MX5 || MX6) + help + This enables framebuffer driver for i.MX processors working + on the IPUv3(Image Processing Unit) internal graphic processor. + +config VIDEO_IMXDPUV1 + bool "i.MX DPU V1 display support" + default n + depends on IMX8 && DM_VIDEO + select VIDEO_LINK + help + Support for IMXDPU V1 display controller for i.MX8 processors. + +config VIDEO_IMX8_LVDS + bool "i.MX8 LDVS bridge support" + default n + depends on IMX8 && DM_VIDEO + select DISPLAY + select VIDEO_LINK + help + Support for i.MX8 LDVS bridge controller for i.MX8 processors. + +config VIDEO_IMX_HDP_LOAD + bool "i.MX8 HDMI/DP firmware loading" + default n + depends on IMX8QM + select VIDEO_NXP_HDP + help + Support for HDMI/DP firmware loading for i.MX8QM processors. The + firmware is copied from system memory to the HDMI/DP IRAM and + DRAM memory. + +config VIDEO_IMX8M_DCSS + bool "i.MX8M DCSS controller" + default n + depends on IMX8M && DM_VIDEO + select VIDEO_LINK + help + Support for DCSS on i.MX8MQ processors. + +config VIDEO_IMX8M_HDMI + bool "i.MX8M HDMI Splash screen" + default n + depends on IMX8M && DM_VIDEO + select DISPLAY + select VIDEO_LINK + select VIDEO_NXP_HDP + help + Support for HDMI on i.MX8MQ processors. + +config VIDEO_SEC_MIPI_DSI + bool + select VIDEO_MIPI_DSI + help + Enables the common driver code for the Samsung + MIPI DSI block found in SoCs from various vendors. + As this does not provide any functionality by itself (but + rather requires a SoC-specific glue driver to call it), it + can not be enabled from the configuration menu. + +config VIDEO_NW_MIPI_DSI + bool + select VIDEO_MIPI_DSI + help + Enables the common driver code for the Northwest + MIPI DSI block found in SoCs from various vendors. + As this does not provide any functionality by itself (but + rather requires a SoC-specific glue driver to call it), it + can not be enabled from the configuration menu. + +config VIDEO_IMX_SEC_DSI + bool "Enable IMX SEC DSI video support" + select VIDEO_BRIDGE + select VIDEO_SEC_MIPI_DSI + select VIDEO_LINK + help + This option enables support DSI internal bridge which can be used on + devices which have DSI devices connected. + +config VIDEO_IMX_NW_DSI + bool "Enable IMX Northwest DSI video support" + select VIDEO_BRIDGE + select VIDEO_NW_MIPI_DSI + select VIDEO_LINK + help + This option enables support DSI internal bridge which can be used on + devices which have DSI devices connected. + +config VIDEO_IMX_LCDIFV3 + bool "i.MX LCDIFv3 support" + depends on DM_VIDEO && IMX8MP + select VIDEO_LINK + help + Support for i.MX8MP LCDIFv3 controller. + diff --git a/drivers/video/nxp/imx/Makefile b/drivers/video/nxp/imx/Makefile new file mode 100644 index 00000000000..b9e1695a65b --- /dev/null +++ b/drivers/video/nxp/imx/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o +obj-$(CONFIG_VIDEO_IMXDPUV1) += imxdpuv1.o imx8_dc.o +obj-$(CONFIG_VIDEO_IMX8_LVDS) += imx8_lvds.o +obj-$(CONFIG_VIDEO_IMX8M_DCSS) += imx8m_dcss.o +obj-$(CONFIG_VIDEO_SEC_MIPI_DSI) += sec_mipi_dsim.o +obj-$(CONFIG_VIDEO_IMX_SEC_DSI) += sec_dsim_imx.o +obj-$(CONFIG_VIDEO_IMX_LCDIFV3) += imx_lcdifv3.o +obj-$(CONFIG_VIDEO_NW_MIPI_DSI) += mipi_dsi_northwest.o +obj-$(CONFIG_VIDEO_IMX_NW_DSI) += nw_dsi_imx.o +obj-y += hdmi/ diff --git a/drivers/video/nxp/imx/hdmi/Makefile b/drivers/video/nxp/imx/hdmi/Makefile new file mode 100644 index 00000000000..40942da3a40 --- /dev/null +++ b/drivers/video/nxp/imx/hdmi/Makefile @@ -0,0 +1,8 @@ +# +# Copyright 2017-2018 NXP +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_VIDEO_IMX_HDP_LOAD) += hdp_load.o hdprx_load.o +obj-$(CONFIG_VIDEO_IMX8M_HDMI) += imx8m_hdmi.o diff --git a/drivers/video/nxp/imx/hdmi/hdp.c b/drivers/video/nxp/imx/hdmi/hdp.c new file mode 100644 index 00000000000..d84716ac63d --- /dev/null +++ b/drivers/video/nxp/imx/hdmi/hdp.c @@ -0,0 +1,46 @@ +/* + * Copyright 2018 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <asm/mach-imx/video.h> +#include <asm/arch/video_common.h> +#include <imx8_hdmi.h> + +int do_hdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc < 2) + return 0; + + if (strncmp(argv[1], "colorbar", 8) == 0) { + GraphicDevice *gdev; + struct video_mode_settings *vm; + + gdev = imx8m_get_gd(); + vm = imx8m_get_gmode(); + imx8m_show_gmode(); + + imx8m_create_color_bar( + (void *)((uint64_t)gdev->frameAdrs), + vm); + printf("colorbar test\n"); + } else if (strncmp(argv[1], "stop", 4) == 0) { + imx8_hdmi_disable(); + printf("stopping hdmi\n"); + } else { + printf("test error argc %d\n", argc); + } + + return 0; +} +/***************************************************/ + +U_BOOT_CMD( + hdp, CONFIG_SYS_MAXARGS, 1, do_hdp, + "hdmi/dp display test commands", + "[<command>] ...\n" + "colorbar - display a colorbar pattern\n" + ); diff --git a/drivers/video/nxp/imx/hdmi/hdp_load.c b/drivers/video/nxp/imx/hdmi/hdp_load.c new file mode 100644 index 00000000000..be6b118e39f --- /dev/null +++ b/drivers/video/nxp/imx/hdmi/hdp_load.c @@ -0,0 +1,118 @@ +/* + * Copyright 2017-2018 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <asm/global_data.h> + +#include "API_General.h" +#include "scfw_utils.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define ON 1 +#define OFF 0 + +static void display_set_power(int onoff) +{ + SC_PM_SET_RESOURCE_POWER_MODE(-1, SC_R_DC_0, onoff); + SC_PM_SET_RESOURCE_POWER_MODE(-1, SC_R_HDMI, onoff); +} + +static void display_set_clocks(void) +{ + const sc_pm_clock_rate_t pll = 800000000; + const sc_pm_clock_rate_t hdmi_core_clock = pll / 4; /* 200 Mhz */ + const sc_pm_clock_rate_t hdmi_bus_clock = pll / 8; /* 100 Mhz */ + + SC_PM_SET_RESOURCE_POWER_MODE(-1, + SC_R_HDMI_PLL_0, SC_PM_PW_MODE_OFF); + SC_PM_SET_CLOCK_RATE(-1, + SC_R_HDMI_PLL_0, SC_PM_CLK_PLL, pll); + SC_PM_SET_RESOURCE_POWER_MODE(-1, + SC_R_HDMI_PLL_0, SC_PM_PW_MODE_ON); + + /* HDMI DI Bus Clock */ + SC_PM_SET_CLOCK_RATE(-1, + SC_R_HDMI, SC_PM_CLK_MISC4, hdmi_bus_clock); + /* HDMI DI Core Clock */ + SC_PM_SET_CLOCK_RATE(-1, + SC_R_HDMI, SC_PM_CLK_MISC2, hdmi_core_clock); +} + +static void display_enable_clocks(int enable) +{ + SC_PM_CLOCK_ENABLE(-1, SC_R_HDMI_PLL_0, SC_PM_CLK_PLL, enable); + SC_PM_CLOCK_ENABLE(-1, SC_R_HDMI, SC_PM_CLK_MISC2, enable); + SC_PM_CLOCK_ENABLE(-1, SC_R_HDMI, SC_PM_CLK_MISC4, enable); + if (enable == OFF) + SC_PM_SET_RESOURCE_POWER_MODE(-1, + SC_R_HDMI_PLL_0, SC_PM_PW_MODE_OFF); +} + +int do_hdp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc < 2) + return 0; + + if (strncmp(argv[1], "tracescfw", 9) == 0) { + g_debug_scfw = 1; + printf("Enabled SCFW API tracing\n"); + } else if (strncmp(argv[1], "load", 4) == 0) { + unsigned long address = 0; + unsigned long offset = 0x2000; + const int iram_size = 0x10000; + const int dram_size = 0x8000; + const char *s; + + if (argc > 2) { + address = simple_strtoul(argv[2], NULL, 0); + if (argc > 3) + offset = simple_strtoul(argv[3], NULL, 0); + } else { + printf("Missing address\n"); + } + + printf("Loading hdp firmware from 0x%016lx offset 0x%016lx\n", + address, offset); + display_set_power(SC_PM_PW_MODE_ON); + display_set_clocks(); + display_enable_clocks(ON); + cdn_api_loadfirmware((unsigned char *)(address + offset), + iram_size, + (unsigned char *)(address + offset + + iram_size), + dram_size); + + s = env_get("hdp_authenticate_fw"); + if (s && !strcmp(s, "yes")) + SC_MISC_AUTH(-1, SC_SECO_AUTH_HDMI_TX_FW, 0); + + display_enable_clocks(OFF); + printf("Loading hdp firmware Complete\n"); + + /* do not turn off hdmi power or firmware load will be lost */ + } else { + printf("test error argc %d\n", argc); + } + + return 0; +} + +/***************************************************/ +U_BOOT_CMD( + hdp, CONFIG_SYS_MAXARGS, 1, do_hdp, + "load hdmi firmware ", + "[<command>] ...\n" + "hdpload [address] [<offset>]\n" + " address - address where the binary image starts\n" + " <offset> - IRAM offset in the binary image (8192 default)\n" + "\n" + " if \"hdp_authenticate_fw\" is set to \"yes\", the seco\n" + " will authenticate the firmware and load HDCP keys.\n" + "\n" + "tracescfw - Trace SCFW API calls for video commands\n" + ); diff --git a/drivers/video/nxp/imx/hdmi/hdprx_load.c b/drivers/video/nxp/imx/hdmi/hdprx_load.c new file mode 100644 index 00000000000..a1b332ff467 --- /dev/null +++ b/drivers/video/nxp/imx/hdmi/hdprx_load.c @@ -0,0 +1,83 @@ +/* + * Copyright 2018 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <asm/global_data.h> + +#include "API_General.h" +#include "scfw_utils.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define ON 1 +#define OFF 0 + +static void hdmi_rx_set_power(int onoff) +{ + SC_PM_SET_RESOURCE_POWER_MODE(-1, SC_R_ISI_CH0, onoff); + SC_PM_SET_RESOURCE_POWER_MODE(-1, SC_R_HDMI_RX, onoff); + SC_PM_SET_RESOURCE_POWER_MODE(-1, SC_R_HDMI_RX_BYPASS, onoff); +} + +int do_hdprx(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + if (argc < 2) + return 0; + + if (strncmp(argv[1], "tracescfw", 9) == 0) { + g_debug_scfw = 1; + printf("Enabled SCFW API tracing\n"); + } else if (strncmp(argv[1], "load", 4) == 0) { + unsigned long address = 0; + unsigned long offset = 0x2000; + const int iram_size = 0x10000; + const int dram_size = 0x8000; + const char *s; + + if (argc > 2) { + address = simple_strtoul(argv[2], NULL, 0); + if (argc > 3) + offset = simple_strtoul(argv[3], NULL, 0); + } else { + printf("Missing address\n"); + } + + printf("Loading hdprx firmware from 0x%016lx offset 0x%016lx\n", + address, offset); + hdmi_rx_set_power(SC_PM_PW_MODE_ON); + hdp_rx_loadfirmware((unsigned char *)(address + offset), + iram_size, + (unsigned char *)(address + offset + + iram_size), + dram_size); + + s = env_get("hdprx_authenticate_fw"); + if (s && !strcmp(s, "yes")) + SC_MISC_AUTH(-1, SC_SECO_AUTH_HDMI_RX_FW, 0); + printf("Loading hdp rx firmware Complete\n"); + /* do not turn off hdmi power or firmware load will be lost */ + } else { + printf("test error argc %d\n", argc); + } + + return 0; +} + +/***************************************************/ +U_BOOT_CMD( + hdprx, CONFIG_SYS_MAXARGS, 1, do_hdprx, + "load hdmi rx firmware ", + "[<command>] ...\n" + "hdpload [address] [<offset>]\n" + " address - address where the binary image starts\n" + " <offset> - IRAM offset in the binary image (8192 default)\n" + "\n" + " if \"hdprx_authenticate_fw\" is set to \"yes\", the seco\n" + " will authenticate the firmware and load HDCP keys.\n" + "\n" + "tracescfw - Trace SCFW API calls for video commands\n" + ); diff --git a/drivers/video/nxp/imx/hdmi/imx8m_hdmi.c b/drivers/video/nxp/imx/hdmi/imx8m_hdmi.c new file mode 100644 index 00000000000..fb069d2ed57 --- /dev/null +++ b/drivers/video/nxp/imx/hdmi/imx8m_hdmi.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <display.h> +#include <video.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <linux/iopoll.h> +#include <clk.h> +#include <video_link.h> + +#include "API_General.h" +#include "vic_table.h" +#include "API_HDMITX.h" +#include "apb_cfg.h" +#include "externs.h" +#include "API_AVI.h" +#include "address.h" +#include "source_car.h" +#include "source_phy.h" +#include "API_AFE.h" +#include "source_vif.h" +#include "general_handler.h" +#include "mhl_hdtx_top.h" +#include "API_AFE_t28hpc_hdmitx.h" + +struct imx8m_hdmi_priv { + fdt_addr_t base; + struct display_timing timings; + int vic; + bool hpol; + bool vpol; +}; + +static int imx8m_hdmi_set_vic_mode(int vic, + struct imx8m_hdmi_priv *priv) +{ + uint32_t pixel_clock_kHz; + uint32_t frame_rate_Hz; + uint32_t frame_rate_frac_Hz; + uint32_t cea_vic; + char iflag; + + if (vic >= VIC_MODE_COUNT) { + debug("%s(): unsupported VIC\n", __func__); + return -1; + } + + priv->timings.hfront_porch.typ = vic_table[vic][FRONT_PORCH]; + priv->timings.hback_porch.typ = vic_table[vic][BACK_PORCH]; + priv->timings.hsync_len.typ = vic_table[vic][HSYNC]; + priv->timings.vfront_porch.typ = vic_table[vic][TYPE_EOF]; + priv->timings.vback_porch.typ = vic_table[vic][SOF]; + priv->timings.vsync_len.typ = vic_table[vic][VSYNC]; + priv->timings.hactive.typ = vic_table[vic][H_ACTIVE]; + priv->timings.vactive.typ = vic_table[vic][V_ACTIVE]; + + priv->hpol = vic_table[vic][HSYNC_POL] != 0; + priv->vpol = vic_table[vic][VSYNC_POL] != 0; + + cea_vic = vic_table[vic][VIC]; + if (vic_table[vic][I_P] != 0) + iflag = 'i'; + else + iflag = 'p'; + pixel_clock_kHz = vic_table[vic][PIXEL_FREQ_KHZ]; + frame_rate_Hz = vic_table[vic][V_FREQ_HZ] * 1000; + frame_rate_frac_Hz = frame_rate_Hz % 1000; + frame_rate_Hz /= 1000; + + priv->timings.pixelclock.typ = pixel_clock_kHz * 1000; + + debug("Cadence VIC %3d, CEA VIC %3d: %4d x %4d %c @ %3d.%03d [%6d kHz] Vpol=%d Hpol=%d\n", + vic, cea_vic, priv->timings.hactive.typ, priv->timings.vactive.typ, iflag, frame_rate_Hz, + frame_rate_frac_Hz, pixel_clock_kHz, priv->vpol, priv->hpol); + + debug(" mode timing fp sync bp h:%3d %3d %3d v:%3d %3d %3d\n", + priv->timings.hfront_porch.typ, priv->timings.hsync_len.typ, priv->timings.hback_porch.typ, + priv->timings.vfront_porch.typ, priv->timings.vsync_len.typ, priv->timings.vback_porch.typ); + + return 0; +} + +static int imx8m_hdmi_init(int vic, + int encoding, + int color_depth, + bool pixel_clk_from_phy) +{ + int ret; + uint32_t character_freq_khz; + + uint8_t echo_msg[] = "echo test"; + uint8_t echo_resp[sizeof(echo_msg) + 1]; + + /*================================================================== */ + /* Parameterization: */ + /*================================================================== */ + + /* VIC Mode - index from vic_table (see API_SRC/vic_table.c) */ + VIC_MODES vic_mode = vic; + + /* Pixel Encodeing Format */ + /* PXL_RGB = 0x1, */ + /* YCBCR_4_4_4 = 0x2, */ + /* YCBCR_4_2_2 = 0x4, */ + /* YCBCR_4_2_0 = 0x8, */ + /* Y_ONLY = 0x10, */ + VIC_PXL_ENCODING_FORMAT format = encoding; + /*VIC_PXL_ENCODING_FORMAT format = 1; */ + + /* B/W Balance Type: 0 no data, 1 IT601, 2 ITU709 */ + BT_TYPE bw_type = 0; + + /* bpp (bits per subpixel) - 8 24bpp, 10 30bpp, 12 36bpp, 16 48bpp */ + uint8_t bps = color_depth; + + /* Set HDMI TX Mode */ + /* Mode = 0 - DVI, 1 - HDMI1.4, 2 HDMI 2.0 */ + HDMI_TX_MAIL_HANDLER_PROTOCOL_TYPE ptype = 1; + + if (vic_mode == VIC_MODE_97_60Hz) + ptype = 2; + + /*================================================================== */ + /* Parameterization done */ + /*================================================================== */ + cdn_api_init(); + debug("CDN_API_Init completed\n"); + + ret = cdn_api_checkalive(); + debug("CDN_API_CheckAlive returned ret = %d\n", ret); + + if (ret) + return -EPERM; + + ret = cdn_api_general_test_echo_ext_blocking(echo_msg, + echo_resp, + sizeof(echo_msg), + CDN_BUS_TYPE_APB); + debug("_General_Test_Echo_Ext_blocking - (ret = %d echo_resp = %s)\n", + ret, echo_resp); + + /* Configure PHY */ + character_freq_khz = phy_cfg_t28hpc(4, vic_mode, bps, + format, pixel_clk_from_phy); + debug("phy_cfg_t28hpc (character_freq_mhz = %d)\n", + character_freq_khz); + + hdmi_tx_t28hpc_power_config_seq(4); + + /* Set the lane swapping */ + ret = cdn_api_general_write_register_blocking + (ADDR_SOURCD_PHY + (LANES_CONFIG << 2), + F_SOURCE_PHY_LANE0_SWAP(0) | F_SOURCE_PHY_LANE1_SWAP(1) | + F_SOURCE_PHY_LANE2_SWAP(2) | F_SOURCE_PHY_LANE3_SWAP(3) | + F_SOURCE_PHY_COMB_BYPASS(0) | F_SOURCE_PHY_20_10(1)); + + debug("_General_Write_Register_blocking LANES_CONFIG ret = %d\n", ret); + + ret = CDN_API_HDMITX_Init_blocking(); + debug("CDN_API_STATUS CDN_API_HDMITX_Init_blocking ret = %d\n", ret); + + ret = CDN_API_HDMITX_Init_blocking(); + debug("CDN_API_STATUS CDN_API_HDMITX_Init_blocking ret = %d\n", ret); + + ret = CDN_API_HDMITX_Set_Mode_blocking(ptype, character_freq_khz); + debug("CDN_API_HDMITX_Set_Mode_blocking ret = %d\n", ret); + + ret = cdn_api_set_avi(vic_mode, format, bw_type); + debug("cdn_api_set_avi ret = %d\n", ret); + + ret = CDN_API_HDMITX_SetVic_blocking(vic_mode, bps, format); + debug("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); + + + udelay(20000); + + return 0; +} + +static int imx8m_hdmi_update_timings(struct udevice *dev) +{ + struct imx8m_hdmi_priv *priv = dev_get_priv(dev); + + /* map the resolution to a VIC index in the vic table*/ + if ((priv->timings.hactive.typ == 1280) && (priv->timings.vactive.typ == 720)) + priv->vic = 1; /* 720p60 */ + else if ((priv->timings.hactive.typ == 1920) && (priv->timings.vactive.typ == 1080)) + priv->vic = 2; /* 1080p60 */ + else if ((priv->timings.hactive.typ == 3840) && (priv->timings.vactive.typ == 2160)) + priv->vic = 3; /* 2160p60 */ + else + priv->vic = 0; /* 480p60 */ + + return imx8m_hdmi_set_vic_mode(priv->vic, priv); +} + +static void imx8m_hdmi_disable(void) +{ + int ret; + GENERAL_READ_REGISTER_RESPONSE resp; + + resp.val = 0; + ret = cdn_api_general_read_register_blocking(ADDR_SOURCE_MHL_HD + + (HDTX_CONTROLLER << 2), + &resp); + if (ret != CDN_OK) { + printf("%s(): dn_api_general_read_register_blocking failed\n", + __func__); + } + + resp.val &= ~F_DATA_EN(1); /* disable HDMI */ + + ret = cdn_api_general_write_register_blocking(ADDR_SOURCE_MHL_HD + + (HDTX_CONTROLLER << 2), + resp.val); + if (ret != CDN_OK) { + printf("%s(): dn_api_general_write_register_blocking failed\n", + __func__); + return; + } +} + +static int imx8m_hdmi_read_timing(struct udevice *dev, struct display_timing *timing) +{ + struct imx8m_hdmi_priv *priv = dev_get_priv(dev); + + if (timing) { + memcpy(timing, &priv->timings, sizeof(struct display_timing)); + + if (priv->hpol) + timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH; + + if (priv->vpol) + timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH; + + return 0; + } + + return -EINVAL; +} + +static int imx8m_hdmi_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct imx8m_hdmi_priv *priv = dev_get_priv(dev); + int ret; + + ret = imx8m_hdmi_init(priv->vic, 1, 8, true); + if (ret) { + printf("HDMI enable failed, ret %d!\n", ret); + return ret; + } + + return 0; +} + +static int imx8m_hdmi_probe(struct udevice *dev) +{ + struct imx8m_hdmi_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + ret = video_link_get_display_timings(&priv->timings); + if (ret) { + printf("decode display timing error %d\n", ret); + return ret; + } + + imx8m_hdmi_update_timings(dev); + + return 0; +} + +static int imx8m_hdmi_remove(struct udevice *dev) +{ + imx8m_hdmi_disable(); + + return 0; +} + +struct dm_display_ops imx8m_hdmi_ops = { + .read_timing = imx8m_hdmi_read_timing, + .enable = imx8m_hdmi_enable, +}; + +static const struct udevice_id imx8m_hdmi_ids[] = { + { .compatible = "fsl,imx8mq-hdmi" }, + { } +}; + +U_BOOT_DRIVER( imx8m_hdmi) = { + .name = " imx8m_hdmi", + .id = UCLASS_DISPLAY, + .of_match = imx8m_hdmi_ids, + .bind = dm_scan_fdt_dev, + .probe = imx8m_hdmi_probe, + .remove = imx8m_hdmi_remove, + .ops = & imx8m_hdmi_ops, + .priv_auto = sizeof(struct imx8m_hdmi_priv), +}; diff --git a/drivers/video/nxp/imx/hdmi/scfw_utils.h b/drivers/video/nxp/imx/hdmi/scfw_utils.h new file mode 100644 index 00000000000..bf11872ab17 --- /dev/null +++ b/drivers/video/nxp/imx/hdmi/scfw_utils.h @@ -0,0 +1,102 @@ +/* + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _SCFW_UTILS_H_ +#define _SCFW_UTILS_H_ + +#include <common.h> +#include <asm/arch/sci/sci.h> + +static int g_debug_scfw; /* set to one to turn on SCFW API tracing */ + +#define SC_PM_SET_CLOCK_PARENT(__ipcHndl__, __res__, __clk__, __parent__) \ +do { \ + char _res_str[] = #__res__;\ + char _clk_str[] = #__clk__;\ + sc_err_t _ret;\ + if (g_debug_scfw) \ + printf("(%4d) sc_pm_set_clock_parent %s:%s -> %d\n",\ + __LINE__, _res_str, _clk_str, __parent__);\ + _ret = sc_pm_set_clock_parent(__ipcHndl__,\ + __res__, __clk__, __parent__);\ + if (_ret != SC_ERR_NONE) \ + printf("(%d)>> sc_pm_set_clock_parent failed! %s:%s -> %d (error = %d)\n",\ + __LINE__, _res_str, _clk_str, __parent__, _ret);\ +} while (0) + +#define SC_PM_SET_CLOCK_RATE(__ipcHndl__, __res__, __clk__, __rate__) \ +do { \ + char _res_str[] = #__res__;\ + char _clk_str[] = #__clk__;\ + sc_err_t _ret;\ + sc_pm_clock_rate_t _actual = __rate__;\ + if (g_debug_scfw) \ + printf("(%4d) sc_pm_set_clock_rate %s:%s -> %d\n",\ + __LINE__, _res_str, _clk_str, __rate__);\ + _ret = sc_pm_set_clock_rate(__ipcHndl__, __res__, __clk__, &_actual);\ + if (_ret != SC_ERR_NONE)\ + printf("(%4d)>> sc_pm_set_clock_rate failed! %s:%s -> %d (error = %d)\n",\ + __LINE__, _res_str, _clk_str, __rate__, _ret);\ + if (_actual != __rate__)\ + printf("(%4d)>> Actual rate for %s:%s is %d instead of %d\n", \ + __LINE__, _res_str, _clk_str, _actual, __rate__); \ +} while (0) + +#define SC_PM_CLOCK_ENABLE(__ipcHndl__, __res__, __clk__, __enable__) \ +do { \ + char _res_str[] = #__res__;\ + char _clk_str[] = #__clk__;\ + sc_err_t _ret;\ + if (g_debug_scfw) \ + printf("(%4d) sc_pm_clock_enable %s:%s -> %d\n",\ + __LINE__, _res_str, _clk_str, __enable__);\ + _ret = sc_pm_clock_enable(__ipcHndl__,\ + __res__, __clk__, __enable__, false);\ + if (_ret != SC_ERR_NONE)\ + printf("(%4d)>> sc_pm_clock_enable failed! %s:%s -> %d (error = %d)\n",\ + __LINE__, _res_str, _clk_str, __enable__, _ret);\ +} while (0) \ + +#define SC_MISC_SET_CONTROL(__ipcHndl__, __res__, __clk__, __value__) \ +do { \ + char _res_str[] = #__res__; \ + char _clk_str[] = #__clk__; \ + sc_err_t _ret; \ + if (g_debug_scfw) \ + printf("(%4d) sc_misc_set_control %s:%s -> %d\n",\ + __LINE__, _res_str, _clk_str, __value__);\ + _ret = sc_misc_set_control(__ipcHndl__, \ + __res__, __clk__, __value__); \ + if (_ret != SC_ERR_NONE) \ + printf("(%4d)>> sc_misc_set_control failed! %s:%s -> %d (error = %d)\n", \ + __LINE__, _res_str, _clk_str, __value__, _ret); \ +} while (0) + +#define SC_PM_SET_RESOURCE_POWER_MODE(__ipcHndl__, __res__, __enable__) \ +do { \ + char _res_str[] = #__res__; \ + sc_err_t _ret; \ + if (g_debug_scfw) \ + printf("(%4d) sc_pm_set_resource_power_mode %s -> %d\n",\ + __LINE__, _res_str, __enable__);\ + _ret = sc_pm_set_resource_power_mode(__ipcHndl__, __res__, __enable__);\ + if (_ret != SC_ERR_NONE) \ + printf("(%4d)>> sc_pm_set_resource_power_mode failed! %s -> %d (error = %d)\n", \ + __LINE__, _res_str, __enable__, _ret);\ +} while (0) + +#define SC_MISC_AUTH(__ipcHndl__, __cmd__, __addr__) \ +do { \ + sc_err_t _ret; \ + if (g_debug_scfw) \ + printf("(%4d) sc_misc_seco_authenticate -> cmd %d addr %d\n",\ + __LINE__, __cmd__, __addr__);\ + _ret = sc_seco_authenticate(__ipcHndl__, __cmd__, __addr__); \ + if (_ret != SC_ERR_NONE) \ + printf("(%4d)>> sc_misc_seco_authenticate cmd %d addr %d (error = %d)\n", \ + __LINE__, __cmd__, __addr__, _ret); \ +} while (0) + +#endif /*_SCFW_UTILS_H_ */ diff --git a/drivers/video/nxp/imx/imx8_dc.c b/drivers/video/nxp/imx/imx8_dc.c new file mode 100644 index 00000000000..9da34c6f3d2 --- /dev/null +++ b/drivers/video/nxp/imx/imx8_dc.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + */ + +#include <common.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <env.h> +#include <linux/err.h> +#include <malloc.h> +#include <video.h> +#include <video_fb.h> +#include <display.h> + +#include <asm/cache.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <panel.h> +#include <video_bridge.h> +#include <video_link.h> +#include <clk.h> + +#include <asm/arch/sci/sci.h> +#include <imxdpuv1.h> +#include <imxdpuv1_registers.h> +#include <imxdpuv1_events.h> +#include <power-domain.h> +#include <asm/arch/lpcg.h> + +#define FLAG_COMBO BIT(1) + +struct imx8_dc_priv { + /*struct udevice *bridge;*/ + struct udevice *panel; + struct udevice *disp_dev; + struct imxdpuv1_videomode mode; + + u32 gpixfmt; + u32 dpu_id; + u32 disp_id; +}; + +static int imx8_dc_soc_setup(struct udevice *dev, sc_pm_clock_rate_t pixel_clock) +{ + sc_err_t err; + sc_rsrc_t dc_rsrc, pll0_rsrc, pll1_rsrc; + sc_pm_clock_rate_t pll_clk; + const char *pll1_pd_name; + u32 dc_lpcg; + struct imx8_dc_priv *priv = dev_get_priv(dev); + + int dc_id = priv->dpu_id; + + struct power_domain pd; + int ret; + + debug("%s, dc_id %d\n", __func__, dc_id); + + if (dc_id == 0) { + dc_rsrc = SC_R_DC_0; + pll0_rsrc = SC_R_DC_0_PLL_0; + pll1_rsrc = SC_R_DC_0_PLL_1; + pll1_pd_name = "dc0_pll1"; + dc_lpcg = DC_0_LPCG; + } else { + dc_rsrc = SC_R_DC_1; + pll0_rsrc = SC_R_DC_1_PLL_0; + pll1_rsrc = SC_R_DC_1_PLL_1; + pll1_pd_name = "dc1_pll1"; + dc_lpcg = DC_1_LPCG; + } + + if (!power_domain_lookup_name(pll1_pd_name, &pd)) { + ret = power_domain_on(&pd); + if (ret) { + printf("%s Power up failed! (error = %d)\n", pll1_pd_name, ret); + return -EIO; + } + } else { + printf("%s lookup failed!\n", pll1_pd_name); + return -EIO; + } + + /* Setup the pll1/2 and DISP0/1 clock */ + if (pixel_clock >= 40000000) + pll_clk = 1188000000; + else + pll_clk = 675000000; + + err = sc_pm_set_clock_rate(-1, pll0_rsrc, SC_PM_CLK_PLL, &pll_clk); + if (err != SC_ERR_NONE) { + printf("PLL0 set clock rate failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_rate(-1, pll1_rsrc, SC_PM_CLK_PLL, &pll_clk); + if (err != SC_ERR_NONE) { + printf("PLL1 set clock rate failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_parent(-1, dc_rsrc, SC_PM_CLK_MISC0, 2); + if (err != SC_ERR_NONE) { + printf("DISP0 set clock parent failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_parent(-1, dc_rsrc, SC_PM_CLK_MISC1, 3); + if (err != SC_ERR_NONE) { + printf("DISP0 set clock parent failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_rate(-1, dc_rsrc, SC_PM_CLK_MISC0, &pixel_clock); + if (err != SC_ERR_NONE) { + printf("DISP0 set clock rate failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_rate(-1, dc_rsrc, SC_PM_CLK_MISC1, &pixel_clock); + if (err != SC_ERR_NONE) { + printf("DISP1 set clock rate failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_clock_enable(-1, pll0_rsrc, SC_PM_CLK_PLL, true, false); + if (err != SC_ERR_NONE) { + printf("PLL0 clock enable failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_clock_enable(-1, pll1_rsrc, SC_PM_CLK_PLL, true, false); + if (err != SC_ERR_NONE) { + printf("PLL1 clock enable failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_clock_enable(-1, dc_rsrc, SC_PM_CLK_MISC0, true, false); + if (err != SC_ERR_NONE) { + printf("DISP0 clock enable failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_clock_enable(-1, dc_rsrc, SC_PM_CLK_MISC1, true, false); + if (err != SC_ERR_NONE) { + printf("DISP1 clock enable failed! (error = %d)\n", err); + return -EIO; + } + + lpcg_all_clock_on(dc_lpcg); + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_PXL_LINK_MST1_ADDR, 0); + if (err != SC_ERR_NONE) { + printf("DC Set control fSC_C_PXL_LINK_MST1_ADDR ailed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_PXL_LINK_MST1_ENB, 1); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_PXL_LINK_MST1_ENB failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_PXL_LINK_MST1_VLD, 1); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_PXL_LINK_MST1_VLD failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_PXL_LINK_MST2_ADDR, 0); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_PXL_LINK_MST2_ADDR ailed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_PXL_LINK_MST2_ENB, 1); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_PXL_LINK_MST2_ENB failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_PXL_LINK_MST2_VLD, 1); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_PXL_LINK_MST2_VLD failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_SYNC_CTRL0, 1); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_SYNC_CTRL0 failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, dc_rsrc, SC_C_SYNC_CTRL1, 1); + if (err != SC_ERR_NONE) { + printf("DC Set control SC_C_SYNC_CTRL1 failed! (error = %d)\n", err); + return -EIO; + } + + return 0; +} + +static int imx8_dc_video_init(struct udevice *dev) +{ + imxdpuv1_channel_params_t channel; + imxdpuv1_layer_t layer; + struct imx8_dc_priv *priv = dev_get_priv(dev); + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + int8_t imxdpuv1_id = priv->dpu_id; + + debug("%s\n", __func__); + + if (imxdpuv1_id != 0 || (imxdpuv1_id == 1 && !is_imx8qm())) { + printf("%s(): invalid imxdpuv1_id %d", __func__, imxdpuv1_id); + return -ENODEV; + } + + imxdpuv1_init(imxdpuv1_id); + imxdpuv1_disp_enable_frame_gen(imxdpuv1_id, 0, IMXDPUV1_FALSE); + imxdpuv1_disp_enable_frame_gen(imxdpuv1_id, 1, IMXDPUV1_FALSE); + + imxdpuv1_disp_setup_frame_gen(imxdpuv1_id, priv->disp_id, + (const struct imxdpuv1_videomode *)&priv->mode, + 0x3ff, 0, 0, 1, IMXDPUV1_DISABLE); + imxdpuv1_disp_init(imxdpuv1_id, priv->disp_id); + imxdpuv1_disp_setup_constframe(imxdpuv1_id, + priv->disp_id, 0, 0, 0xff, 0); /* blue */ + + if (priv->disp_id == 0) + channel.common.chan = IMXDPUV1_CHAN_VIDEO_0; + else + channel.common.chan = IMXDPUV1_CHAN_VIDEO_1; + channel.common.src_pixel_fmt = priv->gpixfmt; + channel.common.dest_pixel_fmt = priv->gpixfmt; + channel.common.src_width = priv->mode.hlen; + channel.common.src_height = priv->mode.vlen; + + channel.common.clip_width = 0; + channel.common.clip_height = 0; + channel.common.clip_top = 0; + channel.common.clip_left = 0; + + channel.common.dest_width = priv->mode.hlen; + channel.common.dest_height = priv->mode.vlen; + channel.common.dest_top = 0; + channel.common.dest_left = 0; + channel.common.stride = + priv->mode.hlen * imxdpuv1_bytes_per_pixel(IMXDPUV1_PIX_FMT_BGRA32); + channel.common.disp_id = priv->disp_id; + channel.common.const_color = 0; + channel.common.use_global_alpha = 0; + channel.common.use_local_alpha = 0; + imxdpuv1_init_channel(imxdpuv1_id, &channel); + + imxdpuv1_init_channel_buffer(imxdpuv1_id, + channel.common.chan, + priv->mode.hlen * imxdpuv1_bytes_per_pixel(IMXDPUV1_PIX_FMT_RGB32), + IMXDPUV1_ROTATE_NONE, + (dma_addr_t)plat->base, + 0, + 0); + + layer.enable = IMXDPUV1_TRUE; + layer.secondary = get_channel_blk(channel.common.chan); + + if (priv->disp_id == 0) { + layer.stream = IMXDPUV1_DISPLAY_STREAM_0; + layer.primary = IMXDPUV1_ID_CONSTFRAME0; + } else { + layer.stream = IMXDPUV1_DISPLAY_STREAM_1; + layer.primary = IMXDPUV1_ID_CONSTFRAME1; + } + + imxdpuv1_disp_setup_layer( + imxdpuv1_id, &layer, IMXDPUV1_LAYER_0, 1); + imxdpuv1_disp_set_layer_global_alpha( + imxdpuv1_id, IMXDPUV1_LAYER_0, 0xff); + + imxdpuv1_disp_set_layer_position( + imxdpuv1_id, IMXDPUV1_LAYER_0, 0, 0); + imxdpuv1_disp_set_chan_position( + imxdpuv1_id, channel.common.chan, 0, 0); + + imxdpuv1_disp_enable_frame_gen(imxdpuv1_id, priv->disp_id, IMXDPUV1_ENABLE); + + debug("IMXDPU display start ...\n"); + + return 0; +} + +static int imx8_dc_get_timings_from_display(struct udevice *dev, + struct display_timing *timings) +{ + struct imx8_dc_priv *priv = dev_get_priv(dev); + int err; + + priv->disp_dev = video_link_get_next_device(dev); + if (!priv->disp_dev || + device_get_uclass_id(priv->disp_dev) != UCLASS_DISPLAY) { + + printf("fail to find display device\n"); + return -ENODEV; + } + + debug("disp_dev %s\n", priv->disp_dev->name); + + err = video_link_get_display_timings(timings); + if (err) + return err; + + return 0; +} + +static int imx8_dc_probe(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct imx8_dc_priv *priv = dev_get_priv(dev); + ulong flag = dev_get_driver_data(dev); + + struct display_timing timings; + u32 fb_start, fb_end; + int ret; + + debug("%s() plat: base 0x%lx, size 0x%x\n", + __func__, plat->base, plat->size); + + priv->dpu_id = dev_seq(dev); + + ret = imx8_dc_get_timings_from_display(dev, &timings); + if (ret) + return ret; + + priv->mode.pixelclock = timings.pixelclock.typ; + priv->mode.hlen = timings.hactive.typ; + priv->mode.hbp = timings.hback_porch.typ; + priv->mode.hfp = timings.hfront_porch.typ; + + priv->mode.vlen = timings.vactive.typ; + priv->mode.vbp = timings.vback_porch.typ; + priv->mode.vfp = timings.vfront_porch.typ; + + priv->mode.hsync = timings.hsync_len.typ; + priv->mode.vsync = timings.vsync_len.typ; + priv->mode.flags = IMXDPUV1_MODE_FLAGS_HSYNC_POL | IMXDPUV1_MODE_FLAGS_VSYNC_POL | IMXDPUV1_MODE_FLAGS_DE_POL; + + priv->gpixfmt = IMXDPUV1_PIX_FMT_BGRA32; + + imx8_dc_soc_setup(dev, priv->mode.pixelclock); + + if (flag & FLAG_COMBO) /* QXP has one DC which contains 2 LVDS/MIPI_DSI combo */ + priv->disp_id = dev_seq(priv->disp_dev->parent); + else + priv->disp_id = 1; /* QM has two DCs each contains one LVDS as secondary display output */ + + debug("dpu %u, disp_id %u, pixelclock %u, hlen %u, vlen %u\n", + priv->dpu_id, priv->disp_id, priv->mode.pixelclock, priv->mode.hlen, priv->mode.vlen); + + + display_enable(priv->disp_dev, 32, NULL); + + + ret = imx8_dc_video_init(dev); + if (ret) { + dev_err(dev, "imx8_dc_video_init fail %d\n", ret); + return ret; + } + + uc_priv->bpix = VIDEO_BPP32; + uc_priv->xsize = priv->mode.hlen; + uc_priv->ysize = priv->mode.vlen; + + /* 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 ret; +} + +static int imx8_dc_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + /* Max size supported by LCDIF, because in bind, we can't probe panel */ + plat->size = 1920 * 1080 *4; + + return 0; +} + +static int imx8_dc_remove(struct udevice *dev) +{ + struct imx8_dc_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + + imxdpuv1_disp_enable_frame_gen(priv->dpu_id, + priv->disp_id, IMXDPUV1_DISABLE); + + return 0; +} + +static const struct udevice_id imx8_dc_ids[] = { + { .compatible = "fsl,imx8qm-dpu" }, + { .compatible = "fsl,imx8qxp-dpu", .data = FLAG_COMBO, }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(imx8_dc) = { + .name = "imx8_dc", + .id = UCLASS_VIDEO, + .of_match = imx8_dc_ids, + .bind = imx8_dc_bind, + .probe = imx8_dc_probe, + .remove = imx8_dc_remove, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto = sizeof(struct imx8_dc_priv), +}; diff --git a/drivers/video/nxp/imx/imx8_lvds.c b/drivers/video/nxp/imx/imx8_lvds.c new file mode 100644 index 00000000000..076e3eb82e9 --- /dev/null +++ b/drivers/video/nxp/imx/imx8_lvds.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <display.h> +#include <video.h> +#include <video_bridge.h> +#include <video_link.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <linux/iopoll.h> +#include <linux/err.h> +#include <clk.h> + +#include <asm/arch/imx8_lvds.h> +#include <asm/arch/imx8_mipi_dsi.h> +#include <power-domain.h> +#include <asm/arch/lpcg.h> +#include <asm/arch/sci/sci.h> +#include <regmap.h> +#include <syscon.h> + +#define FLAG_COMBO BIT(1) + +#define LDB_PHY_OFFSET 0x1000 +#define MIPI_PHY_OFFSET 0x8000 + +struct imx8_ldb_priv { + struct regmap *gpr; + struct udevice *conn_dev; + u32 ldb_id; + struct display_timing timings; +}; + +static int imx8_ldb_soc_setup(struct udevice *dev, sc_pm_clock_rate_t pixel_clock) +{ + sc_err_t err; + sc_rsrc_t lvds_rsrc, mipi_rsrc; + const char *pd_name; + struct imx8_ldb_priv *priv = dev_get_priv(dev); + ulong flag = dev_get_driver_data(dev); + int lvds_id = priv->ldb_id; + + struct power_domain pd; + int ret; + + debug("%s\n", __func__); + + if (lvds_id == 0) { + lvds_rsrc = SC_R_LVDS_0; + mipi_rsrc = SC_R_MIPI_0; + pd_name = "lvds0_power_domain"; + } else { + lvds_rsrc = SC_R_LVDS_1; + mipi_rsrc = SC_R_MIPI_1; + pd_name = "lvds1_power_domain"; + } + /* Power up LVDS */ + if (!power_domain_lookup_name(pd_name, &pd)) { + ret = power_domain_on(&pd); + if (ret) { + printf("%s Power up failed! (error = %d)\n", pd_name, ret); + return -EIO; + } + } else { + printf("%s lookup failed!\n", pd_name); + return -EIO; + } + + /* Setup clocks */ + err = sc_pm_set_clock_rate(-1, lvds_rsrc, SC_PM_CLK_BYPASS, &pixel_clock); + if (err != SC_ERR_NONE) { + printf("LVDS set rate SC_PM_CLK_BYPASS failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_rate(-1, lvds_rsrc, SC_PM_CLK_PER, &pixel_clock); + if (err != SC_ERR_NONE) { + printf("LVDS set rate SC_PM_CLK_BYPASS failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_set_clock_rate(-1, lvds_rsrc, SC_PM_CLK_PHY, &pixel_clock); + if (err != SC_ERR_NONE) { + printf("LVDS set rate SC_PM_CLK_BYPASS failed! (error = %d)\n", err); + return -EIO; + } + + if (flag & FLAG_COMBO) { + /* For QXP, there is only one DC, and two pixel links to each LVDS with a mux provided. + * We connect LVDS0 to pixel link 0, lVDS1 to pixel link 1 from DC + */ + + /* Configure to LVDS mode not MIPI DSI */ + err = sc_misc_set_control(-1, mipi_rsrc, SC_C_MODE, 1); + if (err != SC_ERR_NONE) { + printf("LVDS sc_misc_set_control SC_C_MODE failed! (error = %d)\n", err); + return -EIO; + } + + /* Configure to LVDS mode with single channel */ + err = sc_misc_set_control(-1, mipi_rsrc, SC_C_DUAL_MODE, 0); + if (err != SC_ERR_NONE) { + printf("LVDS sc_misc_set_control SC_C_DUAL_MODE failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_misc_set_control(-1, mipi_rsrc, SC_C_PXL_LINK_SEL, lvds_id); + if (err != SC_ERR_NONE) { + printf("LVDS sc_misc_set_control SC_C_PXL_LINK_SEL failed! (error = %d)\n", err); + return -EIO; + } + } + + err = sc_pm_clock_enable(-1, lvds_rsrc, SC_PM_CLK_BYPASS, true, false); + if (err != SC_ERR_NONE) { + printf("LVDS enable clock SC_PM_CLK_BYPASS failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_clock_enable(-1, lvds_rsrc, SC_PM_CLK_PER, true, false); + if (err != SC_ERR_NONE) { + printf("LVDS enable clock SC_PM_CLK_PER failed! (error = %d)\n", err); + return -EIO; + } + + err = sc_pm_clock_enable(-1, lvds_rsrc, SC_PM_CLK_PHY, true, false); + if (err != SC_ERR_NONE) { + printf("LVDS enable clock SC_PM_CLK_PHY failed! (error = %d)\n", err); + return -EIO; + } + + return 0; +} + +void imx8_ldb_configure(struct udevice *dev) +{ + uint32_t mode; + uint32_t phy_setting; + struct imx8_ldb_priv *priv = dev_get_priv(dev); + ulong flag = dev_get_driver_data(dev); + + if (flag & FLAG_COMBO) { + mode = + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_MODE, LVDS_CTRL_CH0_MODE__DI0) | + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_DATA_WIDTH, LVDS_CTRL_CH0_DATA_WIDTH__24BIT) | + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_BIT_MAP, LVDS_CTRL_CH0_BIT_MAP__JEIDA); + + phy_setting = 0x4 << 5 | 0x4 << 2 | 1 << 1 | 0x1; + regmap_write(priv->gpr, LDB_PHY_OFFSET + LVDS_PHY_CTRL, phy_setting); + regmap_write(priv->gpr, LDB_PHY_OFFSET + LVDS_CTRL, mode); + regmap_write(priv->gpr, LDB_PHY_OFFSET + MIPIv2_CSR_TX_ULPS, 0); + regmap_write(priv->gpr, LDB_PHY_OFFSET + MIPIv2_CSR_PXL2DPI, MIPI_CSR_PXL2DPI_24_BIT); + + /* Power up PLL in MIPI DSI PHY */ + regmap_write(priv->gpr, MIPI_PHY_OFFSET + DPHY_PD_PLL, 0); + regmap_write(priv->gpr, MIPI_PHY_OFFSET + DPHY_PD_TX, 0); + } else { + mode = + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_MODE, LVDS_CTRL_CH0_MODE__DI0) | + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_DATA_WIDTH, LVDS_CTRL_CH0_DATA_WIDTH__24BIT) | + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_BIT_MAP, LVDS_CTRL_CH0_BIT_MAP__JEIDA) | + IMX_LVDS_SET_FIELD(LVDS_CTRL_CH0_10BIT_ENABLE, LVDS_CTRL_CH0_10BIT_ENABLE__10BIT) | + IMX_LVDS_SET_FIELD(LVDS_CTRL_DI0_DATA_WIDTH, LVDS_CTRL_DI0_DATA_WIDTH__USE_30BIT); + + regmap_write(priv->gpr, LDB_PHY_OFFSET + LVDS_CTRL, mode); + + phy_setting = + LVDS_PHY_CTRL_RFB_MASK | + LVDS_PHY_CTRL_CH0_EN_MASK | + (0 << LVDS_PHY_CTRL_M_SHIFT) | + (0x04 << LVDS_PHY_CTRL_CCM_SHIFT) | + (0x04 << LVDS_PHY_CTRL_CA_SHIFT); + regmap_write(priv->gpr, LDB_PHY_OFFSET + LVDS_PHY_CTRL, phy_setting); + } +} + +int imx8_ldb_read_timing(struct udevice *dev, struct display_timing *timing) +{ + struct imx8_ldb_priv *priv = dev_get_priv(dev); + + if (dev->plat_ == NULL) + return -EINVAL; + + if (timing) { + memcpy(timing, &priv->timings, sizeof(struct display_timing)); + return 0; + } + + return -EINVAL; +} + +int imx8_ldb_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct imx8_ldb_priv *priv = dev_get_priv(dev); + int ret; + + if (dev->plat_ == NULL) { + imx8_ldb_soc_setup(dev, timing->pixelclock.typ); + imx8_ldb_configure(dev); + } else { + + display_enable(dev->parent, panel_bpp, &priv->timings); + + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { + if (priv->conn_dev && + device_get_uclass_id(priv->conn_dev) == UCLASS_VIDEO_BRIDGE) { + ret = video_bridge_set_backlight(priv->conn_dev, 80); + if (ret) { + dev_err(dev, "fail to set backlight\n"); + return ret; + } + } + } + } + + return 0; +} + +static int imx8_ldb_probe(struct udevice *dev) +{ + struct imx8_ldb_priv *priv = dev_get_priv(dev); + int ret; + + debug("%s\n", __func__); + + if (dev->plat_ == NULL) { + + priv->gpr = syscon_regmap_lookup_by_phandle(dev, "gpr"); + if (IS_ERR(priv->gpr)) { + printf("fail to get gpr regmap\n"); + return PTR_ERR(priv->gpr); + } + + /* Require to add alias in DTB */ + priv->ldb_id = dev_seq(dev); + + debug("ldb_id %u\n", priv->ldb_id); + } else { + priv->conn_dev = video_link_get_next_device(dev); + if (!priv->conn_dev) { + debug("can't find next device in video link\n"); + } + + ret = video_link_get_display_timings(&priv->timings); + if (ret) { + printf("decode display timing error %d\n", ret); + return ret; + } + + if (IS_ENABLED(CONFIG_VIDEO_BRIDGE)) { + if (priv->conn_dev && + device_get_uclass_id(priv->conn_dev) == UCLASS_VIDEO_BRIDGE) { + ret = video_bridge_attach(priv->conn_dev); + if (ret) { + dev_err(dev, "fail to attach bridge\n"); + return ret; + } + + ret = video_bridge_set_active(priv->conn_dev, true); + if (ret) { + dev_err(dev, "fail to active bridge\n"); + return ret; + } + } + } + } + + return 0; +} + +static int imx8_ldb_bind(struct udevice *dev) +{ + ofnode lvds_ch_node; + int ret = 0; + + lvds_ch_node = ofnode_find_subnode(dev_ofnode(dev), "lvds-channel@0"); + if (ofnode_valid(lvds_ch_node)) { + ret = device_bind(dev, dev->driver, "lvds-channel@0", (void *)1, + lvds_ch_node, NULL); + if (ret) + printf("Error binding driver '%s': %d\n", dev->driver->name, + ret); + } + + return ret; +} + +struct dm_display_ops imx8_ldb_ops = { + .read_timing = imx8_ldb_read_timing, + .enable = imx8_ldb_enable, +}; + +static const struct udevice_id imx8_ldb_ids[] = { + { .compatible = "fsl,imx8qm-ldb" }, + { .compatible = "fsl,imx8qxp-ldb", .data = FLAG_COMBO, }, + { } +}; + +U_BOOT_DRIVER(imx8_ldb) = { + .name = "imx8_ldb", + .id = UCLASS_DISPLAY, + .of_match = imx8_ldb_ids, + .bind = imx8_ldb_bind, + .probe = imx8_ldb_probe, + .ops = &imx8_ldb_ops, + .priv_auto = sizeof(struct imx8_ldb_priv), +}; diff --git a/drivers/video/nxp/imx/imx8m_dcss.c b/drivers/video/nxp/imx/imx8m_dcss.c new file mode 100644 index 00000000000..6315471a900 --- /dev/null +++ b/drivers/video/nxp/imx/imx8m_dcss.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + */ +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <env.h> +#include <linux/errno.h> +#include <malloc.h> +#include <video.h> +#include <video_fb.h> +#include <display.h> + +#include <asm/cache.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <video_bridge.h> +#include <clk.h> +#include <video_link.h> + +#ifdef DEBUG +#define reg32_write(addr, val) \ +do { \ + debug("%s():%d 0x%08x -> 0x%08x\n", __func__, __LINE__, \ + (unsigned int)addr, (unsigned int)val); \ + __raw_writel(val, addr); \ +} while (0) +#else +#define reg32_write(addr, val) __raw_writel(val, addr) +#endif + +#define reg32_read(addr) __raw_readl(addr) + +#define reg32setbit(addr, bitpos) \ + reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos)))) +#define reg32clearbit(addr, bitpos) \ + reg32_write((addr), (reg32_read((addr)) & ~(1<<(bitpos)))) + +#define reg32_read_tst(addr, val, mask) \ +do { \ + u32 temp = reg32_read((addr)); \ + if ((temp & (mask)) == ((val) & (mask))) \ + debug("%s():%d 0x%08x -> 0x%08x\n", \ + __func__, __LINE__, addr, val); \ + else \ + debug("%s():%d 0x%08x -> 0x%08x instead of 0x%08x\n", \ + __func__, __LINE__, addr, temp, val); \ +} while (0) + + +struct imx8m_dcss_priv { + struct udevice *disp_dev; + struct display_timing timings; + + bool hpol; /* horizontal pulse polarity */ + bool vpol; /* vertical pulse polarity */ + bool enabled; + + fdt_addr_t addr; +}; + +__weak int imx8m_dcss_clock_init(u32 pixclk) +{ + return 0; +} + +__weak int imx8m_dcss_power_init(void) +{ + return 0; +} + +static void imx8m_dcss_reset(struct udevice *dev) +{ + struct imx8m_dcss_priv *priv = dev_get_priv(dev); + u32 temp; + + /* DCSS reset */ + reg32_write(priv->addr + 0x2f000, 0xffffffff); + + /* DCSS clock selection */ + reg32_write(priv->addr + 0x2f010, 0x1); + temp = reg32_read(priv->addr + 0x2f010); + debug("%s(): DCSS clock control 0x%08x\n", __func__, temp); +} + +static void imx8m_dcss_init(struct udevice *dev) +{ + struct imx8m_dcss_priv *priv = dev_get_priv(dev); + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + debug("%s() ...\n", __func__); + + /* DTRC-CHAN2/3 */ + reg32_write(priv->addr + 0x160c8, 0x00000002); + reg32_write(priv->addr + 0x170c8, 0x00000002); + + /* CHAN1_DPR */ + reg32_write(priv->addr + 0x180c0, (unsigned int)plat->base); + reg32_write(priv->addr + 0x18090, 0x00000002); + reg32_write(priv->addr + 0x180a0, priv->timings.hactive.typ); + reg32_write(priv->addr + 0x180b0, priv->timings.vactive.typ); + reg32_write(priv->addr + 0x18110, + (unsigned int)plat->base + priv->timings.hactive.typ * priv->timings.vactive.typ); + reg32_write(priv->addr + 0x180f0, 0x00000280); + reg32_write(priv->addr + 0x18100, 0x000000f0); + reg32_write(priv->addr + 0x18070, ((priv->timings.hactive.typ * 4) << 16)); + reg32_write(priv->addr + 0x18050, 0x000e4203); + reg32_write(priv->addr + 0x18050, 0x000e4203); + reg32_write(priv->addr + 0x18200, 0x00000038); + reg32_write(priv->addr + 0x18000, 0x00000004); + reg32_write(priv->addr + 0x18000, 0x00000005); + + /* SCALER */ + reg32_write(priv->addr + 0x1c008, 0x00000000); + reg32_write(priv->addr + 0x1c00c, 0x00000000); + reg32_write(priv->addr + 0x1c010, 0x00000002); + reg32_write(priv->addr + 0x1c014, 0x00000002); + reg32_write(priv->addr + 0x1c018, + ((priv->timings.vactive.typ - 1) << 16 | (priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x1c01c, + ((priv->timings.vactive.typ - 1) << 16 | (priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x1c020, + ((priv->timings.vactive.typ - 1) << 16 | (priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x1c024, + ((priv->timings.vactive.typ - 1) << 16 | (priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x1c028, 0x00000000); + reg32_write(priv->addr + 0x1c02c, 0x00000000); + reg32_write(priv->addr + 0x1c030, 0x00000000); + reg32_write(priv->addr + 0x1c034, 0x00000000); + reg32_write(priv->addr + 0x1c038, 0x00000000); + reg32_write(priv->addr + 0x1c03c, 0x00000000); + reg32_write(priv->addr + 0x1c040, 0x00000000); + reg32_write(priv->addr + 0x1c044, 0x00000000); + reg32_write(priv->addr + 0x1c048, 0x00000000); + reg32_write(priv->addr + 0x1c04c, 0x00002000); + reg32_write(priv->addr + 0x1c050, 0x00000000); + reg32_write(priv->addr + 0x1c054, 0x00002000); + reg32_write(priv->addr + 0x1c058, 0x00000000); + reg32_write(priv->addr + 0x1c05c, 0x00002000); + reg32_write(priv->addr + 0x1c060, 0x00000000); + reg32_write(priv->addr + 0x1c064, 0x00002000); + reg32_write(priv->addr + 0x1c080, 0x00000000); + reg32_write(priv->addr + 0x1c0c0, 0x00040000); + reg32_write(priv->addr + 0x1c100, 0x00000000); + reg32_write(priv->addr + 0x1c084, 0x00000000); + reg32_write(priv->addr + 0x1c0c4, 0x00000000); + reg32_write(priv->addr + 0x1c104, 0x00000000); + reg32_write(priv->addr + 0x1c088, 0x00000000); + reg32_write(priv->addr + 0x1c0c8, 0x00000000); + reg32_write(priv->addr + 0x1c108, 0x00000000); + reg32_write(priv->addr + 0x1c08c, 0x00000000); + reg32_write(priv->addr + 0x1c0cc, 0x00000000); + reg32_write(priv->addr + 0x1c10c, 0x00000000); + reg32_write(priv->addr + 0x1c090, 0x00000000); + reg32_write(priv->addr + 0x1c0d0, 0x00000000); + reg32_write(priv->addr + 0x1c110, 0x00000000); + reg32_write(priv->addr + 0x1c094, 0x00000000); + reg32_write(priv->addr + 0x1c0d4, 0x00000000); + reg32_write(priv->addr + 0x1c114, 0x00000000); + reg32_write(priv->addr + 0x1c098, 0x00000000); + reg32_write(priv->addr + 0x1c0d8, 0x00000000); + reg32_write(priv->addr + 0x1c118, 0x00000000); + reg32_write(priv->addr + 0x1c09c, 0x00000000); + reg32_write(priv->addr + 0x1c0dc, 0x00000000); + reg32_write(priv->addr + 0x1c11c, 0x00000000); + reg32_write(priv->addr + 0x1c0a0, 0x00000000); + reg32_write(priv->addr + 0x1c0e0, 0x00000000); + reg32_write(priv->addr + 0x1c120, 0x00000000); + reg32_write(priv->addr + 0x1c0a4, 0x00000000); + reg32_write(priv->addr + 0x1c0e4, 0x00000000); + reg32_write(priv->addr + 0x1c124, 0x00000000); + reg32_write(priv->addr + 0x1c0a8, 0x00000000); + reg32_write(priv->addr + 0x1c0e8, 0x00000000); + reg32_write(priv->addr + 0x1c128, 0x00000000); + reg32_write(priv->addr + 0x1c0ac, 0x00000000); + reg32_write(priv->addr + 0x1c0ec, 0x00000000); + reg32_write(priv->addr + 0x1c12c, 0x00000000); + reg32_write(priv->addr + 0x1c0b0, 0x00000000); + reg32_write(priv->addr + 0x1c0f0, 0x00000000); + reg32_write(priv->addr + 0x1c130, 0x00000000); + reg32_write(priv->addr + 0x1c0b4, 0x00000000); + reg32_write(priv->addr + 0x1c0f4, 0x00000000); + reg32_write(priv->addr + 0x1c134, 0x00000000); + reg32_write(priv->addr + 0x1c0b8, 0x00000000); + reg32_write(priv->addr + 0x1c0f8, 0x00000000); + reg32_write(priv->addr + 0x1c138, 0x00000000); + reg32_write(priv->addr + 0x1c0bc, 0x00000000); + reg32_write(priv->addr + 0x1c0fc, 0x00000000); + reg32_write(priv->addr + 0x1c13c, 0x00000000); + reg32_write(priv->addr + 0x1c140, 0x00000000); + reg32_write(priv->addr + 0x1c180, 0x00040000); + reg32_write(priv->addr + 0x1c1c0, 0x00000000); + reg32_write(priv->addr + 0x1c144, 0x00000000); + reg32_write(priv->addr + 0x1c184, 0x00000000); + reg32_write(priv->addr + 0x1c1c4, 0x00000000); + reg32_write(priv->addr + 0x1c148, 0x00000000); + reg32_write(priv->addr + 0x1c188, 0x00000000); + reg32_write(priv->addr + 0x1c1c8, 0x00000000); + reg32_write(priv->addr + 0x1c14c, 0x00000000); + reg32_write(priv->addr + 0x1c18c, 0x00000000); + reg32_write(priv->addr + 0x1c1cc, 0x00000000); + reg32_write(priv->addr + 0x1c150, 0x00000000); + reg32_write(priv->addr + 0x1c190, 0x00000000); + reg32_write(priv->addr + 0x1c1d0, 0x00000000); + reg32_write(priv->addr + 0x1c154, 0x00000000); + reg32_write(priv->addr + 0x1c194, 0x00000000); + reg32_write(priv->addr + 0x1c1d4, 0x00000000); + reg32_write(priv->addr + 0x1c158, 0x00000000); + reg32_write(priv->addr + 0x1c198, 0x00000000); + reg32_write(priv->addr + 0x1c1d8, 0x00000000); + reg32_write(priv->addr + 0x1c15c, 0x00000000); + reg32_write(priv->addr + 0x1c19c, 0x00000000); + reg32_write(priv->addr + 0x1c1dc, 0x00000000); + reg32_write(priv->addr + 0x1c160, 0x00000000); + reg32_write(priv->addr + 0x1c1a0, 0x00000000); + reg32_write(priv->addr + 0x1c1e0, 0x00000000); + reg32_write(priv->addr + 0x1c164, 0x00000000); + reg32_write(priv->addr + 0x1c1a4, 0x00000000); + reg32_write(priv->addr + 0x1c1e4, 0x00000000); + reg32_write(priv->addr + 0x1c168, 0x00000000); + reg32_write(priv->addr + 0x1c1a8, 0x00000000); + reg32_write(priv->addr + 0x1c1e8, 0x00000000); + reg32_write(priv->addr + 0x1c16c, 0x00000000); + reg32_write(priv->addr + 0x1c1ac, 0x00000000); + reg32_write(priv->addr + 0x1c1ec, 0x00000000); + reg32_write(priv->addr + 0x1c170, 0x00000000); + reg32_write(priv->addr + 0x1c1b0, 0x00000000); + reg32_write(priv->addr + 0x1c1f0, 0x00000000); + reg32_write(priv->addr + 0x1c174, 0x00000000); + reg32_write(priv->addr + 0x1c1b4, 0x00000000); + reg32_write(priv->addr + 0x1c1f4, 0x00000000); + reg32_write(priv->addr + 0x1c178, 0x00000000); + reg32_write(priv->addr + 0x1c1b8, 0x00000000); + reg32_write(priv->addr + 0x1c1f8, 0x00000000); + reg32_write(priv->addr + 0x1c17c, 0x00000000); + reg32_write(priv->addr + 0x1c1bc, 0x00000000); + reg32_write(priv->addr + 0x1c1fc, 0x00000000); + reg32_write(priv->addr + 0x1c300, 0x00000000); + reg32_write(priv->addr + 0x1c340, 0x00000000); + reg32_write(priv->addr + 0x1c380, 0x00000000); + reg32_write(priv->addr + 0x1c304, 0x00000000); + reg32_write(priv->addr + 0x1c344, 0x00000000); + reg32_write(priv->addr + 0x1c384, 0x00000000); + reg32_write(priv->addr + 0x1c308, 0x00000000); + reg32_write(priv->addr + 0x1c348, 0x00000000); + reg32_write(priv->addr + 0x1c388, 0x00000000); + reg32_write(priv->addr + 0x1c30c, 0x00000000); + reg32_write(priv->addr + 0x1c34c, 0x00000000); + reg32_write(priv->addr + 0x1c38c, 0x00000000); + reg32_write(priv->addr + 0x1c310, 0x00000000); + reg32_write(priv->addr + 0x1c350, 0x00000000); + reg32_write(priv->addr + 0x1c390, 0x00000000); + reg32_write(priv->addr + 0x1c314, 0x00000000); + reg32_write(priv->addr + 0x1c354, 0x00000000); + reg32_write(priv->addr + 0x1c394, 0x00000000); + reg32_write(priv->addr + 0x1c318, 0x00000000); + reg32_write(priv->addr + 0x1c358, 0x00000000); + reg32_write(priv->addr + 0x1c398, 0x00000000); + reg32_write(priv->addr + 0x1c31c, 0x00000000); + reg32_write(priv->addr + 0x1c35c, 0x00000000); + reg32_write(priv->addr + 0x1c39c, 0x00000000); + reg32_write(priv->addr + 0x1c320, 0x00000000); + reg32_write(priv->addr + 0x1c360, 0x00000000); + reg32_write(priv->addr + 0x1c3a0, 0x00000000); + reg32_write(priv->addr + 0x1c324, 0x00000000); + reg32_write(priv->addr + 0x1c364, 0x00000000); + reg32_write(priv->addr + 0x1c3a4, 0x00000000); + reg32_write(priv->addr + 0x1c328, 0x00000000); + reg32_write(priv->addr + 0x1c368, 0x00000000); + reg32_write(priv->addr + 0x1c3a8, 0x00000000); + reg32_write(priv->addr + 0x1c32c, 0x00000000); + reg32_write(priv->addr + 0x1c36c, 0x00000000); + reg32_write(priv->addr + 0x1c3ac, 0x00000000); + reg32_write(priv->addr + 0x1c330, 0x00000000); + reg32_write(priv->addr + 0x1c370, 0x00000000); + reg32_write(priv->addr + 0x1c3b0, 0x00000000); + reg32_write(priv->addr + 0x1c334, 0x00000000); + reg32_write(priv->addr + 0x1c374, 0x00000000); + reg32_write(priv->addr + 0x1c3b4, 0x00000000); + reg32_write(priv->addr + 0x1c338, 0x00000000); + reg32_write(priv->addr + 0x1c378, 0x00000000); + reg32_write(priv->addr + 0x1c3b8, 0x00000000); + reg32_write(priv->addr + 0x1c33c, 0x00000000); + reg32_write(priv->addr + 0x1c37c, 0x00000000); + reg32_write(priv->addr + 0x1c3bc, 0x00000000); + reg32_write(priv->addr + 0x1c200, 0x00000000); + reg32_write(priv->addr + 0x1c240, 0x00000000); + reg32_write(priv->addr + 0x1c280, 0x00000000); + reg32_write(priv->addr + 0x1c204, 0x00000000); + reg32_write(priv->addr + 0x1c244, 0x00000000); + reg32_write(priv->addr + 0x1c284, 0x00000000); + reg32_write(priv->addr + 0x1c208, 0x00000000); + reg32_write(priv->addr + 0x1c248, 0x00000000); + reg32_write(priv->addr + 0x1c288, 0x00000000); + reg32_write(priv->addr + 0x1c20c, 0x00000000); + reg32_write(priv->addr + 0x1c24c, 0x00000000); + reg32_write(priv->addr + 0x1c28c, 0x00000000); + reg32_write(priv->addr + 0x1c210, 0x00000000); + reg32_write(priv->addr + 0x1c250, 0x00000000); + reg32_write(priv->addr + 0x1c290, 0x00000000); + reg32_write(priv->addr + 0x1c214, 0x00000000); + reg32_write(priv->addr + 0x1c254, 0x00000000); + reg32_write(priv->addr + 0x1c294, 0x00000000); + reg32_write(priv->addr + 0x1c218, 0x00000000); + reg32_write(priv->addr + 0x1c258, 0x00000000); + reg32_write(priv->addr + 0x1c298, 0x00000000); + reg32_write(priv->addr + 0x1c21c, 0x00000000); + reg32_write(priv->addr + 0x1c25c, 0x00000000); + reg32_write(priv->addr + 0x1c29c, 0x00000000); + reg32_write(priv->addr + 0x1c220, 0x00000000); + reg32_write(priv->addr + 0x1c260, 0x00000000); + reg32_write(priv->addr + 0x1c2a0, 0x00000000); + reg32_write(priv->addr + 0x1c224, 0x00000000); + reg32_write(priv->addr + 0x1c264, 0x00000000); + reg32_write(priv->addr + 0x1c2a4, 0x00000000); + reg32_write(priv->addr + 0x1c228, 0x00000000); + reg32_write(priv->addr + 0x1c268, 0x00000000); + reg32_write(priv->addr + 0x1c2a8, 0x00000000); + reg32_write(priv->addr + 0x1c22c, 0x00000000); + reg32_write(priv->addr + 0x1c26c, 0x00000000); + reg32_write(priv->addr + 0x1c2ac, 0x00000000); + reg32_write(priv->addr + 0x1c230, 0x00000000); + reg32_write(priv->addr + 0x1c270, 0x00000000); + reg32_write(priv->addr + 0x1c2b0, 0x00000000); + reg32_write(priv->addr + 0x1c234, 0x00000000); + reg32_write(priv->addr + 0x1c274, 0x00000000); + reg32_write(priv->addr + 0x1c2b4, 0x00000000); + reg32_write(priv->addr + 0x1c238, 0x00000000); + reg32_write(priv->addr + 0x1c278, 0x00000000); + reg32_write(priv->addr + 0x1c2b8, 0x00000000); + reg32_write(priv->addr + 0x1c23c, 0x00000000); + reg32_write(priv->addr + 0x1c27c, 0x00000000); + reg32_write(priv->addr + 0x1c2bc, 0x00000000); + reg32_write(priv->addr + 0x1c2bc, 0x00000000); + reg32_write(priv->addr + 0x1c000, 0x00000011); + + /* SUBSAM */ + reg32_write(priv->addr + 0x1b070, 0x21612161); + reg32_write(priv->addr + 0x1b080, 0x03ff0000); + reg32_write(priv->addr + 0x1b090, 0x03ff0000); + + reg32_write(priv->addr + 0x1b010, + (((priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ + priv->timings.vsync_len.typ + + priv->timings.vactive.typ -1) << 16) | + (priv->timings.hfront_porch.typ + priv->timings.hback_porch.typ + priv->timings.hsync_len.typ + + priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x1b020, + (((priv->timings.hsync_len.typ - 1) << 16) | priv->hpol << 31 | (priv->timings.hfront_porch.typ + + priv->timings.hback_porch.typ + priv->timings.hsync_len.typ + priv->timings.hactive.typ -1))); + reg32_write(priv->addr + 0x1b030, + (((priv->timings.vfront_porch.typ + priv->timings.vsync_len.typ - 1) << 16) | priv->vpol << 31 | (priv->timings.vfront_porch.typ - 1))); + reg32_write(priv->addr + 0x1b040, + ((1 << 31) | ((priv->timings.vsync_len.typ +priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ) << 16) | + (priv->timings.hsync_len.typ + priv->timings.hback_porch.typ - 1))); + reg32_write(priv->addr + 0x1b050, + (((priv->timings.vsync_len.typ + priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ + priv->timings.vactive.typ -1) << 16) | + (priv->timings.hsync_len.typ + priv->timings.hback_porch.typ + priv->timings.hactive.typ - 1))); + + /* subsample mode 0 bypass 444, 1 422, 2 420 */ + reg32_write(priv->addr + 0x1b060, 0x0000000); + + reg32_write(priv->addr + 0x1b000, 0x00000001); + + /* DTG */ + /*reg32_write(priv->addr + 0x20000, 0xff000484); */ + /* disable local alpha */ + reg32_write(priv->addr + 0x20000, 0xff005084); + reg32_write(priv->addr + 0x20004, + (((priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ + priv->timings.vsync_len.typ + priv->timings.vactive.typ - + 1) << 16) | (priv->timings.hfront_porch.typ + priv->timings.hback_porch.typ + priv->timings.hsync_len.typ + + priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x20008, + (((priv->timings.vsync_len.typ + priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ - + 1) << 16) | (priv->timings.hsync_len.typ + priv->timings.hback_porch.typ - 1))); + reg32_write(priv->addr + 0x2000c, + (((priv->timings.vsync_len.typ + priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ + priv->timings.vactive.typ - + 1) << 16) | (priv->timings.hsync_len.typ + priv->timings.hback_porch.typ + priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x20010, + (((priv->timings.vsync_len.typ + priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ - + 1) << 16) | (priv->timings.hsync_len.typ + priv->timings.hback_porch.typ - 1))); + reg32_write(priv->addr + 0x20014, + (((priv->timings.vsync_len.typ + priv->timings.vfront_porch.typ + priv->timings.vback_porch.typ + priv->timings.vactive.typ - + 1) << 16) | (priv->timings.hsync_len.typ + priv->timings.hback_porch.typ + priv->timings.hactive.typ - 1))); + reg32_write(priv->addr + 0x20028, 0x000b000a); + + /* disable local alpha */ + reg32_write(priv->addr + 0x20000, 0xff005184); + + debug("leaving %s() ...\n", __func__); +} + +static void imx8m_display_shutdown(struct udevice *dev) +{ + struct imx8m_dcss_priv *priv = dev_get_priv(dev); + + /* stop the DCSS modules in use */ + /* dtg */ + reg32_write(priv->addr + 0x20000, 0); + /* scaler */ + reg32_write(priv->addr + 0x1c000, 0); + reg32_write(priv->addr + 0x1c400, 0); + reg32_write(priv->addr + 0x1c800, 0); + /* dpr */ + reg32_write(priv->addr + 0x18000, 0); + reg32_write(priv->addr + 0x19000, 0); + reg32_write(priv->addr + 0x1a000, 0); + /* sub-sampler*/ + reg32_write(priv->addr + 0x1b000, 0); +} + +static int imx8m_dcss_get_timings_from_display(struct udevice *dev, + struct display_timing *timings) +{ + struct imx8m_dcss_priv *priv = dev_get_priv(dev); + int err; + + priv->disp_dev = video_link_get_next_device(dev); + if (!priv->disp_dev || + device_get_uclass_id(priv->disp_dev) != UCLASS_DISPLAY) { + + printf("fail to find display device\n"); + return -ENODEV; + } + + debug("disp_dev %s\n", priv->disp_dev->name); + + err = video_link_get_display_timings(timings); + if (err) + return err; + + if (timings->flags & DISPLAY_FLAGS_HSYNC_HIGH) + priv->hpol = true; + + if (timings->flags & DISPLAY_FLAGS_VSYNC_HIGH) + priv->vpol = true; + + return 0; +} + +static int imx8m_dcss_probe(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct imx8m_dcss_priv *priv = dev_get_priv(dev); + + u32 fb_start, fb_end; + int ret; + + debug("%s() plat: base 0x%lx, size 0x%x\n", + __func__, plat->base, plat->size); + + priv->addr = dev_read_addr(dev); + if (priv->addr == FDT_ADDR_T_NONE) + return -EINVAL; + + ret = imx8m_dcss_get_timings_from_display(dev, &priv->timings); + if (ret) + return ret; + + debug("pixelclock %u, hlen %u, vlen %u\n", + priv->timings.pixelclock.typ, priv->timings.hactive.typ, priv->timings.vactive.typ); + + imx8m_dcss_power_init(); + + imx8m_dcss_clock_init(priv->timings.pixelclock.typ); + + imx8m_dcss_reset(dev); + + if (display_enable(priv->disp_dev, 32, NULL) == 0) { + imx8m_dcss_init(dev); + priv->enabled = true; + } + + uc_priv->bpix = VIDEO_BPP32; + uc_priv->xsize = priv->timings.hactive.typ; + uc_priv->ysize = priv->timings.vactive.typ; + + /* 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 ret; +} + +static int imx8m_dcss_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + debug("%s\n", __func__); + + /* Max size supported by LCDIF, because in bind, we can't probe panel */ + plat->size = 1920 * 1080 *4; + + return 0; +} + +static int imx8m_dcss_remove(struct udevice *dev) +{ + struct imx8m_dcss_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + + if (priv->enabled) { + device_remove(priv->disp_dev, DM_REMOVE_NORMAL); + imx8m_display_shutdown(dev); + } + + return 0; +} + +static const struct udevice_id imx8m_dcss_ids[] = { + { .compatible = "nxp,imx8mq-dcss" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(imx8m_dcss) = { + .name = "imx8m_dcss", + .id = UCLASS_VIDEO, + .of_match = imx8m_dcss_ids, + .bind = imx8m_dcss_bind, + .probe = imx8m_dcss_probe, + .remove = imx8m_dcss_remove, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto = sizeof(struct imx8m_dcss_priv), +}; diff --git a/drivers/video/nxp/imx/imx_lcdifv3.c b/drivers/video/nxp/imx/imx_lcdifv3.c new file mode 100644 index 00000000000..89fecf97b5e --- /dev/null +++ b/drivers/video/nxp/imx/imx_lcdifv3.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#include <common.h> +#include <malloc.h> +#include <video.h> +#include <video_fb.h> +#include <video_bridge.h> +#include <video_link.h> + +#include <asm/cache.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <linux/err.h> +#include <asm/io.h> + +#include "../../videomodes.h" +#include <linux/string.h> +#include <linux/list.h> +#include <linux/fb.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include "lcdifv3-regs.h" +#include <log.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> + +#define PS2KHZ(ps) (1000000000UL / (ps)) +#define HZ2PS(hz) (1000000000UL / ((hz) / 1000)) + +struct lcdifv3_priv { + fdt_addr_t reg_base; + struct udevice *disp_dev; +}; + +static int lcdifv3_set_pix_fmt(struct lcdifv3_priv *priv, unsigned int format) +{ + uint32_t ctrldescl0_5 = 0; + + ctrldescl0_5 = readl((ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); + + WARN_ON(ctrldescl0_5 & CTRLDESCL0_5_SHADOW_LOAD_EN); + + ctrldescl0_5 &= ~(CTRLDESCL0_5_BPP(0xf) | CTRLDESCL0_5_YUV_FORMAT(0x3)); + + switch (format) { + case GDF_16BIT_565RGB: + ctrldescl0_5 |= CTRLDESCL0_5_BPP(BPP16_RGB565); + break; + case GDF_32BIT_X888RGB: + ctrldescl0_5 |= CTRLDESCL0_5_BPP(BPP32_ARGB8888); + break; + default: + printf("unsupported pixel format: %u\n", format); + return -EINVAL; + } + + writel(ctrldescl0_5, (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); + + return 0; +} + + +static void lcdifv3_set_mode(struct lcdifv3_priv *priv, + struct ctfb_res_modes *mode) +{ + u32 disp_size, hsyn_para, vsyn_para, vsyn_hsyn_width, ctrldescl0_1; + + /* config display timings */ + disp_size = DISP_SIZE_DELTA_Y(mode->yres) | + DISP_SIZE_DELTA_X(mode->xres); + writel(disp_size, (ulong)(priv->reg_base + LCDIFV3_DISP_SIZE)); + + hsyn_para = HSYN_PARA_BP_H(mode->left_margin) | + HSYN_PARA_FP_H(mode->right_margin); + writel(hsyn_para, (ulong)(priv->reg_base + LCDIFV3_HSYN_PARA)); + + vsyn_para = VSYN_PARA_BP_V(mode->upper_margin) | + VSYN_PARA_FP_V(mode->lower_margin); + writel(vsyn_para, (ulong)(priv->reg_base + LCDIFV3_VSYN_PARA)); + + vsyn_hsyn_width = VSYN_HSYN_WIDTH_PW_V(mode->vsync_len) | + VSYN_HSYN_WIDTH_PW_H(mode->hsync_len); + writel(vsyn_hsyn_width, (ulong)(priv->reg_base + LCDIFV3_VSYN_HSYN_WIDTH)); + + /* config layer size */ + /* TODO: 32bits alignment for width */ + ctrldescl0_1 = CTRLDESCL0_1_HEIGHT(mode->yres) | + CTRLDESCL0_1_WIDTH(mode->xres); + writel(ctrldescl0_1, (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_1)); + + /* Polarities */ + writel(CTRL_INV_HS, (ulong)(priv->reg_base + LCDIFV3_CTRL_CLR)); + writel(CTRL_INV_VS, (ulong)(priv->reg_base + LCDIFV3_CTRL_CLR)); + + /* SEC MIPI DSI specific */ + writel(CTRL_INV_PXCK, (ulong)(priv->reg_base + LCDIFV3_CTRL_CLR)); + writel(CTRL_INV_DE, (ulong)(priv->reg_base + LCDIFV3_CTRL_CLR)); + +} + +static void lcdifv3_set_bus_fmt(struct lcdifv3_priv *priv) +{ + uint32_t disp_para = 0; + + disp_para = readl((ulong)(priv->reg_base + LCDIFV3_DISP_PARA)); + disp_para &= DISP_PARA_LINE_PATTERN(0xf); + + /* Fixed to 24 bits output */ + disp_para |= DISP_PARA_LINE_PATTERN(LP_RGB888_OR_YUV444); + + /* config display mode: default is normal mode */ + disp_para &= DISP_PARA_DISP_MODE(3); + disp_para |= DISP_PARA_DISP_MODE(0); + writel(disp_para, (ulong)(priv->reg_base + LCDIFV3_DISP_PARA)); +} + +static void lcdifv3_enable_controller(struct lcdifv3_priv *priv) +{ + u32 disp_para, ctrldescl0_5; + + disp_para = readl((ulong)(priv->reg_base + LCDIFV3_DISP_PARA)); + ctrldescl0_5 = readl((ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); + + /* disp on */ + disp_para |= DISP_PARA_DISP_ON; + writel(disp_para, (ulong)(priv->reg_base + LCDIFV3_DISP_PARA)); + + /* enable shadow load */ + ctrldescl0_5 |= CTRLDESCL0_5_SHADOW_LOAD_EN; + writel(ctrldescl0_5, (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); + + /* enable layer dma */ + ctrldescl0_5 |= CTRLDESCL0_5_EN; + writel(ctrldescl0_5, (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); +} + +static void lcdifv3_disable_controller(struct lcdifv3_priv *priv) +{ + u32 disp_para, ctrldescl0_5; + + disp_para = readl((ulong)(priv->reg_base + LCDIFV3_DISP_PARA)); + ctrldescl0_5 = readl((ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); + + /* dma off */ + ctrldescl0_5 &= ~CTRLDESCL0_5_EN; + writel(ctrldescl0_5, (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_5)); + + /* disp off */ + disp_para &= ~DISP_PARA_DISP_ON; + writel(disp_para, (ulong)(priv->reg_base + LCDIFV3_DISP_PARA)); +} + +static void lcdifv3_init(struct udevice *dev, + struct ctfb_res_modes *mode, unsigned int format) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct lcdifv3_priv *priv = dev_get_priv(dev); + int ret; + + /* Kick in the LCDIF clock */ + mxs_set_lcdclk(priv->reg_base, PS2KHZ(mode->pixclock)); + + writel(CTRL_SW_RESET, (ulong)(priv->reg_base + LCDIFV3_CTRL_CLR)); + + lcdifv3_set_mode(priv, mode); + + lcdifv3_set_bus_fmt(priv); + + ret = lcdifv3_set_pix_fmt(priv, format); + if (ret) { + printf("Fail to init lcdifv3, wrong format %u\n", format); + return; + } + + /* Set fb address to primary layer */ + writel(plat->base, (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL_LOW0_4)); + + writel(CTRLDESCL0_3_P_SIZE(1) |CTRLDESCL0_3_T_SIZE(1) | CTRLDESCL0_3_PITCH(mode->xres * 4), + (ulong)(priv->reg_base + LCDIFV3_CTRLDESCL0_3)); + + lcdifv3_enable_controller(priv); +} + +void lcdifv3_power_down(struct lcdifv3_priv *priv) +{ + int timeout = 1000000; + + /* Disable LCDIF during VBLANK */ + writel(INT_STATUS_D0_VS_BLANK, + (ulong)(priv->reg_base + LCDIFV3_INT_STATUS_D0)); + while (--timeout) { + if (readl((ulong)(priv->reg_base + LCDIFV3_INT_STATUS_D0)) & + INT_STATUS_D0_VS_BLANK) + break; + udelay(1); + } + + lcdifv3_disable_controller(priv); +} + +static int lcdifv3_of_get_timings(struct udevice *dev, + struct display_timing *timings) +{ + int ret = 0; + struct lcdifv3_priv *priv = dev_get_priv(dev); + + priv->disp_dev = video_link_get_next_device(dev); + if (!priv->disp_dev || + (device_get_uclass_id(priv->disp_dev) != UCLASS_VIDEO_BRIDGE + && device_get_uclass_id(priv->disp_dev) != UCLASS_DISPLAY)) { + + printf("fail to find output device\n"); + return -ENODEV; + } + + debug("disp_dev %s\n", priv->disp_dev->name); + + ret = video_link_get_display_timings(timings); + if (ret) { + printf("fail to get display timings\n"); + return ret; + } + + return ret; +} + +static int lcdifv3_video_probe(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct lcdifv3_priv *priv = dev_get_priv(dev); + + struct ctfb_res_modes mode; + struct display_timing timings; + + u32 fb_start, fb_end; + int ret; + + debug("%s() plat: base 0x%lx, size 0x%x\n", + __func__, plat->base, plat->size); + + priv->reg_base = dev_read_addr(dev); + if (priv->reg_base == FDT_ADDR_T_NONE) { + dev_err(dev, "lcdif base address is not found\n"); + return -EINVAL; + } + + ret = lcdifv3_of_get_timings(dev, &timings); + if (ret) + return ret; + + if (priv->disp_dev) { +#if IS_ENABLED(CONFIG_VIDEO_BRIDGE) + if (device_get_uclass_id(priv->disp_dev) == UCLASS_VIDEO_BRIDGE) { + ret = video_bridge_attach(priv->disp_dev); + if (ret) { + dev_err(dev, "fail to attach bridge\n"); + return ret; + } + + ret = video_bridge_set_backlight(priv->disp_dev, 80); + if (ret) { + dev_err(dev, "fail to set backlight\n"); + return ret; + } + } +#endif + } + + mode.xres = timings.hactive.typ; + mode.yres = timings.vactive.typ; + mode.left_margin = timings.hback_porch.typ; + mode.right_margin = timings.hfront_porch.typ; + mode.upper_margin = timings.vback_porch.typ; + mode.lower_margin = timings.vfront_porch.typ; + mode.hsync_len = timings.hsync_len.typ; + mode.vsync_len = timings.vsync_len.typ; + mode.pixclock = HZ2PS(timings.pixelclock.typ); + + lcdifv3_init(dev, &mode, GDF_32BIT_X888RGB); + + uc_priv->bpix = VIDEO_BPP32; /* only support 32 BPP now */ + uc_priv->xsize = mode.xres; + uc_priv->ysize = mode.yres; + + /* 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); + gd->fb_base = plat->base; + + return ret; +} + +static int lcdifv3_video_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + /* Max size supported by LCDIF, because in bind, we can't probe panel */ + plat->size = 1920 * 1080 *4 * 2; + + return 0; +} + +static int lcdifv3_video_remove(struct udevice *dev) +{ + struct lcdifv3_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + + if (priv->disp_dev) + device_remove(priv->disp_dev, DM_REMOVE_NORMAL); + + lcdifv3_power_down(priv); + + return 0; +} + +static const struct udevice_id lcdifv3_video_ids[] = { + { .compatible = "fsl,imx8mp-lcdif1" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(lcdifv3_video) = { + .name = "lcdifv3_video", + .id = UCLASS_VIDEO, + .of_match = lcdifv3_video_ids, + .bind = lcdifv3_video_bind, + .probe = lcdifv3_video_probe, + .remove = lcdifv3_video_remove, + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE, + .priv_auto = sizeof(struct lcdifv3_priv), +}; diff --git a/drivers/video/nxp/imx/imxdpuv1.c b/drivers/video/nxp/imx/imxdpuv1.c new file mode 100644 index 00000000000..26025e1453a --- /dev/null +++ b/drivers/video/nxp/imx/imxdpuv1.c @@ -0,0 +1,6214 @@ +/* + * Copyright 2015-2017 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/types.h> + +#include "imxdpuv1_private.h" +#include "imxdpuv1_registers.h" +#include "imxdpuv1_events.h" + +#include "imxdpuv1_be.h" + +#define ptr_to_uint32(__ptr__) ((uint32_t)((uint64_t)(__ptr__))) + +/* Private data*/ +static struct imxdpuv1_soc imxdpuv1_array[IMXDPUV1_MAX_NUM]; + +typedef struct { + uint8_t len; + uint8_t buffers; +} imxdpuv1_burst_entry_t; + +static const imxdpuv1_burst_entry_t burst_param[] = { + { 0, 0 }, /* IMXDPUV1_SCAN_DIR_UNKNOWN */ + { 8, 32 }, /* IMXDPUV1_SCAN_DIR_LEFT_RIGHT_DOWN */ + { 16, 16 }, /* IMXDPUV1_SCAN_DIR_HORIZONTAL */ + { 8, 32 }, /* IMXDPUV1_SCAN_DIR_VERTICAL possibly 8/32 here */ + { 8, 32 }, /* IMXDPUV1_SCAN_DIR_FREE */ +}; + +typedef struct { + uint32_t extdst; + uint32_t sub; +} trigger_entry_t; + +static const trigger_entry_t trigger_list[IMXDPUV1_SHDLD_IDX_MAX] = { + /* IMXDPUV1_SHDLD_* extdst, sub */ + /* _DISP0 */{ 1, 0 }, + /* _DISP1 */{ 1, 0 }, + /* _CONST0 */{ IMXDPUV1_SHDLD_CONSTFRAME0, 0 }, + /* _CONST1 */{ IMXDPUV1_SHDLD_CONSTFRAME1, 0 }, + /* _CHAN_00 */{ IMXDPUV1_SHDLD_FETCHDECODE2, 0 }, + /* _CHAN_01 */{ IMXDPUV1_SHDLD_FETCHDECODE0, 0 }, + /* _CHAN_02 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_1 }, + /* _CHAN_03 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_2 }, + /* _CHAN_04 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_3 }, + /* _CHAN_05 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_4 }, + /* _CHAN_06 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_5 }, + /* _CHAN_07 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_6 }, + /* _CHAN_08 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_7 }, + /* _CHAN_09 */{ IMXDPUV1_SHDLD_FETCHLAYER0, IMXDPUV1_SUB_8 }, + /* _CHAN_10 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_1 << 16 }, + /* _CHAN_11 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_2 << 16 }, + /* _CHAN_12 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_3 << 16 }, + /* _CHAN_13 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_4 << 16 }, + /* _CHAN_14 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_5 << 16 }, + /* _CHAN_15 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_6 << 16 }, + /* _CHAN_16 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_7 << 16 }, + /* _CHAN_17 */{ IMXDPUV1_SHDLD_FETCHWARP2, IMXDPUV1_SUB_8 << 16 }, + /* _CHAN_18 */{ IMXDPUV1_SHDLD_FETCHDECODE3, 0 }, + /* _CHAN_19 */{ IMXDPUV1_SHDLD_FETCHDECODE1, 0 }, + /* _CHAN_20 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_1 << 8 }, + /* _CHAN_21 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_2 << 8 }, + /* _CHAN_22 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_3 << 8 }, + /* _CHAN_23 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_4 << 8 }, + /* _CHAN_24 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_5 << 8 }, + /* _CHAN_25 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_6 << 8 }, + /* _CHAN_26 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_7 << 8 }, + /* _CHAN_27 */{ IMXDPUV1_SHDLD_FETCHLAYER1, IMXDPUV1_SUB_8 << 8 }, + /* _CHAN_28 */{ IMXDPUV1_SHDLD_FETCHECO0, 0 }, + /* _CHAN_29 */{ IMXDPUV1_SHDLD_FETCHECO1, 0 }, + /* _CHAN_30 */{ IMXDPUV1_SHDLD_FETCHECO2, 0 } +}; + +#ifdef ENABLE_IMXDPUV1_TRACE_REG +uint32_t _imxdpuv1_read(struct imxdpuv1_soc *imxdpu, uint32_t offset, char *file, + int line) +{ + uint32_t val = 0; + val = __raw_readl(imxdpu->base + offset); + IMXDPUV1_TRACE_REG("%s:%d R reg 0x%08x --> val 0x%08x\n", file, line, + (uint32_t)offset, (uint32_t)val); + return val; +} + +void _imxdpuv1_write(struct imxdpuv1_soc *imxdpu, uint32_t offset, uint32_t value, + char *file, int line) +{ + __raw_writel(value, imxdpu->base + offset); + IMXDPUV1_TRACE_REG("%s:%d W reg 0x%08x <-- val 0x%08x\n", file, line, + (uint32_t)offset, (uint32_t)value); +} + +#endif + +void _imxdpuv1_write_block(struct imxdpuv1_soc *imxdpu, uint32_t offset, + void *values, uint32_t cnt, char *file, int line) +{ + int i; + uint32_t *dest = (uint32_t *)(imxdpu->base + offset); + uint32_t *src = (uint32_t *)values; + IMXDPUV1_TRACE_REG("%s:%d W reg 0x%08x <-- cnt 0x%08x\n", file, line, + (uint32_t)offset, (uint32_t)cnt); + for (i = 0; i < cnt; i++) { + dest[i] = src[i]; + IMXDPUV1_TRACE_REG("%s:%d WB reg 0x%08x <-- val 0x%08x\n", file, line, + (uint32_t) ((uint64_t)(&dest[i])), (uint32_t)(src[i])); + + } +} + +#ifdef ENABLE_IMXDPUV1_TRACE_IRQ_READ +uint32_t _imxdpuv1_read_irq(struct imxdpuv1_soc *imxdpu, uint32_t offset, + char *file, int line) +{ + uint32_t val = 0; + val = __raw_readl(imxdpu->base + offset); + IMXDPUV1_TRACE_IRQ("%s:%d IRQ R reg 0x%08x --> val 0x%08x\n", file, line, + (uint32_t)offset, (uint32_t)val); + return val; +} +#endif + +#ifdef ENABLE_IMXDPUV1_TRACE_IRQ_WRITE +void _imxdpuv1_write_irq(struct imxdpuv1_soc *imxdpu, uint32_t offset, + uint32_t value, char *file, int line) +{ + __raw_writel(value, imxdpu->base + offset); + IMXDPUV1_TRACE_IRQ("%s:%d IRQ W reg 0x%08x <-- val 0x%08x\n", file, line, + (uint32_t)offset, (uint32_t)value); +} +#endif + +/* static prototypes */ +int imxdpuv1_dump_channel(int8_t imxdpuv1_id, imxdpuv1_chan_t chan); +static int imxdpuv1_disp_start_shadow_loads(int8_t imxdpuv1_id, int8_t disp); +void imxdpuv1_dump_pixencfg_status(int8_t imxdpuv1_id); +static bool imxdpuv1_is_yuv(uint32_t fmt); +bool imxdpuv1_is_rgb(uint32_t fmt); + +/*! + * Returns IMXDPUV1_TRUE for a valid channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_chan(imxdpuv1_chan_t chan) +{ + imxdpuv1_chan_idx_t chan_idx = get_channel_idx(chan); + + if ((chan_idx >= IMXDPUV1_CHAN_IDX_IN_FIRST) && + (chan_idx < IMXDPUV1_CHAN_IDX_IN_MAX)) + return IMXDPUV1_TRUE; + if ((chan_idx >= IMXDPUV1_CHAN_IDX_OUT_FIRST) && + (chan_idx < IMXDPUV1_CHAN_IDX_OUT_MAX)) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE for a valid store channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_store_chan(imxdpuv1_chan_t chan) +{ +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_STORE4) || (blk_id == IMXDPUV1_ID_STORE4)) + return IMXDPUV1_TRUE; +#endif + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE for a valid fetch channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_fetch_eco_chan(imxdpuv1_chan_t chan) +{ + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_FETCHECO0) || + (blk_id == IMXDPUV1_ID_FETCHECO1) || + (blk_id == IMXDPUV1_ID_FETCHECO2)) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE for a valid fetch decode channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_fetch_decode_chan(imxdpuv1_chan_t chan) +{ + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_FETCHDECODE0) || + (blk_id == IMXDPUV1_ID_FETCHDECODE1) +#ifdef IMXDPUV1_VERSION_0 + || (blk_id == IMXDPUV1_ID_FETCHDECODE2) + || (blk_id == IMXDPUV1_ID_FETCHDECODE3) +#endif + ) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE if a fetch channel has an eco fetch + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int has_fetch_eco_chan(imxdpuv1_chan_t chan) +{ + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_FETCHDECODE0) || + (blk_id == IMXDPUV1_ID_FETCHDECODE1) || + (blk_id == IMXDPUV1_ID_FETCHWARP2)) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE for a valid fetch warp channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_fetch_warp_chan(imxdpuv1_chan_t chan) +{ + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_FETCHWARP2)) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE for a valid fetch layer channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_fetch_layer_chan(imxdpuv1_chan_t chan) +{ + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_FETCHLAYER0) +#ifdef IMXDPUV1_VERSION_0 + || (blk_id == IMXDPUV1_ID_FETCHLAYER1) +#endif + ) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns IMXDPUV1_TRUE for a valid layer sub1 channel + * + * @param channel to test + * + * @return This function returns IMXDPUV1_TRUE on success or + * IMXDPUV1_FALSE if the test fails. + */ +static int is_fetch_layer_sub_chan1(imxdpuv1_chan_t chan) +{ + imxdpuv1_id_t blk_id = get_channel_blk(chan); + if ((blk_id == IMXDPUV1_ID_FETCHLAYER0) || +#ifdef IMXDPUV1_VERSION_0 + (blk_id == IMXDPUV1_ID_FETCHLAYER1) || +#endif + (blk_id == IMXDPUV1_ID_FETCHWARP2)) + if (get_channel_sub(chan) == IMXDPUV1_SUB_1) + return IMXDPUV1_TRUE; + return IMXDPUV1_FALSE; +} + +/*! + * Returns subindex of a channel + * + * @param channel + * + * @return returns the subindex of a channel + */ +static int imxdpuv1_get_channel_subindex(imxdpuv1_chan_t chan) +{ + switch (get_channel_sub(chan)) { + case IMXDPUV1_SUB_2: + return 1; + case IMXDPUV1_SUB_3: + return 2; + case IMXDPUV1_SUB_4: + return 3; + case IMXDPUV1_SUB_5: + return 4; + case IMXDPUV1_SUB_6: + return 5; + case IMXDPUV1_SUB_7: + return 6; + case IMXDPUV1_SUB_8: + return 7; + case IMXDPUV1_SUB_1: + case IMXDPUV1_SUBWINDOW_NONE: + default: + return 0; + } +} + +/*! + * Returns returns the eco channel for a channel index + * + * @param chan + * + * @return returns number of bits per pixel or zero + * if the format is not matched. + */ +imxdpuv1_chan_t imxdpuv1_get_eco(imxdpuv1_chan_t chan) +{ + switch (get_eco_idx(chan)) { + case get_channel_idx(IMXDPUV1_CHAN_28): + return IMXDPUV1_CHAN_28; + case get_channel_idx(IMXDPUV1_CHAN_29): + return IMXDPUV1_CHAN_29; + case get_channel_idx(IMXDPUV1_CHAN_30): + return IMXDPUV1_CHAN_30; + default: + return 0; + } +} +/*! + * Returns the start address offset for a given block ID + * + * @param block id + * + * @return This function returns the address offset if the block id + * matches a valid block. Otherwise, IMXDPUV1_OFFSET_INVALID + * is returned. + */ +uint32_t id2blockoffset(imxdpuv1_id_t block_id) +{ + switch (block_id) { + /*case IMXDPUV1_ID_NONE: return IMXDPUV1_NONE_LOCKUNLOCK; */ + case IMXDPUV1_ID_FETCHDECODE9: + return IMXDPUV1_FETCHDECODE9_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_FETCHPERSP9: + return IMXDPUV1_FETCHPERSP9_LOCKUNLOCK; +#else + case IMXDPUV1_ID_FETCHWARP9: + return IMXDPUV1_FETCHWARP9_LOCKUNLOCK; +#endif + case IMXDPUV1_ID_FETCHECO9: + return IMXDPUV1_FETCHECO9_LOCKUNLOCK; + case IMXDPUV1_ID_ROP9: + return IMXDPUV1_ROP9_LOCKUNLOCK; + case IMXDPUV1_ID_CLUT9: + return IMXDPUV1_CLUT9_LOCKUNLOCK; + case IMXDPUV1_ID_MATRIX9: + return IMXDPUV1_MATRIX9_LOCKUNLOCK; + case IMXDPUV1_ID_HSCALER9: + return IMXDPUV1_HSCALER9_LOCKUNLOCK; + case IMXDPUV1_ID_VSCALER9: + return IMXDPUV1_VSCALER9_LOCKUNLOCK; + case IMXDPUV1_ID_FILTER9: + return IMXDPUV1_FILTER9_LOCKUNLOCK; + case IMXDPUV1_ID_BLITBLEND9: + return IMXDPUV1_BLITBLEND9_LOCKUNLOCK; + case IMXDPUV1_ID_STORE9: + return IMXDPUV1_STORE9_LOCKUNLOCK; + case IMXDPUV1_ID_CONSTFRAME0: + return IMXDPUV1_CONSTFRAME0_LOCKUNLOCK; + case IMXDPUV1_ID_EXTDST0: + return IMXDPUV1_EXTDST0_LOCKUNLOCK; + case IMXDPUV1_ID_CONSTFRAME4: + return IMXDPUV1_CONSTFRAME4_LOCKUNLOCK; + case IMXDPUV1_ID_EXTDST4: + return IMXDPUV1_EXTDST4_LOCKUNLOCK; + case IMXDPUV1_ID_CONSTFRAME1: + return IMXDPUV1_CONSTFRAME1_LOCKUNLOCK; + case IMXDPUV1_ID_EXTDST1: + return IMXDPUV1_EXTDST1_LOCKUNLOCK; + case IMXDPUV1_ID_CONSTFRAME5: + return IMXDPUV1_CONSTFRAME5_LOCKUNLOCK; + case IMXDPUV1_ID_EXTDST5: + return IMXDPUV1_EXTDST5_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_EXTSRC4: + return IMXDPUV1_EXTSRC4_LOCKUNLOCK; + case IMXDPUV1_ID_STORE4: + return IMXDPUV1_STORE4_LOCKUNLOCK; + case IMXDPUV1_ID_EXTSRC5: + return IMXDPUV1_EXTSRC5_LOCKUNLOCK; + case IMXDPUV1_ID_STORE5: + return IMXDPUV1_STORE5_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHDECODE2: + return IMXDPUV1_FETCHDECODE2_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHDECODE3: + return IMXDPUV1_FETCHDECODE3_LOCKUNLOCK; +#endif + case IMXDPUV1_ID_FETCHWARP2: + return IMXDPUV1_FETCHWARP2_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHECO2: + return IMXDPUV1_FETCHECO2_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHDECODE0: + return IMXDPUV1_FETCHDECODE0_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHECO0: + return IMXDPUV1_FETCHECO0_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHDECODE1: + return IMXDPUV1_FETCHDECODE1_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHECO1: + return IMXDPUV1_FETCHECO1_LOCKUNLOCK; + case IMXDPUV1_ID_FETCHLAYER0: + return IMXDPUV1_FETCHLAYER0_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_FETCHLAYER1: + return IMXDPUV1_FETCHLAYER1_LOCKUNLOCK; + case IMXDPUV1_ID_GAMMACOR4: + return IMXDPUV1_GAMMACOR4_LOCKUNLOCK; +#endif + case IMXDPUV1_ID_MATRIX4: + return IMXDPUV1_MATRIX4_LOCKUNLOCK; + case IMXDPUV1_ID_HSCALER4: + return IMXDPUV1_HSCALER4_LOCKUNLOCK; + case IMXDPUV1_ID_VSCALER4: + return IMXDPUV1_VSCALER4_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_HISTOGRAM4: + return IMXDPUV1_HISTOGRAM4_CONTROL; + case IMXDPUV1_ID_GAMMACOR5: + return IMXDPUV1_GAMMACOR5_LOCKUNLOCK; +#endif + case IMXDPUV1_ID_MATRIX5: + return IMXDPUV1_MATRIX5_LOCKUNLOCK; + case IMXDPUV1_ID_HSCALER5: + return IMXDPUV1_HSCALER5_LOCKUNLOCK; + case IMXDPUV1_ID_VSCALER5: + return IMXDPUV1_VSCALER5_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_HISTOGRAM5: + return IMXDPUV1_HISTOGRAM5_CONTROL; +#endif + case IMXDPUV1_ID_LAYERBLEND0: + return IMXDPUV1_LAYERBLEND0_LOCKUNLOCK; + case IMXDPUV1_ID_LAYERBLEND1: + return IMXDPUV1_LAYERBLEND1_LOCKUNLOCK; + case IMXDPUV1_ID_LAYERBLEND2: + return IMXDPUV1_LAYERBLEND2_LOCKUNLOCK; + case IMXDPUV1_ID_LAYERBLEND3: + return IMXDPUV1_LAYERBLEND3_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_LAYERBLEND4: + return IMXDPUV1_LAYERBLEND4_LOCKUNLOCK; + case IMXDPUV1_ID_LAYERBLEND5: + return IMXDPUV1_LAYERBLEND5_LOCKUNLOCK; + case IMXDPUV1_ID_LAYERBLEND6: + return IMXDPUV1_LAYERBLEND6_LOCKUNLOCK; + case IMXDPUV1_ID_EXTSRC0: + return IMXDPUV1_EXTSRC0_LOCKUNLOCK; + case IMXDPUV1_ID_EXTSRC1: + return IMXDPUV1_EXTSRC1_LOCKUNLOCK; +#endif + case IMXDPUV1_ID_DISENGCFG: + return IMXDPUV1_DISENGCFG_LOCKUNLOCK0; + case IMXDPUV1_ID_FRAMEGEN0: + return IMXDPUV1_FRAMEGEN0_LOCKUNLOCK; + case IMXDPUV1_ID_MATRIX0: + return IMXDPUV1_MATRIX0_LOCKUNLOCK; + case IMXDPUV1_ID_GAMMACOR0: + return IMXDPUV1_GAMMACOR0_LOCKUNLOCK; + case IMXDPUV1_ID_DITHER0: + return IMXDPUV1_DITHER0_LOCKUNLOCK; + case IMXDPUV1_ID_TCON0: + return IMXDPUV1_TCON0_LOCKUNLOCK; + case IMXDPUV1_ID_SIG0: + return IMXDPUV1_SIG0_LOCKUNLOCK; + case IMXDPUV1_ID_FRAMEGEN1: + return IMXDPUV1_FRAMEGEN1_LOCKUNLOCK; + case IMXDPUV1_ID_MATRIX1: + return IMXDPUV1_MATRIX1_LOCKUNLOCK; + case IMXDPUV1_ID_GAMMACOR1: + return IMXDPUV1_GAMMACOR1_LOCKUNLOCK; + case IMXDPUV1_ID_DITHER1: + return IMXDPUV1_DITHER1_LOCKUNLOCK; + case IMXDPUV1_ID_TCON1: + return IMXDPUV1_TCON1_LOCKUNLOCK; + case IMXDPUV1_ID_SIG1: + return IMXDPUV1_SIG1_LOCKUNLOCK; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_FRAMECAP4: + return IMXDPUV1_FRAMECAP4_LOCKUNLOCK; + case IMXDPUV1_ID_FRAMECAP5: + return IMXDPUV1_FRAMECAP5_LOCKUNLOCK; +#endif + default: + return IMXDPUV1_OFFSET_INVALID; + } +} + +/*! + * Returns the start address offset for the dynamic configuraiton for + * a given block ID + * + * @param block id + * + * @return This function returns the address offset if the block id + * matches a valid block. Otherwise, IMXDPUV1_OFFSET_INVALID + * is returned. + */ +uint32_t id2dynamicoffset(imxdpuv1_id_t block_id) +{ + switch (block_id) { + case IMXDPUV1_ID_FETCHDECODE9: + return IMXDPUV1_PIXENGCFG_FETCHDECODE9_DYNAMIC; + +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_FETCHPERSP9: + return IMXDPUV1_PIXENGCFG_FETCHPERSP9_DYNAMIC; +#else + case IMXDPUV1_ID_FETCHWARP9: + return IMXDPUV1_PIXENGCFG_FETCHWARP9_DYNAMIC; +#endif + case IMXDPUV1_ID_ROP9: + return IMXDPUV1_PIXENGCFG_ROP9_DYNAMIC; + case IMXDPUV1_ID_CLUT9: + return IMXDPUV1_PIXENGCFG_CLUT9_DYNAMIC; + case IMXDPUV1_ID_MATRIX9: + return IMXDPUV1_PIXENGCFG_MATRIX9_DYNAMIC; + case IMXDPUV1_ID_HSCALER9: + return IMXDPUV1_PIXENGCFG_HSCALER9_DYNAMIC; + case IMXDPUV1_ID_VSCALER9: + return IMXDPUV1_PIXENGCFG_VSCALER9_DYNAMIC; + case IMXDPUV1_ID_FILTER9: + return IMXDPUV1_PIXENGCFG_FILTER9_DYNAMIC; + case IMXDPUV1_ID_BLITBLEND9: + return IMXDPUV1_PIXENGCFG_BLITBLEND9_DYNAMIC; + case IMXDPUV1_ID_STORE9: + return IMXDPUV1_PIXENGCFG_STORE9_DYNAMIC; + case IMXDPUV1_ID_EXTDST0: + return IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC; + case IMXDPUV1_ID_EXTDST4: + return IMXDPUV1_PIXENGCFG_EXTDST4_DYNAMIC; + case IMXDPUV1_ID_EXTDST1: + return IMXDPUV1_PIXENGCFG_EXTDST1_DYNAMIC; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_EXTDST5: + return IMXDPUV1_PIXENGCFG_EXTDST5_DYNAMIC; + case IMXDPUV1_ID_STORE4: + return IMXDPUV1_PIXENGCFG_STORE4_DYNAMIC; + case IMXDPUV1_ID_STORE5: + return IMXDPUV1_PIXENGCFG_STORE5_DYNAMIC; + case IMXDPUV1_ID_FETCHDECODE2: + return IMXDPUV1_PIXENGCFG_FETCHDECODE2_DYNAMIC; + case IMXDPUV1_ID_FETCHDECODE3: + return IMXDPUV1_PIXENGCFG_FETCHDECODE3_DYNAMIC; +#endif + case IMXDPUV1_ID_FETCHWARP2: + return IMXDPUV1_PIXENGCFG_FETCHWARP2_DYNAMIC; + case IMXDPUV1_ID_FETCHDECODE0: + return IMXDPUV1_PIXENGCFG_FETCHDECODE0_DYNAMIC; + case IMXDPUV1_ID_FETCHDECODE1: + return IMXDPUV1_PIXENGCFG_FETCHDECODE1_DYNAMIC; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_GAMMACOR4: + return IMXDPUV1_PIXENGCFG_GAMMACOR4_DYNAMIC; +#endif + case IMXDPUV1_ID_MATRIX4: + return IMXDPUV1_PIXENGCFG_MATRIX4_DYNAMIC; + case IMXDPUV1_ID_HSCALER4: + return IMXDPUV1_PIXENGCFG_HSCALER4_DYNAMIC; + case IMXDPUV1_ID_VSCALER4: + return IMXDPUV1_PIXENGCFG_VSCALER4_DYNAMIC; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_HISTOGRAM4: + return IMXDPUV1_PIXENGCFG_HISTOGRAM4_DYNAMIC; + case IMXDPUV1_ID_GAMMACOR5: + return IMXDPUV1_PIXENGCFG_GAMMACOR5_DYNAMIC; +#endif + case IMXDPUV1_ID_MATRIX5: + return IMXDPUV1_PIXENGCFG_MATRIX5_DYNAMIC; + case IMXDPUV1_ID_HSCALER5: + return IMXDPUV1_PIXENGCFG_HSCALER5_DYNAMIC; + case IMXDPUV1_ID_VSCALER5: + return IMXDPUV1_PIXENGCFG_VSCALER5_DYNAMIC; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_HISTOGRAM5: + return IMXDPUV1_PIXENGCFG_HISTOGRAM5_DYNAMIC; +#endif + case IMXDPUV1_ID_LAYERBLEND0: + return IMXDPUV1_PIXENGCFG_LAYERBLEND0_DYNAMIC; + case IMXDPUV1_ID_LAYERBLEND1: + return IMXDPUV1_PIXENGCFG_LAYERBLEND1_DYNAMIC; + case IMXDPUV1_ID_LAYERBLEND2: + return IMXDPUV1_PIXENGCFG_LAYERBLEND2_DYNAMIC; + case IMXDPUV1_ID_LAYERBLEND3: + return IMXDPUV1_PIXENGCFG_LAYERBLEND3_DYNAMIC; +#ifdef IMXDPUV1_VERSION_0 + case IMXDPUV1_ID_LAYERBLEND4: + return IMXDPUV1_PIXENGCFG_LAYERBLEND4_DYNAMIC; + case IMXDPUV1_ID_LAYERBLEND5: + return IMXDPUV1_PIXENGCFG_LAYERBLEND5_DYNAMIC; + case IMXDPUV1_ID_LAYERBLEND6: + return IMXDPUV1_PIXENGCFG_LAYERBLEND6_DYNAMIC; +#endif + default: + return IMXDPUV1_OFFSET_INVALID; + } +} + +/*! + * Returns the start address offset for a given shadow index + * + * @param block id + * + * @return This function returns the address offset if the shadow + * index matches a valid block. Otherwise, IMXDPUV1_OFFSET_INVALID + * is returned. + */ +imxdpuv1_chan_t shadowindex2channel(imxdpuv1_shadow_load_index_t shadow_index) +{ + switch (shadow_index) { + case IMXDPUV1_SHDLD_IDX_CHAN_00: + return IMXDPUV1_CHAN_00; + case IMXDPUV1_SHDLD_IDX_CHAN_01: + return IMXDPUV1_CHAN_01; + case IMXDPUV1_SHDLD_IDX_CHAN_02: + return IMXDPUV1_CHAN_02; + case IMXDPUV1_SHDLD_IDX_CHAN_03: + return IMXDPUV1_CHAN_03; + case IMXDPUV1_SHDLD_IDX_CHAN_04: + return IMXDPUV1_CHAN_04; + case IMXDPUV1_SHDLD_IDX_CHAN_05: + return IMXDPUV1_CHAN_05; + case IMXDPUV1_SHDLD_IDX_CHAN_06: + return IMXDPUV1_CHAN_06; + case IMXDPUV1_SHDLD_IDX_CHAN_07: + return IMXDPUV1_CHAN_07; + case IMXDPUV1_SHDLD_IDX_CHAN_08: + return IMXDPUV1_CHAN_08; + case IMXDPUV1_SHDLD_IDX_CHAN_09: + return IMXDPUV1_CHAN_09; + case IMXDPUV1_SHDLD_IDX_CHAN_10: + return IMXDPUV1_CHAN_10; + case IMXDPUV1_SHDLD_IDX_CHAN_11: + return IMXDPUV1_CHAN_11; + case IMXDPUV1_SHDLD_IDX_CHAN_12: + return IMXDPUV1_CHAN_12; + case IMXDPUV1_SHDLD_IDX_CHAN_13: + return IMXDPUV1_CHAN_13; + case IMXDPUV1_SHDLD_IDX_CHAN_14: + return IMXDPUV1_CHAN_14; + case IMXDPUV1_SHDLD_IDX_CHAN_15: + return IMXDPUV1_CHAN_15; + case IMXDPUV1_SHDLD_IDX_CHAN_16: + return IMXDPUV1_CHAN_16; + case IMXDPUV1_SHDLD_IDX_CHAN_17: + return IMXDPUV1_CHAN_17; + case IMXDPUV1_SHDLD_IDX_CHAN_18: + return IMXDPUV1_CHAN_18; + case IMXDPUV1_SHDLD_IDX_CHAN_19: + return IMXDPUV1_CHAN_19; + case IMXDPUV1_SHDLD_IDX_CHAN_20: + return IMXDPUV1_CHAN_20; + case IMXDPUV1_SHDLD_IDX_CHAN_21: + return IMXDPUV1_CHAN_21; + case IMXDPUV1_SHDLD_IDX_CHAN_22: + return IMXDPUV1_CHAN_22; + case IMXDPUV1_SHDLD_IDX_CHAN_23: + return IMXDPUV1_CHAN_23; + case IMXDPUV1_SHDLD_IDX_CHAN_24: + return IMXDPUV1_CHAN_24; + case IMXDPUV1_SHDLD_IDX_CHAN_25: + return IMXDPUV1_CHAN_25; + case IMXDPUV1_SHDLD_IDX_CHAN_26: + return IMXDPUV1_CHAN_26; + case IMXDPUV1_SHDLD_IDX_CHAN_27: + return IMXDPUV1_CHAN_27; + case IMXDPUV1_SHDLD_IDX_CHAN_28: + return IMXDPUV1_CHAN_28; + case IMXDPUV1_SHDLD_IDX_CHAN_29: + return IMXDPUV1_CHAN_29; + case IMXDPUV1_SHDLD_IDX_CHAN_30: + return IMXDPUV1_CHAN_30; + default: + return IMXDPUV1_CHANNEL_INVALID; + } +} + + +/*! + * This function returns the pointer to the imxdpu structutre + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * + * @return This function returns the pointer to the imxdpu structutre + * return a NULL pointer for a failure. + */ +struct imxdpuv1_soc *imxdpuv1_get_soc(int8_t imxdpuv1_id) +{ + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return NULL; + } + return &(imxdpuv1_array[imxdpuv1_id]); +} + +/*! + * This function enables the interrupt for the specified interrupt line. + * The interrupt lines are defined in imxdpuv1_events.h. + * + * @param imxdpu imxdpu instance + * @param irq Interrupt line to enable interrupt for. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_enable_irq(int8_t imxdpuv1_id, uint32_t irq) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + +#ifdef DEBUG_IMXDPUV1_IRQ_ERROR + if (irq == 0) + panic("Trying to enable irq 0!"); +#endif + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpuv1_clear_irq(imxdpuv1_id, irq); + if (irq < IMXDPUV1_INTERRUPT_MAX) { + if (irq < 32) { + imxdpu->enabled_int[0] |= INTSTAT0_BIT(irq); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE0, + imxdpu->enabled_int[0]); + } else if (irq < 64) { + imxdpu->enabled_int[1] |= INTSTAT1_BIT(irq); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE1, + imxdpu->enabled_int[1]); +#ifdef IMXDPUV1_VERSION_0 + } else { + imxdpu->enabled_int[2] |= INTSTAT2_BIT(irq); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE2, + imxdpu->enabled_int[2]); +#endif + } + } else { + return -EINVAL; + } + + return ret; +} + +/*! + * This function disables the interrupt for the specified interrupt line.g + * The interrupt lines are defined in imxdpuv1_events.h. + * + * @param imxdpu imxdpu instance + * @param irq Interrupt line to disable interrupt for. + * + */ +int imxdpuv1_disable_irq(int8_t imxdpuv1_id, uint32_t irq) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (irq < IMXDPUV1_INTERRUPT_MAX) { + if (irq < 32) { + imxdpu->enabled_int[0] &= ~INTSTAT0_BIT(irq); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE0, + imxdpu->enabled_int[0]); + } else if (irq < 64) { + imxdpu->enabled_int[1] &= ~INTSTAT1_BIT(irq); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE1, + imxdpu->enabled_int[1]); +#ifdef IMXDPUV1_VERSION_0 + } else { + imxdpu->enabled_int[2] &= ~INTSTAT2_BIT(irq); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE2, + imxdpu->enabled_int[2]); +#endif + } + } else { + return -EINVAL; + } + + return ret; +} + +/*! + * This function clears all interrupts. + * + * @param imxdpu imxdpu instance + * + */ +int imxdpuv1_clear_all_irqs(int8_t imxdpuv1_id) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR0, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR0_USERINTERRUPTCLEAR0_MASK); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR1, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR1_USERINTERRUPTCLEAR1_MASK); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR2, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR2_USERINTERRUPTCLEAR2_MASK); +#endif +#if 1 + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_INTERRUPTCLEAR0, + IMXDPUV1_COMCTRL_INTERRUPTCLEAR0_INTERRUPTCLEAR0_MASK); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_INTERRUPTCLEAR1, + IMXDPUV1_COMCTRL_INTERRUPTCLEAR1_INTERRUPTCLEAR1_MASK); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_INTERRUPTCLEAR2, + IMXDPUV1_COMCTRL_INTERRUPTCLEAR2_INTERRUPTCLEAR2_MASK); +#endif +#endif + return ret; +} + +/*! + * This function disables all interrupts. + * + * @param imxdpu imxdpu instance + * + */ +int imxdpuv1_disable_all_irqs(int8_t imxdpuv1_id) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpuv1_write_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTENABLE0, 0); + imxdpuv1_write_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTENABLE1, 0); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTENABLE2, 0); +#endif + +#if 1 + imxdpuv1_write_irq(imxdpu, IMXDPUV1_COMCTRL_INTERRUPTENABLE0, 0); + imxdpuv1_write_irq(imxdpu, IMXDPUV1_COMCTRL_INTERRUPTENABLE1, 0); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write_irq(imxdpu, IMXDPUV1_COMCTRL_INTERRUPTENABLE2, 0); +#endif +#endif + + imxdpu->enabled_int[0] = 0; + imxdpu->enabled_int[1] = 0; +#ifdef IMXDPUV1_VERSION_0 + imxdpu->enabled_int[2] = 0; +#endif + return ret; +} + +/*! + * This function clears the interrupt for the specified interrupt line. + * The interrupt lines are defined in ipu_irq_line enum. + * + * @param imxdpu imxdpu instance + * @param irq Interrupt line to clear interrupt for. + * + */ +int imxdpuv1_clear_irq(int8_t imxdpuv1_id, uint32_t irq) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (irq < IMXDPUV1_INTERRUPT_MAX) { + if (irq < 32) { + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR0, + 1U << irq); + } + if (irq < 64) { + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR1, + 1U << (irq - 32)); +#ifdef IMXDPUV1_VERSION_0 + } else { + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR2, + 1U << (irq - 64)); +#endif + } + } else { + return -EINVAL; + } + + return ret; +} + +/*! + * This function initializes the imxdpu interrupts + * + * @param imxdpu imxdpu instance + * + */ +int imxdpuv1_init_irqs(int8_t imxdpuv1_id) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpuv1_disable_all_irqs(imxdpuv1_id); + imxdpuv1_clear_all_irqs(imxdpuv1_id); + + /* Set all irq to user mode */ + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK0, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK0_USERINTERRUPTMASK0_MASK); + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK1, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK1_USERINTERRUPTMASK1_MASK); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK2, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK2_USERINTERRUPTMASK2_MASK); +#endif + /* enable needed interupts */ + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_EXTDST0_SHDLOAD_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_EXTDST1_SHDLOAD_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ); + +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_STORE4_SHDLOAD_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_STORE5_SHDLOAD_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_STORE4_SEQCOMPLETE_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_STORE5_SEQCOMPLETE_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_STORE4_FRAMECOMPLETE_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_STORE5_FRAMECOMPLETE_IRQ); +#endif + /* enable the frame interrupts as IMXDPUV1_IRQF_ONESHOT */ + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_FRAMEGEN0_INT0_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_FRAMEGEN1_INT0_IRQ); + + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_COMCTRL_SW0_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_COMCTRL_SW1_IRQ); + + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_DISENGCFG_SHDLOAD0_IRQ); + imxdpuv1_enable_irq(imxdpuv1_id, IMXDPUV1_DISENGCFG_SHDLOAD1_IRQ); + + IMXDPUV1_TRACE("%s() enabled_int[0] 0x%08x\n", __func__, + imxdpu->enabled_int[0]); + IMXDPUV1_TRACE("%s() enabled_int[1] 0x%08x\n", __func__, + imxdpu->enabled_int[1]); +#ifdef IMXDPUV1_VERSION_0 + IMXDPUV1_TRACE("%s() enabled_int[2] 0x%08x\n", __func__, + imxdpu->enabled_int[2]); +#endif + return ret; +} + +/*! + * This function checks pending shadow loads + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_check_shadow_loads(int8_t imxdpuv1_id, int8_t disp) +{ + int ret = 0; + uint32_t addr_extdst = IMXDPUV1_OFFSET_INVALID; /* address for extdst */ + uint32_t extdst = 0; + uint32_t extdst_stat = 0; + uint32_t fgen = 1; + uint32_t fgen_stat = 0; + uint32_t sub = 0; + uint32_t sub_stat = 0; + uint32_t stat; + + int32_t i; + + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + stat = imxdpuv1_read_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTSTATUS0); + if (disp == 0) { + addr_extdst = IMXDPUV1_PIXENGCFG_EXTDST0_REQUEST; + if (stat & IMXDPUV1_DISENGCFG_SHDLOAD0_IRQ) { + fgen = 0; + } + } else if (disp == 1) { + addr_extdst = IMXDPUV1_PIXENGCFG_EXTDST1_REQUEST; + if (stat & IMXDPUV1_DISENGCFG_SHDLOAD1_IRQ) { + fgen = 0; + } + } else { + return -EINVAL; + } + + sub |= (imxdpuv1_read(imxdpu, IMXDPUV1_FETCHLAYER0_TRIGGERENABLE)) & 0xff; +#ifdef IMXDPUV1_VERSION_0 + sub |= (imxdpuv1_read(imxdpu, IMXDPUV1_FETCHLAYER1_TRIGGERENABLE) << 8) & 0xff00; +#endif + sub |= (imxdpuv1_read(imxdpu, IMXDPUV1_FETCHWARP2_TRIGGERENABLE) << 16) & 0xff0000; + extdst = imxdpuv1_read(imxdpu, addr_extdst); + + /* this loop may need to be optimized */ + for (i = 0; i < IMXDPUV1_SHDLD_IDX_CHAN_00; i++) { + if (imxdpu->shadow_load_state[disp][i].state.complete) { + if (imxdpu->shadow_load_state[disp][i].state.trys > 0) { + IMXDPUV1_TRACE_IRQ + ("shadow index complete after retry: index %d trys %d\n", + i, + imxdpu->shadow_load_state[disp][i]. + state.trys); + } else { + IMXDPUV1_TRACE_IRQ("shadow index complete: index %d\n", i); + } + imxdpu->shadow_load_state[disp][i].word = 0; + } else if (imxdpu->shadow_load_state[disp][i].state.processing) { + if (i > IMXDPUV1_SHDLD_IDX_CONST1) { + if (!(extdst & trigger_list[i].extdst) && !fgen) { + imxdpu->shadow_load_state[disp][i]. + state.complete = 1; + } else { + extdst_stat |= trigger_list[i].extdst; + fgen_stat |= 1 << i; + } + } else if (!(extdst & trigger_list[i].extdst)) { + imxdpu->shadow_load_state[disp][i]. + state.complete = 1; + } else { + imxdpu->shadow_load_state[disp][i].state.trys++; + extdst |= trigger_list[i].extdst; + IMXDPUV1_TRACE_IRQ + ("shadow index retry: index %d trys %d\n", + i, + imxdpu->shadow_load_state[disp][i]. + state.trys); + } + } + } + + + for (i = IMXDPUV1_SHDLD_IDX_CHAN_00; i < IMXDPUV1_SHDLD_IDX_MAX; i++) { + if (imxdpu->shadow_load_state[disp][i].state.complete) { + + if (imxdpu->shadow_load_state[disp][i].state.trys > 0) { + IMXDPUV1_TRACE_IRQ + ("shadow index complete after retry: index %d trys %d\n", + i, + imxdpu->shadow_load_state[disp][i]. + state.trys); + } else { + IMXDPUV1_TRACE_IRQ("shadow index complete: index %d\n", i); + } + imxdpu->shadow_load_state[disp][i].word = 0; + } else if (imxdpu->shadow_load_state[disp][i].state.processing) { + /* fetch layer and fetchwarp */ + if ((trigger_list[i].extdst != 0) && + (trigger_list[i].sub != 0)) { + if (!(extdst & trigger_list[i].extdst) && + !(sub & trigger_list[i].sub)) { + imxdpu->shadow_load_state[disp][i]. + state.complete = 1; + } else { + extdst_stat |= trigger_list[i].extdst; + sub_stat |= trigger_list[i].sub; + } + } else if (!(extdst & trigger_list[i].extdst)) { + imxdpu->shadow_load_state[disp][i]. + state.complete = 1; + } else { + imxdpu->shadow_load_state[disp][i].state.trys++; + extdst_stat |= trigger_list[i].extdst; + IMXDPUV1_TRACE_IRQ + ("shadow index retry: index %d trys %d\n", + i, + imxdpu->shadow_load_state[disp][i]. + state.trys); + } + } + } + + if ((extdst_stat == 0) && (sub_stat == 0) && (fgen_stat == 0)) { + /* clear interrupt */ + IMXDPUV1_TRACE_IRQ("shadow requests are complete.\n"); + } else { + IMXDPUV1_TRACE_IRQ + ("shadow requests are not complete: extdst 0x%08x, sub 0x%08x, fgen 0x%08x\n", + extdst, sub, fgen); + IMXDPUV1_TRACE_IRQ + ("shadow requests are not complete: extdst_stat 0x%08x, sub_stat 0x%08x, fgen_stat 0x%08x\n", + extdst_stat, sub_stat, fgen_stat); + } + + return ret; +} + +/*! + * This function starts pending shadow loads + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +static int imxdpuv1_disp_start_shadow_loads(int8_t imxdpuv1_id, int8_t disp) +{ + int ret = 0; + uint32_t addr_extdst; /* address for extdst */ + uint32_t addr_fgen; /* address for frame generator */ + uint32_t extdst = 0; + uint32_t fgen = 0; + uint32_t sub = 0; + int32_t i; + + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (disp == 0) { + addr_fgen = IMXDPUV1_FRAMEGEN0_FGSLR; + addr_extdst = IMXDPUV1_PIXENGCFG_EXTDST0_REQUEST; + + } else if (disp == 1) { + addr_fgen = IMXDPUV1_FRAMEGEN1_FGSLR; + addr_extdst = IMXDPUV1_PIXENGCFG_EXTDST1_REQUEST; + } else { + return -EINVAL; + } + + /* this loop may need to be optimized */ + for (i = 0; i < IMXDPUV1_SHDLD_IDX_CHAN_00; i++) { + if (imxdpu->shadow_load_state[disp][i].state.request && + (imxdpu->shadow_load_state[disp][i].state.processing == 0)) { + imxdpu->shadow_load_state[disp][i].state.processing = 1; + extdst |= trigger_list[i].extdst; + /* only trigger frame generator for const frames*/ + if (i >= IMXDPUV1_SHDLD_IDX_CONST0) { + fgen |= 1; + } + } + } + for (i = IMXDPUV1_SHDLD_IDX_CHAN_00; i < IMXDPUV1_SHDLD_IDX_MAX; i++) { + if (imxdpu->shadow_load_state[disp][i].state.request && + (imxdpu->shadow_load_state[disp][i].state.processing == 0)) { + imxdpu->shadow_load_state[disp][i].state.processing = 1; + /*todo: need a completion handler */ + extdst |= trigger_list[i].extdst; + sub |= trigger_list[i].sub; + } + } + + if (sub) { + IMXDPUV1_TRACE_IRQ("Fetch layer shadow request 0x%08x\n", sub); + if (sub & 0xff) { /* FETCHLAYER0 */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_TRIGGERENABLE, + sub & 0xff); + } +#ifdef IMXDPUV1_VERSION_0 + if (sub & 0xff00) { /* FETCHLAYER1 */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_TRIGGERENABLE, + (sub >> 8) & 0xff); + } +#endif + if (sub & 0xff0000) { /* FETCHWARP2 */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_TRIGGERENABLE, + (sub >> 16) & 0xff); + } + } + + if (extdst) { + IMXDPUV1_TRACE_IRQ("Extdst shadow request 0x%08x\n", extdst); + imxdpuv1_write(imxdpu, addr_extdst, extdst); + } + + if (fgen) { + IMXDPUV1_TRACE_IRQ("Fgen shadow request 0x%08x\n", fgen); + imxdpuv1_write(imxdpu, addr_fgen, fgen); + } + + return ret; +} + +/*! + * This function handles the VYNC interrupt for a display + * + * @param imxdpu imxdpu instance + * @param disp display index + * + */ +static void imxdpuv1_disp_vsync_handler(int8_t imxdpuv1_id, int8_t disp) +{ + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return; + } + if (!((disp == 0) || (disp == 1))) + return; + + /* send notifications + shadow load finished + */ + + imxdpuv1_disp_start_shadow_loads(imxdpuv1_id, disp); + imxdpuv1_disp_update_fgen_status(imxdpuv1_id, disp); + + return; + +} + +/*! + * This function calls a register handler for an interrupt + * + * @param imxdpu imxdpu instance + * @param irq interrupt line + * + */ +static void imxdpuv1_handle_registered_irq(int8_t imxdpuv1_id, int8_t irq) +{ + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if ((irq < 0) || (irq >= IMXDPUV1_INTERRUPT_MAX)) + return; + + if (imxdpu->irq_list[irq].handler == NULL) + return; + + imxdpu->irq_list[irq].handler(irq, imxdpu->irq_list[irq].data); + + if ((imxdpu->irq_list[irq].flags & IMXDPUV1_IRQF_ONESHOT) != 0) { + imxdpuv1_disable_irq(imxdpuv1_id, irq); + imxdpuv1_clear_irq(imxdpuv1_id, irq); + } + return; + +} + +/* todo: this irq handler assumes all irq are ORed together. + The irqs may be grouped so this function can be + optimized if that is the case*/ +/*! + * This function processes all IRQs for the IMXDPU + * + * @param data pointer to the imxdpu structure + * + */ +int imxdpuv1_handle_irq(int32_t imxdpuv1_id) +{ + uint32_t int_stat[3]; + uint32_t int_temp[3]; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + IMXDPUV1_TRACE_IRQ("%s(): invalid imxdpuv1_id\n", __func__); +#ifdef DEBUG_IMXDPUV1_IRQ_ERROR + panic("wrong imxdpuv1_id"); +#endif + return IMXDPUV1_FALSE; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpu->irq_count++; + +#ifdef DEBUG_IMXDPUV1_IRQ_ERROR + { + uint32_t int_enable0; + int_enable0 = imxdpuv1_read_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE0); + if (int_enable0 & 1) { + panic("IRQ0 enabled\n"); + } + if (imxdpu->enabled_int[0] & 1) { + panic("IRQ0 in enabled_int is set\n"); + } + } +#endif + /* Get and clear interrupt status */ + int_temp[0] = + imxdpuv1_read_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTSTATUS0); + int_stat[0] = imxdpu->enabled_int[0] & int_temp[0]; + int_temp[1] = + imxdpuv1_read_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTSTATUS1); + int_stat[1] = imxdpu->enabled_int[1] & int_temp[1]; +#ifdef IMXDPUV1_VERSION_0 +#ifdef IMXDPUV1_ENABLE_INTSTAT2 + /* Enable this (IMXDPUV1_ENABLE_INTSTAT2) if intstat2 interrupts + are needed */ + int_temp[2] = + imxdpuv1_read_irq(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTSTATUS2); + int_stat[2] = imxdpu->enabled_int[2] & int_temp[2]; +#endif +#endif + /* No interrupts are pending */ + if ((int_temp[0] == 0) && (int_temp[1] == 0) +#ifdef IMXDPUV1_VERSION_0 +#ifdef IMXDPUV1_ENABLE_INTSTAT2 + && (int_temp[2] == 0) +#endif +#endif + ) { + } + + /* No enabled interrupts are pending */ + if ((int_stat[0] == 0) && (int_stat[1] == 0) +#ifdef IMXDPUV1_ENABLE_INTSTAT2 + && (int_stat[2] == 0) +#endif + ) { + IMXDPUV1_TRACE_IRQ + ("Error: No enabled interrupts, 0x%08x 0x%08x\n", + int_temp[0] & ~imxdpu->enabled_int[0], + int_temp[1] & ~imxdpu->enabled_int[1]); +#ifdef DEBUG_IMXDPUV1_IRQ_ERROR + panic("no enabled IMXDPU interrupts"); +#endif + + return IMXDPUV1_FALSE; + } + + /* Clear the enabled interrupts */ + if (int_stat[0]) { + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR0, + int_stat[0]); + } + if (int_stat[1]) { + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR1, + int_stat[1]); + } +#ifdef IMXDPUV1_ENABLE_INTSTAT2 + if (int_stat[2]) { + imxdpuv1_write_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTCLEAR2, + int_stat[2]); + } +#endif + +#ifdef IMXDPUV1_ENABLE_INTSTAT2 + if (int_stat[1] != 0) { + /* add int_stat[2] if needed */ + } +#endif +#ifdef IMXDPUV1_VERSION_0 + /* now handle the interrupts that are pending */ + if (int_stat[0] != 0) { + if (int_stat[0] & 0xff) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE9_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_STORE9_SHDLOAD_IRQ irq\n"); + imxdpuv1_be_irq_handler(imxdpuv1_id, + IMXDPUV1_STORE9_SHDLOAD_IRQ); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE9_SHDLOAD_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ irq\n"); + imxdpuv1_be_irq_handler(imxdpuv1_id, + IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE9_SEQCOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_STORE9_SEQCOMPLETE_IRQ irq\n"); + imxdpuv1_be_irq_handler(imxdpuv1_id, + IMXDPUV1_STORE9_SEQCOMPLETE_IRQ); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE9_SEQCOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_EXTDST0_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_EXTDST0_SHDLOAD_IRQ irq\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST0_SHDLOAD_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ\n"); + /* todo: move */ + imxdpuv1_disp_check_shadow_loads(imxdpuv1_id, 0); + + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ); + } + } + if (int_stat[0] & 0xff00) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_EXTDST1_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_EXTDST1_SHDLOAD_IRQ irq\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST1_SHDLOAD_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT( + IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ\n"); + /* todo: move */ + imxdpuv1_disp_check_shadow_loads(imxdpuv1_id, 1); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE4_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ_CAPTURE("IMXDPUV1_STORE4_SHDLOAD_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE4_SHDLOAD_IRQ); + } + } + if (int_stat[0] & 0xff0000) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE4_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ_CAPTURE( + "IMXDPUV1_STORE4_FRAMECOMPLETE_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE4_FRAMECOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE4_SEQCOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ_CAPTURE( + "IMXDPUV1_STORE4_SEQCOMPLETE_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE4_SEQCOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_HISTOGRAM4_VALID_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_HISTOGRAM4_VALID_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_HISTOGRAM4_VALID_IRQ); + } + } + if (int_stat[0] & 0xff000000) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_HISTOGRAM5_VALID_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_HISTOGRAM5_VALID_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_HISTOGRAM5_VALID_IRQ); + } + if (int_stat[1] & + INTSTAT0_BIT(IMXDPUV1_DISENGCFG_SHDLOAD0_IRQ)) { + IMXDPUV1_PRINT + ("IMXDPUV1_DISENGCFG_SHDLOAD0_IRQ irq\n"); + imxdpuv1_disp_check_shadow_loads(imxdpuv1_id, 0); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_DISENGCFG_SHDLOAD0_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_DISENGCFG_FRAMECOMPLETE0_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_DISENGCFG_FRAMECOMPLETE0_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_DISENGCFG_FRAMECOMPLETE0_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_FRAMEGEN0_INT0_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_FRAMEGEN0_INT0_IRQ\n"); + imxdpuv1_disp_vsync_handler(imxdpuv1_id, 0); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_FRAMEGEN0_INT0_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_FRAMEGEN0_INT1_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_FRAMEGEN0_INT1_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_FRAMEGEN0_INT1_IRQ); + } + } + } + + if (int_stat[1] != 0) { + if (int_stat[1] & 0xff) { + + } + if (int_stat[1] & 0xff00) { + if (int_stat[1] & + INTSTAT1_BIT(IMXDPUV1_FRAMEGEN1_INT0_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_FRAMEGEN1_INT0_IRQ\n"); + imxdpuv1_disp_vsync_handler(imxdpuv1_id, 1); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_FRAMEGEN1_INT0_IRQ); + } + } + if (int_stat[0] & 0xff0000) { + if (int_stat[0] & + INTSTAT1_BIT(IMXDPUV1_COMCTRL_SW0_IRQ)) { + IMXDPUV1_TRACE_IRQ("IMXDPUV1_COMCTRL_SW0_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_COMCTRL_SW0_IRQ); + } + if (int_stat[1] & INTSTAT1_BIT(IMXDPUV1_COMCTRL_SW2_IRQ)) { + IMXDPUV1_TRACE_IRQ("IMXDPUV1_COMCTRL_SW2_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_COMCTRL_SW2_IRQ); + } + if (int_stat[1] & INTSTAT1_BIT(IMXDPUV1_COMCTRL_SW3_IRQ)) { + IMXDPUV1_TRACE_IRQ("IMXDPUV1_COMCTRL_SW3_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_COMCTRL_SW3_IRQ); + } + + } + } +#else + /* now handle the interrupts that are pending */ + if (int_stat[0] != 0) { + if (int_stat[0] & 0xff) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE9_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_STORE9_SHDLOAD_IRQ irq\n"); + imxdpuv1_be_irq_handler(imxdpuv1_id, + IMXDPUV1_STORE9_SHDLOAD_IRQ); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE9_SHDLOAD_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ irq\n"); + imxdpuv1_be_irq_handler(imxdpuv1_id, + IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE9_FRAMECOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_STORE9_SEQCOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_STORE9_SEQCOMPLETE_IRQ irq\n"); + imxdpuv1_be_irq_handler(imxdpuv1_id, + IMXDPUV1_STORE9_SEQCOMPLETE_IRQ); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_STORE9_SEQCOMPLETE_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_EXTDST0_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_EXTDST0_SHDLOAD_IRQ irq\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST0_SHDLOAD_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ + ("IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ\n"); + /* todo: move */ + imxdpuv1_disp_check_shadow_loads(imxdpuv1_id, 0); + + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST0_FRAMECOMPLETE_IRQ); + } + } + if (int_stat[0] & 0xff00) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_EXTDST1_SHDLOAD_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_EXTDST1_SHDLOAD_IRQ irq\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST1_SHDLOAD_IRQ); + } + if (int_stat[0] & + INTSTAT0_BIT( + IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ\n"); + /* todo: move */ + imxdpuv1_disp_check_shadow_loads(imxdpuv1_id, 1); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_EXTDST1_FRAMECOMPLETE_IRQ); + } + } + if (int_stat[0] & 0xff0000) { + if (int_stat[0] & + INTSTAT0_BIT(IMXDPUV1_FRAMEGEN0_INT0_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_FRAMEGEN0_INT0_IRQ\n"); + imxdpuv1_disp_vsync_handler(imxdpuv1_id, 0); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_FRAMEGEN0_INT0_IRQ); + } + + } + if (int_stat[0] & 0xff000000) { + if (int_stat[1] & + INTSTAT0_BIT(IMXDPUV1_FRAMEGEN1_INT0_IRQ)) { + IMXDPUV1_TRACE_IRQ( + "IMXDPUV1_FRAMEGEN1_INT0_IRQ\n"); + imxdpuv1_disp_vsync_handler(imxdpuv1_id, 1); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_FRAMEGEN1_INT0_IRQ); + } + } + } + + if (int_stat[1] != 0) { + if (int_stat[1] & 0xff) { + if (int_stat[0] & + INTSTAT1_BIT(IMXDPUV1_COMCTRL_SW0_IRQ)) { + IMXDPUV1_TRACE_IRQ("IMXDPUV1_COMCTRL_SW0_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_COMCTRL_SW0_IRQ); + } + if (int_stat[1] & INTSTAT1_BIT(IMXDPUV1_COMCTRL_SW2_IRQ)) { + IMXDPUV1_TRACE_IRQ("IMXDPUV1_COMCTRL_SW2_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_COMCTRL_SW2_IRQ); + } + } + if (int_stat[1] & 0xff00) { + if (int_stat[1] & INTSTAT1_BIT(IMXDPUV1_COMCTRL_SW3_IRQ)) { + IMXDPUV1_TRACE_IRQ("IMXDPUV1_COMCTRL_SW3_IRQ\n"); + imxdpuv1_handle_registered_irq(imxdpuv1_id, + IMXDPUV1_COMCTRL_SW3_IRQ); + } + } + if (int_stat[0] & 0xff0000) { + /* Reserved for command sequencer debug */ + } + } +#endif + return IMXDPUV1_TRUE; +} + +/*! + * This function registers an interrupt handler function for the specified + * irq line. The interrupt lines are defined in imxdpuv1_events.h + * + * @param imxdpu imxdpu instance + * @param irq Interrupt line to get status for. + * + * @param handler Input parameter for address of the handler + * function. + * + * @param irq_flags Flags for interrupt mode. Currently not used. + * + * @param devname Input parameter for string name of driver + * registering the handler. + * + * @param data Input parameter for pointer of data to be + * passed to the handler. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_request_irq(int8_t imxdpuv1_id, + uint32_t irq, + int (*handler)(int, void *), + uint32_t irq_flags, const char *devname, void *data) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (imxdpu->irq_list[irq].handler != NULL) { + IMXDPUV1_TRACE("handler already installed on irq %d\n", irq); + ret = -EINVAL; + goto out; + } + + imxdpu->irq_list[irq].handler = handler; + imxdpu->irq_list[irq].flags = irq_flags; + imxdpu->irq_list[irq].data = data; + imxdpu->irq_list[irq].name = devname; + + /* Clear and enable the IRQ */ + imxdpuv1_clear_irq(imxdpuv1_id, irq); + /* Don't enable if a one shot */ + if ((imxdpu->irq_list[irq].flags & IMXDPUV1_IRQF_ONESHOT) == 0) + imxdpuv1_enable_irq(imxdpuv1_id, irq); +out: + return ret; +} + +/*! + * This function unregisters an interrupt handler for the specified interrupt + * line. The interrupt lines are defined in imxdpuv1_events.h + * + * @param imxdpu imxdpu instance + * @param irq Interrupt line to get status for. + * + * @param data Input parameter for pointer of data to be passed + * to the handler. This must match value passed to + * ipu_request_irq(). + * + */ +int imxdpuv1_free_irq(int8_t imxdpuv1_id, uint32_t irq, void *data) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpuv1_disable_irq(imxdpuv1_id, irq); + imxdpuv1_clear_irq(imxdpuv1_id, irq); + if (imxdpu->irq_list[irq].data == data) + memset(&imxdpu->irq_list[irq], 0, sizeof(imxdpu->irq_list[irq])); + + return ret; +} + +/*! + * This function un-initializes the imxdpu interrupts + * + * @param imxdpu imxdpu instance + * + */ +int imxdpuv1_uninit_interrupts(int8_t imxdpuv1_id) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + imxdpu->enabled_int[0] = 0; + imxdpu->enabled_int[1] = 0; +#ifdef IMXDPUV1_VERSION_0 + imxdpu->enabled_int[2] = 0; +#endif + imxdpuv1_clear_all_irqs(imxdpuv1_id); + + /* Set all interrupt to user mode */ + imxdpuv1_write(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK0, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK0_USERINTERRUPTMASK0_MASK); + imxdpuv1_write(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK1, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK1_USERINTERRUPTMASK1_MASK); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK2, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK2_USERINTERRUPTMASK2_MASK); +#endif + /* Set all interrupts to user mode. this will to change to + enable panic mode */ + imxdpuv1_write(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTENABLE0, 0); + imxdpuv1_write(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTENABLE1, 0); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_COMCTRL_USERINTERRUPTENABLE2, 0); +#endif + /* enable needed interupts */ + return ret; +} + +/*! + * This function initializes the imxdpu and the required data structures + * + * @param imxdpuv1_id id of the diplay unit + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +/* todo: replace with probe function or call from probe + use device tree as needed */ +int imxdpuv1_init(int8_t imxdpuv1_id) +{ + int ret = 0; + int i; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + /* todo: add resource mapping for xrdc, layers, blit, display, ... */ + + /* imxdpuv1_id starts from 0 */ + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + + /* Map the channels to display streams + todo: + make this mapping dynamic + add channel features + map capture channels + */ + for (i = IMXDPUV1_CHAN_IDX_IN_FIRST; i < IMXDPUV1_CHAN_IDX_MAX; i++) { + if (i <= IMXDPUV1_CHAN_IDX_17) + imxdpuv1_array[imxdpuv1_id].chan_data[i].disp_id = 0; + else if (i < IMXDPUV1_CHAN_IDX_IN_MAX) + imxdpuv1_array[imxdpuv1_id].chan_data[i].disp_id = 1; + else if (i < IMXDPUV1_CHAN_IDX_OUT_FIRST) + imxdpuv1_array[imxdpuv1_id].chan_data[i].disp_id = 0; + else if (i < IMXDPUV1_CHAN_IDX_OUT_MAX) + imxdpuv1_array[imxdpuv1_id].chan_data[i].disp_id = 1; + else + imxdpuv1_array[imxdpuv1_id].chan_data[i].disp_id = 0; + } + + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + imxdpu->irq_count = 0; + + if (imxdpuv1_id == 0) { + imxdpu->base = (void __iomem *)IMXDPUV1_REGS_BASE_PHY0; + IMXDPUV1_TRACE("%s(): virtual base address is 0x%p (0x%08x physical)\n", + __func__, imxdpu->base, IMXDPUV1_REGS_BASE_PHY0); + + } else if (imxdpuv1_id == 1) { + imxdpu->base = (void __iomem *)IMXDPUV1_REGS_BASE_PHY1; + IMXDPUV1_TRACE("%s(): virtual base address is 0x%p (0x%08x physical)\n", + __func__, imxdpu->base, IMXDPUV1_REGS_BASE_PHY1); + + } else { + return -ENOMEM; + } + + /* todo: may need to check resource allocaiton/ownership for these */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY0, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY0_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY1, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY1_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY2, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY2_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY3, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY3_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY4, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY4_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY5, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY5_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY6, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY6_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_LAYERPROPERTY7, + IMXDPUV1_FETCHLAYER0_LAYERPROPERTY7_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_TRIGGERENABLE, + IMXDPUV1_FETCHLAYER0_TRIGGERENABLE_RESET_VALUE); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY0, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY0_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY1, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY1_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY2, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY2_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY3, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY3_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY4, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY4_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY5, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY5_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY6, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY6_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_LAYERPROPERTY7, + IMXDPUV1_FETCHLAYER1_LAYERPROPERTY7_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_TRIGGERENABLE, + IMXDPUV1_FETCHLAYER1_TRIGGERENABLE_RESET_VALUE); +#endif + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY0, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY0_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY1, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY1_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY2, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY2_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY3, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY3_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY4, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY4_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY5, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY5_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY6, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY6_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_LAYERPROPERTY7, + IMXDPUV1_FETCHWARP2_LAYERPROPERTY7_RESET_VALUE); + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_TRIGGERENABLE, + IMXDPUV1_FETCHWARP2_TRIGGERENABLE_RESET_VALUE); + + /* Initial StaticControl configuration - reset values */ + /* IMXDPUV1_FETCHDECODE9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE9_STATICCONTROL, + IMXDPUV1_FETCHDECODE9_STATICCONTROL_RESET_VALUE); +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_FETCHPERSP9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHPERSP9_STATICCONTROL, + IMXDPUV1_FETCHPERSP9_STATICCONTROL_RESET_VALUE); +#else + /* IMXDPUV1_FETCHPERSP9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP9_STATICCONTROL, + IMXDPUV1_FETCHWARP9_STATICCONTROL_RESET_VALUE); +#endif + + /* IMXDPUV1_FETCHECO9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO9_STATICCONTROL, + IMXDPUV1_FETCHECO9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_ROP9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_ROP9_STATICCONTROL, + IMXDPUV1_ROP9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_CLUT9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_CLUT9_STATICCONTROL, + IMXDPUV1_CLUT9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_MATRIX9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_MATRIX9_STATICCONTROL, + IMXDPUV1_MATRIX9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_HSCALER9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_HSCALER9_STATICCONTROL, + IMXDPUV1_HSCALER9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_VSCALER9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_VSCALER9_STATICCONTROL, + IMXDPUV1_VSCALER9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FILTER9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FILTER9_STATICCONTROL, + IMXDPUV1_FILTER9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_BLITBLEND9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_BLITBLEND9_STATICCONTROL, + IMXDPUV1_BLITBLEND9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_STORE9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_STORE9_STATICCONTROL, + IMXDPUV1_STORE9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_CONSTFRAME0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_CONSTFRAME0_STATICCONTROL, + IMXDPUV1_CONSTFRAME0_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_EXTDST0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTDST0_STATICCONTROL, + IMXDPUV1_EXTDST0_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_EXTDST4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTDST4_STATICCONTROL, + IMXDPUV1_EXTDST4_STATICCONTROL_RESET_VALUE); + + /* todo: IMXDPUV1_CONSTFRAME4_STATICCONTROL */ + + /* IMXDPUV1_CONSTFRAME1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_CONSTFRAME1_STATICCONTROL, + IMXDPUV1_CONSTFRAME1_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_EXTDST1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTDST1_STATICCONTROL, + IMXDPUV1_EXTDST1_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_EXTDST5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTDST5_STATICCONTROL, + IMXDPUV1_EXTDST5_STATICCONTROL_RESET_VALUE); + + /* todo: IMXDPUV1_CONSTFRAME5_STATICCONTROL */ +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_EXTSRC4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTSRC4_STATICCONTROL, + IMXDPUV1_EXTSRC4_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_STORE4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_STORE4_STATICCONTROL, + IMXDPUV1_STORE4_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_EXTSRC5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTSRC5_STATICCONTROL, + IMXDPUV1_EXTSRC5_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_STORE5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_STORE5_STATICCONTROL, + IMXDPUV1_STORE5_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHDECODE2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE2_STATICCONTROL, + IMXDPUV1_FETCHDECODE2_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHDECODE3_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE3_STATICCONTROL, + IMXDPUV1_FETCHDECODE3_STATICCONTROL_RESET_VALUE); +#endif + /* IMXDPUV1_FETCHWARP2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_STATICCONTROL, + IMXDPUV1_FETCHWARP2_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHECO2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO9_STATICCONTROL, + IMXDPUV1_FETCHECO9_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHDECODE0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE0_STATICCONTROL, + IMXDPUV1_FETCHDECODE0_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHECO0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO0_STATICCONTROL, + IMXDPUV1_FETCHECO0_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHDECODE1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE1_STATICCONTROL, + IMXDPUV1_FETCHDECODE1_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_FETCHECO1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO1_STATICCONTROL, + IMXDPUV1_FETCHECO1_STATICCONTROL_RESET_VALUE); + + /* todo: IMXDPUV1_MATRIX5_STATICCONTROL */ + /* todo: IMXDPUV1_HSCALER5_STATICCONTROL */ + /* todo: IMXDPUV1_VSCALER5_STATICCONTROL */ + /* IMXDPUV1_LAYERBLEND0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND0_STATICCONTROL, + IMXDPUV1_LAYERBLEND0_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_LAYERBLEND1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND1_STATICCONTROL, + IMXDPUV1_LAYERBLEND1_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_LAYERBLEND2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND2_STATICCONTROL, + IMXDPUV1_LAYERBLEND2_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_LAYERBLEND3_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND3_STATICCONTROL, + IMXDPUV1_LAYERBLEND3_STATICCONTROL_RESET_VALUE); +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_LAYERBLEND4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND4_STATICCONTROL, + IMXDPUV1_LAYERBLEND4_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_LAYERBLEND5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND5_STATICCONTROL, + IMXDPUV1_LAYERBLEND5_STATICCONTROL_RESET_VALUE); + + /* IMXDPUV1_LAYERBLEND6_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND6_STATICCONTROL, + IMXDPUV1_LAYERBLEND6_STATICCONTROL_RESET_VALUE); +#endif + /* Dynamic config */ + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHPERSP9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#else + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHWARP9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#endif + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_ROP9_DYNAMIC, + IMXDPUV1_SET_FIELD + (IMXDPUV1_PIXENGCFG_ROP9_DYNAMIC_ROP9_PRIM_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD + (IMXDPUV1_PIXENGCFG_ROP9_DYNAMIC_ROP9_SEC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD + (IMXDPUV1_PIXENGCFG_ROP9_DYNAMIC_ROP9_TERT_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_CLUT9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_MATRIX9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_HSCALER9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_VSCALER9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FILTER9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_BLITBLEND9_DYNAMIC, + IMXDPUV1_SET_FIELD + (IMXDPUV1_PIXENGCFG_BLITBLEND9_DYNAMIC_BLITBLEND9_PRIM_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD + (IMXDPUV1_PIXENGCFG_BLITBLEND9_DYNAMIC_BLITBLEND9_SEC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE9_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE2_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE3_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#endif + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHWARP2_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE0_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE1_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_GAMMACOR4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#endif + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_MATRIX4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_HSCALER4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_VSCALER4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_HISTOGRAM4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_GAMMACOR5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE)); +#endif + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_MATRIX5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_HSCALER5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_VSCALER5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_HISTOGRAM5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); +#endif + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND0_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND1_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND2_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND3_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND4_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND5_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND6_DYNAMIC, + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL, + IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_CLKEN, + IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC)); +#endif + /* Static configuration - reset values */ + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE9_STATIC, + IMXDPUV1_PIXENGCFG_STORE9_STATIC_RESET_VALUE); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_STATIC, + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_RESET_VALUE); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST4_STATIC, + IMXDPUV1_PIXENGCFG_EXTDST4_STATIC_RESET_VALUE); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_STATIC, + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_RESET_VALUE); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST5_STATIC, + IMXDPUV1_PIXENGCFG_EXTDST5_STATIC_RESET_VALUE); +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE4_STATIC, + IMXDPUV1_PIXENGCFG_STORE4_STATIC_RESET_VALUE); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE5_STATIC, + IMXDPUV1_PIXENGCFG_STORE5_STATIC_RESET_VALUE); +#endif + /* Static configuration - initial settings */ + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE9_STATIC, + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_POWERDOWN, + IMXDPUV1_FALSE) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_SYNC_MODE, + IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_SYNC_MODE__SINGLE) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_SW_RESET, + IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_SW_RESET__OPERATION) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_STORE9_STATIC_STORE9_DIV, + IMXDPUV1_PIXENGCFG_DIVIDER_RESET)); + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_STATIC, + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_POWERDOWN, + IMXDPUV1_FALSE) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_SYNC_MODE, + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_SYNC_MODE__AUTO) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_SW_RESET, + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_SW_RESET__OPERATION) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST0_STATIC_EXTDST0_DIV, + IMXDPUV1_PIXENGCFG_DIVIDER_RESET)); + + /* todo: IMXDPUV1_PIXENGCFG_EXTDST4_STATIC_OFFSET */ + + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_STATIC, + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_POWERDOWN, + IMXDPUV1_FALSE) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_SYNC_MODE, + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_SYNC_MODE__AUTO) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_SW_RESET, + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_SW_RESET__OPERATION) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_EXTDST1_STATIC_EXTDST1_DIV, + IMXDPUV1_PIXENGCFG_DIVIDER_RESET)); + + /* todo: IMXDPUV1_PIXENGCFG_EXTDST5_STATIC_OFFSET */ +#ifdef IMXDPUV1_VERSION_0 + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE4_STATIC, + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_POWERDOWN, + IMXDPUV1_FALSE) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_SYNC_MODE, + IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_SYNC_MODE__SINGLE) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_SW_RESET, + IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_SW_RESET__OPERATION) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_STORE4_STATIC_STORE4_DIV, + IMXDPUV1_PIXENGCFG_DIVIDER_RESET)); +#endif + /* todo: IMXDPUV1_PIXENGCFG_STORE4_STATIC */ + /* Static Control configuration */ + /* IMXDPUV1_FETCHDECODE9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE9_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE9_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_FETCHPERSP9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHPERSP9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHPERSP9_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHPERSP9_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); +#else + /* IMXDPUV1_FETCHWARP9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHWARP9_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHWARP9_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); +#endif + /* IMXDPUV1_FETCHECO9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO9_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHECO9_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); + + /* IMXDPUV1_ROP9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_ROP9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_ROP9_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_CLUT9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_CLUT9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_CLUT9_STATICCONTROL_SHDEN, 1)); + + imxdpuv1_write(imxdpu, IMXDPUV1_CLUT9_UNSHADOWEDCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_CLUT9_UNSHADOWEDCONTROL_B_EN, + IMXDPUV1_CLUT9_UNSHADOWEDCONTROL_B_EN__ENABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_CLUT9_UNSHADOWEDCONTROL_G_EN, + IMXDPUV1_CLUT9_UNSHADOWEDCONTROL_G_EN__ENABLE) + | IMXDPUV1_SET_FIELD(IMXDPUV1_CLUT9_UNSHADOWEDCONTROL_R_EN, + IMXDPUV1_CLUT9_UNSHADOWEDCONTROL_R_EN__ENABLE)); + + /* IMXDPUV1_MATRIX9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_MATRIX9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_MATRIX9_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_HSCALER9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_HSCALER9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_HSCALER9_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_VSCALER9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_VSCALER9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_VSCALER9_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_FILTER9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FILTER9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FILTER9_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_BLITBLEND9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_BLITBLEND9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_BLITBLEND9_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_STORE9_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_STORE9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_STORE9_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE9_STATICCONTROL_BASEADDRESSAUTOUPDATE, 1)); + + /* IMXDPUV1_CONSTFRAME0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_CONSTFRAME0_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_CONSTFRAME0_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_EXTDST0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTDST0_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTDST0_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_EXTDST0_STATICCONTROL_PERFCOUNTMODE, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTDST0_STATICCONTROL_KICK_MODE, + IMXDPUV1_EXTDST0_STATICCONTROL_KICK_MODE__EXTERNAL)); + + /* todo: IMXDPUV1_CONSTFRAME4_STATICCONTROL */ + /* todo: IMXDPUV1_EXTDST4_STATICCONTROL */ + + /* IMXDPUV1_CONSTFRAME1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_CONSTFRAME1_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_CONSTFRAME1_STATICCONTROL_SHDEN, 1)); + + /* IMXDPUV1_EXTDST1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTDST1_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTDST1_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_EXTDST1_STATICCONTROL_PERFCOUNTMODE, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTDST1_STATICCONTROL_KICK_MODE, + IMXDPUV1_EXTDST1_STATICCONTROL_KICK_MODE__EXTERNAL)); + + /* todo: IMXDPUV1_CONSTFRAME5_STATICCONTROL */ + /* todo: IMXDPUV1_EXTDST5_STATICCONTROL */ +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_EXTSRC4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTSRC4_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_STATICCONTROL_STARTSEL, + IMXDPUV1_EXTSRC4_STATICCONTROL_STARTSEL__LOCAL)); + + /* IMXDPUV1_STORE4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_STORE4_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_STORE4_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE4_STATICCONTROL_BASEADDRESSAUTOUPDATE, 1)); + + /* IMXDPUV1_EXTSRC5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_EXTSRC5_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC5_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC5_STATICCONTROL_STARTSEL, + IMXDPUV1_EXTSRC5_STATICCONTROL_STARTSEL__LOCAL)); + + /* IMXDPUV1_STORE5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_STORE5_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_STORE5_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE5_STATICCONTROL_BASEADDRESSAUTOUPDATE, 1)); + + /* IMXDPUV1_FETCHDECODE2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE2_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE2_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE2_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); + + /* IMXDPUV1_FETCHDECODE3_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE3_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE3_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE3_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); +#endif + /* IMXDPUV1_FETCHWARP2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHWARP2_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHWARP2_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHWARP2_STATICCONTROL_SHDLDREQSTICKY, 0)); + + /* IMXDPUV1_FETCHECO2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO9_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO9_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHECO9_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); + + /* IMXDPUV1_FETCHDECODE0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE0_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE0_STATICCONTROL_BASEADDRESSAUTOUPDATE, + 0)); + + /* IMXDPUV1_FETCHECO0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO0_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO0_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHECO0_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); + + /* IMXDPUV1_FETCHDECODE1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHDECODE1_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE1_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE1_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); + + /* IMXDPUV1_FETCHECO1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHECO1_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO1_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHECO1_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0)); + + /* IMXDPUV1_FETCHLAYER0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHLAYER0_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHLAYER0_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHLAYER0_STATICCONTROL_SHDLDREQSTICKY, 0)); +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_FETCHLAYER1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHLAYER1_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHLAYER1_STATICCONTROL_BASEADDRESSAUTOUPDATE, 0) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHLAYER1_STATICCONTROL_SHDLDREQSTICKY, 0)); + + /* IMXDPUV1_GAMMACOR4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_GAMMACOR4_STATICCONTROL, + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR4_STATICCONTROL_BLUEWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR4_STATICCONTROL_GREENWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR4_STATICCONTROL_REDWRITEENABLE, 1)); +#endif + /* todo: IMXDPUV1_MATRIX4_STATICCONTROL */ + /* todo: IMXDPUV1_HSCALER4_STATICCONTROL */ + /* todo: IMXDPUV1_VSCALER4_STATICCONTROL */ +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_GAMMACOR5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_GAMMACOR5_STATICCONTROL, + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR5_STATICCONTROL_BLUEWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR5_STATICCONTROL_GREENWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR5_STATICCONTROL_REDWRITEENABLE, 1)); +#endif + /* todo: IMXDPUV1_MATRIX5_STATICCONTROL */ + /* todo: IMXDPUV1_HSCALER5_STATICCONTROL */ + /* todo: IMXDPUV1_VSCALER5_STATICCONTROL */ + + /* IMXDPUV1_LAYERBLEND0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND0_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND0_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND0_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND0_STATICCONTROL_SHDTOKSEL__BOTH)); + + /* IMXDPUV1_LAYERBLEND1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND1_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND1_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND1_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND1_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND1_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND1_STATICCONTROL_SHDTOKSEL__BOTH)); + + /* IMXDPUV1_LAYERBLEND2_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND2_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND2_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND2_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND2_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND2_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND2_STATICCONTROL_SHDTOKSEL__BOTH)); + + /* IMXDPUV1_LAYERBLEND3_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND3_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND3_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND3_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND3_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND3_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND3_STATICCONTROL_SHDTOKSEL__BOTH)); + +#ifdef IMXDPUV1_VERSION_0 + /* IMXDPUV1_LAYERBLEND4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND4_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDTOKSEL__BOTH)); + + /* IMXDPUV1_LAYERBLEND4_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND4_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDEN, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND4_STATICCONTROL_SHDTOKSEL__BOTH)); + + /* IMXDPUV1_LAYERBLEND5_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND5_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND5_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND5_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND5_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND5_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND5_STATICCONTROL_SHDTOKSEL__BOTH)); + + /* IMXDPUV1_LAYERBLEND6_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_LAYERBLEND6_STATICCONTROL, + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND6_STATICCONTROL_SHDEN, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND6_STATICCONTROL_SHDLDSEL, + IMXDPUV1_LAYERBLEND6_STATICCONTROL_SHDLDSEL__SECONDARY) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_LAYERBLEND6_STATICCONTROL_SHDTOKSEL, + IMXDPUV1_LAYERBLEND6_STATICCONTROL_SHDTOKSEL__BOTH)); +#endif + /* todo: IMXDPUV1_EXTSRC0_STATICCONTROL */ + /* todo: IMXDPUV1_EXTSRC1_STATICCONTROL */ + /* todo: IMXDPUV1_MATRIX0_STATICCONTROL */ + /* IMXDPUV1_GAMMACOR0_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_GAMMACOR0_STATICCONTROL, + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR1_STATICCONTROL_BLUEWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR1_STATICCONTROL_GREENWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR1_STATICCONTROL_REDWRITEENABLE, 1)); + /* todo: IMXDPUV1_SIG0_STATICCONTROL */ + /* todo: IMXDPUV1_MATRIX1_STATICCONTROL */ + /* IMXDPUV1_GAMMACOR1_STATICCONTROL */ + imxdpuv1_write(imxdpu, IMXDPUV1_GAMMACOR1_STATICCONTROL, + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR1_STATICCONTROL_BLUEWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR1_STATICCONTROL_GREENWRITEENABLE, 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_GAMMACOR1_STATICCONTROL_REDWRITEENABLE, 1)); + /* IMXDPUV1_SIG1_STATICCONTROL */ + + imxdpuv1_init_irqs(imxdpuv1_id); + + return ret; +} + +int imxdpuv1_init_sync_panel(int8_t imxdpuv1_id, + int8_t disp, + uint32_t pixel_fmt, struct imxdpuv1_videomode mode) +{ + int ret = 0; + IMXDPUV1_TRACE("%s()\n", __func__); + return ret; +} + +int imxdpuv1_uninit_sync_panel(int8_t imxdpuv1_id, int8_t disp) +{ + int ret = 0; + IMXDPUV1_TRACE("%s()\n", __func__); + return ret; +} + +int imxdpuv1_reset_disp_panel(int8_t imxdpuv1_id, int8_t disp) +{ + int ret = 0; + IMXDPUV1_TRACE("%s()\n", __func__); + return ret; +} + +/*! + * This function initializes the display + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_init(int8_t imxdpuv1_id, int8_t disp) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + struct imxdpuv1_videomode *mode; + int reg = 0; + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + mode = &imxdpu->video_mode[disp]; + /*imxdpuv1_disp_dump_mode(&imxdpu->video_mode[disp]);*/ + + if (disp == 0) { +#ifdef IMXDPUV1_TCON0_MAP_24BIT_0_23 + /* Static 24-bit TCON bit mapping for FPGA */ + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT7_4, 0x1d1c1b1a); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT3_0, 0x19181716); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT15_12, 0x13121110); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT11_8, 0x0f0e0d0c); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT23_20, 0x09080706); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT19_16, 0x05040302); +#else + /* tcon mapping + * RR RRRR RRRR GGGG GGGG GGBB BBBB BBBB + * 98 7654 3210 9876 5432 1098 7654 3210 + * bits + * 00 0000 0000 1111 1111 1122 2222 2222 + * 98 7654 3210 8765 5432 1098 7654 3210 + */ + /* 30-bit timing controller setup */ + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT31_28, 0x00000908); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT27_24, 0x07060504); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT23_20, 0x03020100); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT19_16, 0x13121110); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT15_12, 0x0f0e0d0c); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT11_8, 0x0b0a1d1c); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT7_4, 0x1b1a1918); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON0_MAPBIT3_0, 0x17161514); + +#endif + + /* set data enable polarity */ + if (mode->flags & IMXDPUV1_MODE_FLAGS_HSYNC_POL) + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLHS0, + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLHS0__HIGH); + else + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLHS0, + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLHS0__LOW); + + if (mode->flags & IMXDPUV1_MODE_FLAGS_VSYNC_POL) + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLVS0, + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLVS0__HIGH); + else + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLVS0, + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLVS0__LOW); + + if (mode->flags & IMXDPUV1_MODE_FLAGS_DE_POL) + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLEN0, + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLEN0__HIGH); + else + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLEN0, + IMXDPUV1_DISENGCFG_POLARITYCTRL0_POLEN0__LOW); + + imxdpuv1_write(imxdpu, IMXDPUV1_DISENGCFG_POLARITYCTRL0, reg); + /* printf("polreg=0x%x\n", imxdpuv1_read(imxdpu, IMXDPUV1_DISENGCFG_POLARITYCTRL0)); */ + + } else if (disp == 1) { +#ifdef IMXDPUV1_TCON1_MAP_24BIT_0_23 + /* Static TCON bit mapping */ + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT7_4, 0x1d1c1b1a); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT3_0, 0x19181716); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT15_12, 0x13121110); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT11_8, 0x0f0e0d0c); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT23_20, 0x09080706); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT19_16, 0x05040302); +#else + /* tcon mapping + * RR RRRR RRRR GGGG GGGG GGBB BBBB BBBB + * 98 7654 3210 9876 5432 1098 7654 3210 + * bits + * 00 0000 0000 1111 1111 1122 2222 2222 + * 98 7654 3210 8765 5432 1098 7654 3210 + */ + /* 30-bit timing controller setup */ + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT31_28, 0x00000908); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT27_24, 0x07060504); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT23_20, 0x03020100); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT19_16, 0x13121110); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT15_12, 0x0f0e0d0c); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT11_8, 0x0b0a1d1c); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT7_4, 0x1b1a1918); + imxdpuv1_write(imxdpu, IMXDPUV1_TCON1_MAPBIT3_0, 0x17161514); +#endif + /* set data enable polarity */ + if (mode->flags & IMXDPUV1_MODE_FLAGS_HSYNC_POL) + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLHS1, + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLHS1__HIGH); + else + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLHS1, + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLHS1__LOW); + + if (mode->flags & IMXDPUV1_MODE_FLAGS_VSYNC_POL) + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLVS1, + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLVS1__HIGH); + else + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLVS1, + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLVS1__LOW); + + if (mode->flags & IMXDPUV1_MODE_FLAGS_DE_POL) + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLEN1, + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLEN1__HIGH); + else + reg |= IMXDPUV1_SET_FIELD( + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLEN1, + IMXDPUV1_DISENGCFG_POLARITYCTRL1_POLEN1__LOW); + + imxdpuv1_write(imxdpu, IMXDPUV1_DISENGCFG_POLARITYCTRL1, reg); + /* printf("polreg=0x%x\n", imxdpuv1_read(imxdpu, IMXDPUV1_DISENGCFG_POLARITYCTRL1)); */ + + } else { + return -EINVAL; + } + /* todo: initialize prefetch */ + + return ret; +} + +int imxdpuv1_disp_setup_tcon_bypass_mode( + int8_t imxdpuv1_id, + int8_t disp, + const struct imxdpuv1_videomode *mode) +{ + struct imxdpuv1_soc *imxdpu; + uint32_t b_off; /* block offset for tcon generator */ + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (disp == 0) { + b_off = IMXDPUV1_TCON0_LOCKUNLOCK; + } else if (disp == 1) { + b_off = IMXDPUV1_TCON1_LOCKUNLOCK; + } else { + return -EINVAL; + } + + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_TCON_CTRL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_TCON_CTRL_LVDS_BALANCE, + IMXDPUV1_TCON0_TCON_CTRL_LVDS_BALANCE__BALANCED) | + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_TCON_CTRL_MINILVDS_OPCODE, + IMXDPUV1_TCON0_TCON_CTRL_MINILVDS_OPCODE__MODE_4PAIRS) | + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_TCON_CTRL_SPLITPOSITION, + 0x140)); + /* setup hsync */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG0POSON_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG0POSON_SPGPSON_X0, mode->hlen + mode->hfp)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG0MASKON_OFFSET, 0xffff); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG0POSOFF_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG0POSOFF_SPGPSOFF_X0, mode->hlen + mode->hfp + mode->hsync)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG0MASKOFF_OFFSET, 0xffff); + + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX0SIGS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SMX0SIGS_SMX0SIGS_S0, 2)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX0FCTTABLE_OFFSET, 1); + + /* Setup Vsync */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG1POSON_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG1POSON_SPGPSON_X1, mode->hlen + mode->hfp + mode->hsync) | + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG1POSON_SPGPSON_Y1, mode->vlen + mode->vfp - 1)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG1MASKON_OFFSET, 0); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG1POSOFF_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG1POSOFF_SPGPSOFF_X1, mode->hlen + mode->hfp + mode->hsync)| + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG1POSOFF_SPGPSOFF_Y1, mode->vlen + mode->vfp + mode->vsync - 1)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG1MASKOFF_OFFSET, 0); + + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX1SIGS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SMX1SIGS_SMX1SIGS_S0, 3)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX1FCTTABLE_OFFSET, 1); + + /* data enable horizontal */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG2POSON_OFFSET, 0); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG2MASKON_OFFSET, 0xffff); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG2POSOFF_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG2POSOFF_SPGPSOFF_X2, mode->hlen)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG2MASKOFF_OFFSET, 0xffff); + /* data enable vertical */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG3POSON_OFFSET, 0); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG3MASKON_OFFSET, 0x7fff0000); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG3POSOFF_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG3POSOFF_SPGPSOFF_X3, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG3POSOFF_SPGPSOFF_Y3, mode->vlen)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG3MASKOFF_OFFSET, 0x7fff0000); + + /* use both SPG2 and SPG3 to generate data enable */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX2SIGS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SMX2SIGS_SMX2SIGS_S0, 4)| + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SMX2SIGS_SMX2SIGS_S1, 5)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX2FCTTABLE_OFFSET, 8); + + /* shadow load trigger (aka kachunk) */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG4POSON_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG4POSON_SPGPSON_X4, 10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG4POSON_SPGPSON_Y4, mode->vlen)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG4MASKON_OFFSET, 0); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG4POSOFF_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG4POSOFF_SPGPSOFF_X4, 26) | + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SPG4POSOFF_SPGPSOFF_Y4, mode->vlen)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SPG4MASKOFF_OFFSET, 0); + + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX3SIGS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_TCON0_SMX3SIGS_SMX3SIGS_S0, 6)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_TCON0_SMX3FCTTABLE_OFFSET, 2); + + return 0; +} + +/*! + * This function sets up the frame generator + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * @param enable state to set frame generator to + * @param mode to set the display to + * @param cc_red constant color red + * @param cc_green constant color green + * @param cc_blue constant color blue + * @param cc_alpha constant color alpha +* + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_setup_frame_gen( + int8_t imxdpuv1_id, + int8_t disp, + const struct imxdpuv1_videomode *mode, + uint16_t cc_red, /* 10 bits */ + uint16_t cc_green, /* 10 bits */ + uint16_t cc_blue, /* 10 bits */ + uint8_t cc_alpha, + bool test_mode_enable) +{ /* 1 bits, yes 1 bit */ + int ret = 0; + uint32_t b_off; /* block offset for frame generator */ + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (disp == 0) { + b_off = IMXDPUV1_FRAMEGEN0_LOCKUNLOCK; + } else if (disp == 1) { + b_off = IMXDPUV1_FRAMEGEN1_LOCKUNLOCK; + } else { + return -EINVAL; + } + + /* todo: + add video mode sanity check here + check if LRSYNC is required + */ + + if (mode->flags & IMXDPUV1_MODE_FLAGS_LRSYNC) { + /* todo: here we need to use two outputs to make one */ + if (disp == 0) { + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_FRAMEGEN0_FGSTCTRL_FGSYNCMODE, + IMXDPUV1_FRAMEGEN0_FGSTCTRL_FGSYNCMODE__MASTER); + } else { + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_FRAMEGEN1_FGSTCTRL_FGSYNCMODE, + IMXDPUV1_FRAMEGEN1_FGSTCTRL_FGSYNCMODE__SLAVE_CYC); + } + } else { + reg = IMXDPUV1_SET_FIELD( + IMXDPUV1_FRAMEGEN0_FGSTCTRL_FGSYNCMODE, + IMXDPUV1_FRAMEGEN0_FGSTCTRL_FGSYNCMODE__OFF); + } + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_FGSTCTRL_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_HTCFG1_HACT, mode->hlen) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_HTCFG1_HTOTAL, + (mode->hlen + mode->hfp + mode->hbp + mode->hsync - 1)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_HTCFG1_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_HTCFG2_HSYNC, + mode->hsync - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_HTCFG2_HSBP, + mode->hbp + mode->hsync - 1) | + /* shadow enable */ + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_HTCFG2_HSEN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_HTCFG2_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_VTCFG1_VACT, mode->vlen) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_VTCFG1_VTOTAL, + (mode->vlen + mode->vfp + mode->vbp + mode->vsync - + 1)); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_VTCFG1_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_VTCFG2_VSYNC, + mode->vsync - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_VTCFG2_VSBP, + mode->vbp + mode->vsync - 1) | + /* shadow enable */ + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_VTCFG2_VSEN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_VTCFG2_OFFSET, reg); + + /* Interupt at position (0, vlen - 3) for end of frame interrupt */ + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT0CONFIG_INT0COL, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT0CONFIG_INT0HSEN, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT0CONFIG_INT0ROW, + mode->vlen - 3) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT0CONFIG_INT0EN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_INT0CONFIG_OFFSET, reg); + + /* Interupt at position 1, mode->vlen */ + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT1CONFIG_INT1COL, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT1CONFIG_INT1HSEN, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT1CONFIG_INT1ROW, + mode->vlen) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT1CONFIG_INT1EN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_INT1CONFIG_OFFSET, reg); + + /* Interupt at position 2, mode->vlen */ + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT2CONFIG_INT2COL, 2) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT2CONFIG_INT2HSEN, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT2CONFIG_INT2ROW, + mode->vlen) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT2CONFIG_INT2EN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_INT2CONFIG_OFFSET, reg); + + /* Interupt at position 3, mode->vlen */ + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT3CONFIG_INT3COL, 3) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT3CONFIG_INT3HSEN, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT3CONFIG_INT3ROW, + mode->vlen) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_INT3CONFIG_INT3EN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_INT3CONFIG_OFFSET, reg); + + /* todo: these need to be checked + _SKICKCOL for verification: =(FW - 40) , for ref driver = 1 ? + _SKICKROW for verif. =(FH - 1), ref driver = vlen-2 + */ + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_SKICKCONFIG_SKICKCOL, + mode->hlen - 40) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_SKICKCONFIG_SKICKINT1EN, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_SKICKCONFIG_SKICKROW, + mode->vlen + 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_SKICKCONFIG_SKICKEN, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_SKICKCONFIG_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_PACFG_PSTARTX, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_PACFG_PSTARTY, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_PACFG_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_SACFG_SSTARTX, 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_SACFG_SSTARTY, 1); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_SACFG_OFFSET, reg); + + if (IMXDPUV1_ENABLE == test_mode_enable) { + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRL_FGDM, + IMXDPUV1_FRAMEGEN0_FGINCTRL_FGDM__TEST); + } else { + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRL_FGDM, + IMXDPUV1_FRAMEGEN0_FGINCTRL_FGDM__SEC) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRL_ENPRIMALPHA, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRL_ENSECALPHA, 0); + } + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_FGINCTRL_OFFSET, reg); + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRLPANIC_FGDMPANIC, + IMXDPUV1_FRAMEGEN0_FGINCTRLPANIC_FGDMPANIC__CONSTCOL) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRLPANIC_ENPRIMALPHAPANIC, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGINCTRLPANIC_ENSECALPHAPANIC, 0); + imxdpuv1_write(imxdpu, b_off + + IMXDPUV1_FRAMEGEN0_FGINCTRLPANIC_OFFSET, reg); + + /* Set the constant color - ARGB 1-10-10-10 */ + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGCCR_CCRED, cc_red) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGCCR_CCBLUE, cc_blue) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGCCR_CCGREEN, cc_green) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGCCR_CCALPHA, cc_alpha); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_FGCCR_OFFSET, reg); + + + imxdpuv1_disp_setup_tcon_bypass_mode(imxdpuv1_id, disp, mode); + + /* save the mode */ + imxdpu->video_mode[disp] = *mode; + + /* imxdpuv1_disp_dump_mode(&imxdpu->video_mode[disp]); */ + + return ret; +} + +/*! + * This function updates the frame generator status + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_update_fgen_status(int8_t imxdpuv1_id, int8_t disp) +{ + int ret = 0; + uint32_t b_off; /* block offset for frame generator */ + uint32_t reg; + uint32_t temp; + struct imxdpuv1_soc *imxdpu; + static uint32_t fcount[IMXDPUV1_NUM_DI_MAX] = { 0, 0 }; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (disp == 0) { + b_off = IMXDPUV1_FRAMEGEN0_LOCKUNLOCK; + } else if (disp == 1) { + b_off = IMXDPUV1_FRAMEGEN1_LOCKUNLOCK; + } else { + return -EINVAL; + } + + /* todo: + add video mode sanity check here + check if LRSYNC is required + */ + + reg = imxdpuv1_read_irq(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_FGTIMESTAMP_OFFSET); + IMXDPUV1_TRACE_IRQ("DISP %d: findex %d, lindex %d\n", disp, + IMXDPUV1_GET_FIELD + (IMXDPUV1_FRAMEGEN0_FGTIMESTAMP_FRAMEINDEX, reg), + IMXDPUV1_GET_FIELD + (IMXDPUV1_FRAMEGEN0_FGTIMESTAMP_LINEINDEX, reg)); + + temp = IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGTIMESTAMP_FRAMEINDEX, reg); + if (temp != fcount[disp]) { + fcount[disp] = temp; + /* Just increment we assume this is called one per frame */ + imxdpu->fgen_stats[disp].frame_count++; + } + + reg = imxdpuv1_read_irq(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_FGCHSTAT_OFFSET); + temp = IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGCHSTAT_SECSYNCSTAT, reg); + + /* Sync status bits should be set */ + if ((temp != imxdpu->fgen_stats[disp].sec_sync_state) && (temp == 1)) { + imxdpu->fgen_stats[disp].sec_sync_count++; + IMXDPUV1_TRACE_IRQ("DISP %d: sec in sync\n", disp); + } + if ((temp != imxdpu->fgen_stats[disp].sec_sync_state) && (temp == 0)) { + IMXDPUV1_TRACE_IRQ("DISP %d: sec out of sync\n", disp); + } + imxdpu->fgen_stats[disp].sec_sync_state = temp; + temp = IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGCHSTAT_PRIMSYNCSTAT, reg); + + /* Sync status bits should be set */ + if ((temp != imxdpu->fgen_stats[disp].prim_sync_state) && + (temp == 1)) { + imxdpu->fgen_stats[disp].prim_sync_count++; + IMXDPUV1_TRACE_IRQ("DISP %d: prim in sync\n", disp); + } + if ((temp != imxdpu->fgen_stats[disp].prim_sync_state) && + (temp == 0)) { + IMXDPUV1_TRACE_IRQ("DISP %d: prim out of sync\n", disp); + } + imxdpu->fgen_stats[disp].prim_sync_state = temp; + + /* primary fifo bit should be clear if in use (panic stream) */ + if (IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGCHSTAT_PFIFOEMPTY, reg)) { + IMXDPUV1_TRACE_IRQ("DISP %d: primary fifo empty\n", disp); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FRAMEGEN0_FGCHSTATCLR_OFFSET, + IMXDPUV1_FRAMEGEN0_FGCHSTATCLR_CLRPRIMSTAT_MASK); + imxdpu->fgen_stats[disp].prim_fifo_empty_count++; + } + /* secondary fifo and skew error bits should be clear + if in use (content stream) */ + if (IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGCHSTAT_SFIFOEMPTY, reg) || + IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGCHSTAT_SKEWRANGEERR, reg)) { + if (IMXDPUV1_GET_FIELD(IMXDPUV1_FRAMEGEN0_FGCHSTAT_SFIFOEMPTY, reg)) { + IMXDPUV1_TRACE_IRQ("DISP %d: secondary fifo empty\n", + disp); + imxdpu->fgen_stats[disp].sec_fifo_empty_count++; + } + if (IMXDPUV1_GET_FIELD + (IMXDPUV1_FRAMEGEN0_FGCHSTAT_SKEWRANGEERR, reg)) { + IMXDPUV1_TRACE_IRQ("DISP %d: secondary skew error\n", + disp); + imxdpu->fgen_stats[disp].skew_error_count++; + } + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FRAMEGEN0_FGCHSTATCLR_OFFSET, + IMXDPUV1_FRAMEGEN0_FGCHSTATCLR_CLRSECSTAT_MASK); + } + return ret; +} +/*! + * This function sets up the frame capture + * + * @param imxdpuv1_id id of the diplay unit + * @param src_id id of the capture source block + * @param dest_id id of the capture dest block + * @param sync_count number of valid required to aquire sync + * @param cap_mode mode of the video input + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_cap_setup_frame( + int8_t imxdpuv1_id, + int8_t src_id, + int8_t dest_id, + int8_t sync_count, + const struct imxdpuv1_videomode *cap_mode) +{ +#ifndef IMXDPUV1_VERSION_0 + return -EINVAL; +#else + int ret = 0; + uint32_t b_off_frame; /* block offset for capture source */ + uint32_t b_off_extsrc; /* block offset for extsrc */ + + int8_t cap_id; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (src_id == IMXDPUV1_ID_FRAMECAP4) { + cap_id = 0; + b_off_frame = IMXDPUV1_FRAMECAP4_LOCKUNLOCK; + b_off_extsrc = IMXDPUV1_EXTSRC4_LOCKUNLOCK; + } else if (src_id == IMXDPUV1_ID_FRAMECAP5) { + cap_id = 1; + b_off_frame = IMXDPUV1_FRAMECAP5_LOCKUNLOCK; + b_off_extsrc = IMXDPUV1_EXTSRC5_LOCKUNLOCK; + } else if (src_id == IMXDPUV1_ID_FRAMEDUMP0) { + cap_id = 0; + b_off_frame = IMXDPUV1_FRAMEDUMP0_CONTROL; + b_off_extsrc = IMXDPUV1_EXTSRC0_LOCKUNLOCK; + } else if (src_id == IMXDPUV1_ID_FRAMEDUMP1) { + cap_id = 1; + b_off_frame = IMXDPUV1_FRAMEDUMP1_CONTROL; + b_off_extsrc = IMXDPUV1_EXTSRC4_LOCKUNLOCK; + } else { + return -EINVAL; + } + + if (dest_id == IMXDPUV1_ID_STORE4) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE4_DYNAMIC, + IMXDPUV1_PIXENGCFG_STORE4_DYNAMIC_STORE4_SRC_SEL__EXTSRC4); + } else if (dest_id == IMXDPUV1_ID_STORE5) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE5_DYNAMIC, + IMXDPUV1_PIXENGCFG_STORE5_DYNAMIC_STORE5_SRC_SEL__EXTSRC5); + } else if (dest_id == IMXDPUV1_ID_EXTDST0) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC, + IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC_EXTDST0_SRC_SEL__EXTSRC4); + } else if (dest_id == IMXDPUV1_ID_EXTDST1) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_DYNAMIC, + IMXDPUV1_PIXENGCFG_EXTDST1_DYNAMIC_EXTDST1_SRC_SEL__EXTSRC5); + } else { + return -EINVAL; + } + + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_STATICCONTROL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_STATICCONTROL_STARTSEL, + IMXDPUV1_EXTSRC4_STATICCONTROL_STARTSEL__LOCAL) | + IMXDPUV1_EXTSRC4_STATICCONTROL_SHDEN_MASK); + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_CONSTANTCOLOR_OFFSET, 0); + + if (cap_mode->format == IMXDPUV1_PIX_FMT_BGR24) { + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_COMPONENTBITSRED, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_COMPONENTBITSGREEN, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_COMPONENTBITSBLUE, 0x8)); + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_COMPONENTSHIFTRED, 0x10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_COMPONENTSHIFTGREEN, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_COMPONENTSHIFTBLUE, 0x00)); + + /* fixme: handle all cases for control */ + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_CONTROL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CONTROL_YUVCONVERSIONMODE, + IMXDPUV1_EXTSRC4_CONTROL_YUVCONVERSIONMODE__ITU601) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CONTROL_RASTERMODE, + IMXDPUV1_EXTSRC4_CONTROL_RASTERMODE__YUV422) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CONTROL_YUV422UPSAMPLINGMODE, + IMXDPUV1_EXTSRC4_CONTROL_YUV422UPSAMPLINGMODE__REPLICATE) | + IMXDPUV1_EXTSRC4_CONTROL_CLIPWINDOWENABLE_MASK); + + } else if (cap_mode->format == IMXDPUV1_PIX_FMT_YUYV) { + + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_OFFSET, + + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_COMPONENTBITSRED, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_COMPONENTBITSGREEN, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTBITS_COMPONENTBITSBLUE, 0x8)); + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_COMPONENTSHIFTRED, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_COMPONENTSHIFTGREEN, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_COLORCOMPONENTSHIFT_COMPONENTSHIFTBLUE, 0x0)); + + /* fixme: handle all cases for control */ + imxdpuv1_write(imxdpu, + b_off_extsrc + IMXDPUV1_EXTSRC4_CONTROL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CONTROL_RASTERMODE, + IMXDPUV1_EXTSRC4_CONTROL_RASTERMODE__YUV422) | + IMXDPUV1_EXTSRC4_CONTROL_CLIPWINDOWENABLE_MASK); + + } else { + IMXDPUV1_PRINT("%s(): invalid capture interface format\n", __func__); + return -EINVAL; + } + + + if ((src_id == IMXDPUV1_ID_FRAMECAP4) || (src_id == IMXDPUV1_ID_FRAMECAP5)) { + /* setup cature */ + uint8_t capture_interface_mode; + /* Fixme: change these mode bits to an enumeration */ + if ((cap_mode->flags & IMXDPUV1_MODE_FLAGS_32BIT) != 0) { + capture_interface_mode = IMXDPUV1_CAPENGCFG_CAPTUREINPUT1_CAPTUREMODE1__ENHSVS_32BIT; + } else if ((cap_mode->flags & IMXDPUV1_MODE_FLAGS_BT656_10BIT) != 0) { + capture_interface_mode = IMXDPUV1_CAPENGCFG_CAPTUREINPUT1_CAPTUREMODE1__ITU656_10BIT; + } else if ((cap_mode->flags & IMXDPUV1_MODE_FLAGS_BT656_8BIT) != 0) { + capture_interface_mode = IMXDPUV1_CAPENGCFG_CAPTUREINPUT1_CAPTUREMODE1__ITU656_8BIT; + } else { + return -EINVAL; + } + + if (cap_id == 0) { + imxdpuv1_write(imxdpu, IMXDPUV1_CAPENGCFG_CAPTUREINPUT0, + IMXDPUV1_SET_FIELD(IMXDPUV1_CAPENGCFG_CAPTUREINPUT0_CAPTUREMODE0, + capture_interface_mode)); + } else { + imxdpuv1_write(imxdpu, IMXDPUV1_CAPENGCFG_CAPTUREINPUT1, + IMXDPUV1_SET_FIELD(IMXDPUV1_CAPENGCFG_CAPTUREINPUT1_CAPTUREMODE1, + capture_interface_mode)); + } + + imxdpuv1_write(imxdpu, b_off_frame + IMXDPUV1_FRAMECAP4_FDR_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_FDR_HEIGHT, cap_mode->vlen - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_FDR_WIDTH, cap_mode->hlen - 1)); + + imxdpuv1_write(imxdpu, + b_off_frame + IMXDPUV1_FRAMECAP4_FDR1_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_FDR_HEIGHT, cap_mode->vlen1 - 1)); + + imxdpuv1_write(imxdpu, + b_off_frame + IMXDPUV1_FRAMECAP4_SCR_OFFSET, sync_count); + + + imxdpuv1_write(imxdpu, + b_off_frame + IMXDPUV1_FRAMECAP4_KCR_OFFSET, 0); + if ((cap_mode->clip_height != 0) && (cap_mode->clip_width != 0)) { + imxdpuv1_write(imxdpu, b_off_extsrc + IMXDPUV1_EXTSRC4_CLIPWINDOWDIMENSION_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWDIMENSION_CLIPWINDOWHEIGHT, cap_mode->clip_height - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWDIMENSION_CLIPWINDOWWIDTH, cap_mode->clip_width - 1)); + + imxdpuv1_write(imxdpu, b_off_extsrc + IMXDPUV1_EXTSRC4_CLIPWINDOWOFFSET_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWOFFSET_CLIPWINDOWXOFFSET, cap_mode->clip_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWOFFSET_CLIPWINDOWYOFFSET, cap_mode->clip_top)); + } + + imxdpuv1_write(imxdpu, + b_off_frame + IMXDPUV1_FRAMECAP4_SPR_OFFSET, + + /* low is active low, high is active high */ + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_SPR_POLHS, + ((cap_mode->flags & IMXDPUV1_MODE_FLAGS_HSYNC_POL) != 0)) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_SPR_POLVS, + ((cap_mode->flags & IMXDPUV1_MODE_FLAGS_VSYNC_POL) != 0)) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_SPR_POLEN, + ((cap_mode->flags & IMXDPUV1_MODE_FLAGS_DE_POL) == 0)) + ); + + + /* fixme: may need to move this mapping */ + if (src_id == IMXDPUV1_ID_FRAMECAP4) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE4_DYNAMIC, + IMXDPUV1_PIXENGCFG_STORE4_DYNAMIC_STORE4_SRC_SEL__EXTSRC4); + } else if (src_id == IMXDPUV1_ID_FRAMECAP5) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_STORE5_DYNAMIC, + IMXDPUV1_PIXENGCFG_STORE5_DYNAMIC_STORE5_SRC_SEL__EXTSRC5); + } + } + + if ((src_id == IMXDPUV1_ID_FRAMEDUMP0) || (src_id == IMXDPUV1_ID_FRAMEDUMP1)) { + /* todo */ + } + + /* save the mode */ + imxdpu->capture_mode[cap_id] = *cap_mode; + /* imxdpuv1_disp_dump_mode(cap_mode); */ + return ret; +#endif +} + +/*! + * This function sets up the frame capture + * + * @param imxdpuv1_id id of the diplay unit + * @param cap id of the capture inpute + * @param sync_count number of valid required to aquire sync + * @param cap_mode mode of the video input + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_cap_setup_crop( + int8_t imxdpuv1_id, + int8_t src_id, + int16_t clip_top, + int16_t clip_left, + uint16_t clip_width, + uint16_t clip_height) +{ +#ifndef IMXDPUV1_VERSION_0 + return -EINVAL; +#else + int ret = 0; + uint32_t b_off_extsrc; /* block offset for extsrc */ +#if 0 + uint32_t b_off_dest; /* block offset for destination */ +#endif + int8_t cap_id; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (src_id == IMXDPUV1_ID_FRAMECAP4) { + cap_id = 0; + b_off_extsrc = IMXDPUV1_EXTSRC4_LOCKUNLOCK; + } else if (src_id == IMXDPUV1_ID_FRAMECAP5) { + cap_id = 1; + b_off_extsrc = IMXDPUV1_EXTSRC5_LOCKUNLOCK; + } else if (src_id == IMXDPUV1_ID_FRAMEDUMP0) { + cap_id = 0; + b_off_extsrc = IMXDPUV1_EXTSRC0_LOCKUNLOCK; + } else if (src_id == IMXDPUV1_ID_FRAMEDUMP1) { + cap_id = 1; + b_off_extsrc = IMXDPUV1_EXTSRC4_LOCKUNLOCK; + } else { + return -EINVAL; + } + + if ((src_id == IMXDPUV1_ID_FRAMECAP4) || (src_id == IMXDPUV1_ID_FRAMECAP5)) { + if ((clip_height != 0) && (clip_width != 0)) { + imxdpuv1_write(imxdpu, b_off_extsrc + IMXDPUV1_EXTSRC4_CLIPWINDOWDIMENSION_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWDIMENSION_CLIPWINDOWHEIGHT, clip_height - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWDIMENSION_CLIPWINDOWWIDTH, clip_width - 1)); + + imxdpuv1_write(imxdpu, b_off_extsrc + IMXDPUV1_EXTSRC4_CLIPWINDOWOFFSET_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWOFFSET_CLIPWINDOWXOFFSET, clip_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_EXTSRC4_CLIPWINDOWOFFSET_CLIPWINDOWYOFFSET, clip_top)); + /* save the clip data */ + imxdpu->capture_mode[cap_id].clip_height = clip_height; + imxdpu->capture_mode[cap_id].clip_width = clip_width; + imxdpu->capture_mode[cap_id].clip_top = clip_top; + imxdpu->capture_mode[cap_id].clip_left = clip_left; + } + } + + if ((src_id == IMXDPUV1_ID_FRAMEDUMP0) || (src_id == IMXDPUV1_ID_FRAMEDUMP1)) { + /* todo */ + } + /* imxdpuv1_disp_dump_mode(&imxdpu->video_mode[cap_id]); */ + return ret; +#endif +} +/*! + * This function enables the frame capture + * + * @param imxdpuv1_id id of the display unit + * @param cap id of the capture output pipe + * @param enable state to set frame generator to + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_cap_enable(int8_t imxdpuv1_id, int8_t cap, bool enable) +{ +#ifndef IMXDPUV1_VERSION_0 + return -EINVAL; +#else + int ret = 0; + uint32_t b_off; + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (cap == 0) { + b_off = IMXDPUV1_FRAMECAP4_LOCKUNLOCK; + } else { + return -EINVAL; + } + + if (enable) { + /* imxdpuv1_dump_pixencfg_status(imxdpuv1_id); */ + printf("%s(): %s:%d stubbed feature\n", __func__, __FILE__, __LINE__); + /* imxdpuv1_dump_pixencfg_status(imxdpuv1_id); */ + } + reg = enable ? IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_CTR_CEN, 1) : + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMECAP4_CTR_CEN, 0); + + + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMECAP4_CTR_OFFSET, reg); + + return ret; +#endif +} + +/*! + * This function triggers a shadow load + * + * @param imxdpuv1_id id of the diplay unit + * @param dest_id id of the capture dest block + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_cap_request_shadow_load(int8_t imxdpuv1_id, int8_t dest_id, uint32_t mask) +{ +#ifndef IMXDPUV1_VERSION_0 + return -EINVAL; +#else + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + switch (dest_id) { + case IMXDPUV1_ID_STORE4: + imxdpuv1_write(imxdpu, + IMXDPUV1_PIXENGCFG_STORE4_REQUEST, + mask); + imxdpuv1_write(imxdpu, + IMXDPUV1_PIXENGCFG_STORE4_TRIGGER, + IMXDPUV1_PIXENGCFG_STORE4_TRIGGER_STORE4_SYNC_TRIGGER_MASK); + break; + case IMXDPUV1_ID_STORE5: + imxdpuv1_write(imxdpu, + IMXDPUV1_PIXENGCFG_STORE5_REQUEST, + mask); + imxdpuv1_write(imxdpu, + IMXDPUV1_PIXENGCFG_STORE5_TRIGGER, + IMXDPUV1_PIXENGCFG_STORE5_TRIGGER_STORE5_SYNC_TRIGGER_MASK); + break; + + default: + return -EINVAL; + + } + return ret; +#endif +} + +/*! + * This function requests a shadow loads + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * @param shadow_load_idx index of the shadow load requested + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_request_shadow_load(int8_t imxdpuv1_id, + int8_t disp, + imxdpuv1_shadow_load_index_t shadow_load_idx) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s(): imxdpuv1_id %d, disp %d, shadow_load_idx %d\n", + __func__, imxdpuv1_id, disp, shadow_load_idx); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + /* trigger configuration of the pipeline */ + + if ((disp == 0) || (disp == 1)) { + /* last request was complete or no request in progress, + then start a new request */ + if (imxdpu->shadow_load_state[disp][shadow_load_idx].word == 0) { + imxdpu->shadow_load_state[disp][shadow_load_idx].state. + request = IMXDPUV1_TRUE; + } else { /* check ifg the request is busy */ + IMXDPUV1_TRACE("%s(): shadow load not complete.", __func__); + return -EBUSY; + } + } else { + return -EINVAL; + } + + return ret; +} + +/*! + * This function force a shadow loads + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * @param shadow_load_idx index of the shadow load requested + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_force_shadow_load(int8_t imxdpuv1_id, + int8_t disp, + uint64_t mask) +{ + int ret = 0; + uint32_t addr_extdst; /* address for extdst */ + uint32_t addr_fgen; /* address for frame generator */ + uint32_t extdst = 0; + uint32_t fgen = 0; + uint32_t sub = 0; + struct imxdpuv1_soc *imxdpu; + int i; + uint64_t temp_mask; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (!((disp == 0) || (disp == 1))) { + return -EINVAL; + } + + if (mask == 0) { + return -EINVAL; + } + + if (disp == 0) { + addr_fgen = IMXDPUV1_FRAMEGEN0_FGSLR; + addr_extdst = IMXDPUV1_PIXENGCFG_EXTDST0_REQUEST; + } else if (disp == 1) { + addr_fgen = IMXDPUV1_FRAMEGEN1_FGSLR; + addr_extdst = IMXDPUV1_PIXENGCFG_EXTDST1_REQUEST; + } else { + return -EINVAL; + } + + for (i = 0; i < IMXDPUV1_SHDLD_IDX_MAX; i++) { + temp_mask = 1ULL << i; + if ((mask & temp_mask) == 0) + continue; + + extdst |= trigger_list[i].extdst; + sub |= trigger_list[i].sub; + + if ((i == IMXDPUV1_SHDLD_IDX_CONST0) || + (i == IMXDPUV1_SHDLD_IDX_CONST1)) { + fgen |= 1; + } + mask &= ~temp_mask; + } + + if (sub) { + IMXDPUV1_TRACE_IRQ("Fetch layer shadow request 0x%08x\n", sub); + if (sub & 0xff) { /* FETCHLAYER0 */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER0_TRIGGERENABLE, + sub & 0xff); + } +#ifdef IMXDPUV1_VERSION_0 + if (sub & 0xff00) { /* FETCHLAYER1 */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHLAYER1_TRIGGERENABLE, + (sub >> 8) & 0xff); + } +#endif + if (sub & 0xff0000) { /* FETCHWARP2 */ + imxdpuv1_write(imxdpu, IMXDPUV1_FETCHWARP2_TRIGGERENABLE, + (sub >> 16) & 0xff); + } + } + + if (extdst) { + IMXDPUV1_TRACE_IRQ("Extdst shadow request 0x%08x\n", extdst); + imxdpuv1_write(imxdpu, addr_extdst, extdst); + } + + if (fgen) { + IMXDPUV1_TRACE_IRQ("Fgen shadow request 0x%08x\n", fgen); + imxdpuv1_write(imxdpu, addr_fgen, fgen); + } + + return ret; +} + +/*! + * This function shows the frame generators status + * + * @param imxdpuv1_id id of the diplay unit + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_show_fgen_status(int8_t imxdpuv1_id) +{ +#ifndef ENABLE_IMXDPUV1_TRACE + return 0; +#else + int ret = 0; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + IMXDPUV1_PRINT("IMXDPU %d stat fg0 fg1\n" + "prim_sync_state: %10d %10d\n" + "sec_sync_state: %10d %10d\n" + "prim_sync_count: %10d %10d\n" + "sec_sync_count: %10d %10d\n" + "skew_error_count: %10d %10d\n" + "prim_fifo_empty_count: %10d %10d\n" + "sec_fifo_empty_count: %10d %10d\n" + "frame_count: %10d %10d\n" + "irq_count: %10u\n\n", + imxdpuv1_id, + imxdpu->fgen_stats[0].prim_sync_state, + imxdpu->fgen_stats[1].prim_sync_state, + imxdpu->fgen_stats[0].sec_sync_state, + imxdpu->fgen_stats[1].sec_sync_state, + imxdpu->fgen_stats[0].prim_sync_count, + imxdpu->fgen_stats[1].prim_sync_count, + imxdpu->fgen_stats[0].sec_sync_count, + imxdpu->fgen_stats[1].sec_sync_count, + imxdpu->fgen_stats[0].skew_error_count, + imxdpu->fgen_stats[1].skew_error_count, + imxdpu->fgen_stats[0].prim_fifo_empty_count, + imxdpu->fgen_stats[1].prim_fifo_empty_count, + imxdpu->fgen_stats[0].sec_fifo_empty_count, + imxdpu->fgen_stats[1].sec_fifo_empty_count, + imxdpu->fgen_stats[0].frame_count, + imxdpu->fgen_stats[1].frame_count, + imxdpu->irq_count); + + return ret; +#endif +} + +/*! + * This function enables the frame generator + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * @param enable state to set frame generator to + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_enable_frame_gen(int8_t imxdpuv1_id, int8_t disp, bool enable) +{ + int ret = 0; + uint32_t b_off; + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (disp == 0) { + b_off = IMXDPUV1_FRAMEGEN0_LOCKUNLOCK; + } else if (disp == 1) { + b_off = IMXDPUV1_FRAMEGEN1_LOCKUNLOCK; + } else { + return -EINVAL; + } + + imxdpuv1_disp_start_shadow_loads(imxdpuv1_id, disp); + + reg = enable ? IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGENABLE_FGEN, 1) : + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEGEN0_FGENABLE_FGEN, 0); + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_FRAMEGEN0_FGENABLE_OFFSET, reg); + + return ret; +} + +/*! + * This function sets up the constframe generator + * + * @param imxdpuv1_id id of the diplay unit + * @param disp id of the diplay output pipe + * @param bg_red background red + * @param bg_green background green + * @param bg_blue background blue + * @param bg_alpha background alpha + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_setup_constframe( + int8_t imxdpuv1_id, + int8_t disp, + uint8_t bg_red, + uint8_t bg_green, + uint8_t bg_blue, + uint8_t bg_alpha) +{ + int ret = 0; + uint32_t b_off; + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + imxdpuv1_shadow_load_index_t shadow_idx; + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + /* todo: add constfram4 and constframe5 */ + if (disp == 0) { + b_off = IMXDPUV1_CONSTFRAME0_LOCKUNLOCK; + shadow_idx = IMXDPUV1_SHDLD_IDX_CONST0; + } else if (disp == 1) { + b_off = IMXDPUV1_CONSTFRAME1_LOCKUNLOCK; + shadow_idx = IMXDPUV1_SHDLD_IDX_CONST1; + } else { + return -EINVAL; + } + + if (imxdpu->video_mode[disp].flags & IMXDPUV1_MODE_FLAGS_LRSYNC) { + /* todo: need to handle sync display case */ + } + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEHEIGHT, + imxdpu->video_mode[disp].vlen - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEWIDTH, + imxdpu->video_mode[disp].hlen - 1); + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_CONSTFRAME0_FRAMEDIMENSIONS_OFFSET, reg); + + /* todo: add linear light correction if needed */ + imxdpuv1_write(imxdpu, b_off + IMXDPUV1_CONSTFRAME0_CONSTANTCOLOR_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_CONSTRED, bg_red) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_CONSTGREEN, bg_green) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_CONSTBLUE, bg_blue) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_CONSTALPHA, bg_alpha)); + + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, disp, shadow_idx); + + /* todo: add linear light correction if needed */ + return ret; +} + +/*! + * This function sets up a layer + * + * @param imxdpuv1_id id of the diplay unit + * @param layer layer data to use + * @param layer_idx layer index to use + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_setup_layer(int8_t imxdpuv1_id, + const imxdpuv1_layer_t *layer, + imxdpuv1_layer_idx_t layer_idx, + bool is_top_layer) +{ + int ret = 0; + uint32_t dynamic_offset; + uint32_t static_offset; + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + IMXDPUV1_TRACE("%s(): enable %d, primary %d, secondary %d, stream 0x%08x\n", __func__, + layer->enable, + layer->primary, + layer->secondary, + layer->stream); + imxdpu->blend_layer[layer_idx] = *layer; + + dynamic_offset = id2dynamicoffset(layer_idx + IMXDPUV1_ID_LAYERBLEND0); + if (dynamic_offset == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + static_offset = id2blockoffset(layer_idx + IMXDPUV1_ID_LAYERBLEND0); + if (static_offset == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + reg = + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_LAYERBLEND0_DYNAMIC_LAYERBLEND0_PRIM_SEL, + imxdpu->blend_layer[layer_idx].primary) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND0_DYNAMIC_LAYERBLEND0_SEC_SEL, + imxdpu->blend_layer[layer_idx].secondary) | + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_LAYERBLEND0_DYNAMIC_LAYERBLEND0_CLKEN, + IMXDPUV1_PIXENGCFG_LAYERBLEND0_DYNAMIC_LAYERBLEND0_CLKEN__AUTOMATIC); + imxdpuv1_write(imxdpu, dynamic_offset, reg); + + if (imxdpu->blend_layer[layer_idx].stream & IMXDPUV1_DISPLAY_STREAM_0) { + + IMXDPUV1_TRACE("%s(): IMXDPUV1_DISPLAY_STREAM_0\n", __func__); + if (is_top_layer) { + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC_EXTDST0_SRC_SEL, + layer_idx + IMXDPUV1_ID_LAYERBLEND0); + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC, reg); + } + + /* trigger configuration of the pipeline */ + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_TRIGGER, + IMXDPUV1_PIXENGCFG_EXTDST0_TRIGGER_EXTDST0_SYNC_TRIGGER_MASK); + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, 0, + IMXDPUV1_SHDLD_IDX_DISP0); + } + if (imxdpu->blend_layer[layer_idx].stream & IMXDPUV1_DISPLAY_STREAM_1) { + IMXDPUV1_TRACE_IRQ("%s(): IMXDPUV1_DISPLAY_STREAM_1\n", __func__); + if (is_top_layer) { + reg = + IMXDPUV1_SET_FIELD(IMXDPUV1_PIXENGCFG_EXTDST0_DYNAMIC_EXTDST0_SRC_SEL, + layer_idx + IMXDPUV1_ID_LAYERBLEND0); + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_DYNAMIC, reg); + + } + /* trigger configuration of the pipeline */ + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_TRIGGER, + IMXDPUV1_PIXENGCFG_EXTDST1_TRIGGER_EXTDST1_SYNC_TRIGGER_MASK); + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, 1, + IMXDPUV1_SHDLD_IDX_DISP1); + } + + /* todo: add code to disable a layer */ + return ret; +} + +/*! + * This function sets global alpha for a blend layer + * + * @param imxdpuv1_id id of the diplay unit + * @param layer_idx layer index to use + * @param alpha global alpha + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_set_layer_global_alpha(int8_t imxdpuv1_id, + imxdpuv1_layer_idx_t layer_idx, + uint8_t alpha) +{ + int ret = 0; + uint32_t offset; + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + /* update imxdpu */ + + offset = id2blockoffset(layer_idx + IMXDPUV1_ID_LAYERBLEND0); + if (offset == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_BLENDCONTROL_BLENDALPHA, + alpha) + | IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_BLENDCONTROL_PRIM_C_BLD_FUNC, + IMXDPUV1_LAYERBLEND0_BLENDCONTROL_PRIM_C_BLD_FUNC__ONE_MINUS_SEC_ALPHA) + | IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_BLENDCONTROL_SEC_C_BLD_FUNC, + IMXDPUV1_LAYERBLEND0_BLENDCONTROL_SEC_C_BLD_FUNC__CONST_ALPHA) + | IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_BLENDCONTROL_PRIM_A_BLD_FUNC, + IMXDPUV1_LAYERBLEND0_BLENDCONTROL_PRIM_A_BLD_FUNC__ONE_MINUS_SEC_ALPHA) + | IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_BLENDCONTROL_SEC_A_BLD_FUNC, + IMXDPUV1_LAYERBLEND0_BLENDCONTROL_SEC_A_BLD_FUNC__ONE); + imxdpuv1_write(imxdpu, offset + IMXDPUV1_LAYERBLEND0_BLENDCONTROL_OFFSET, + reg); + + reg = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_CONTROL_MODE, + IMXDPUV1_LAYERBLEND0_CONTROL_MODE__BLEND) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_CONTROL_ALPHAMASKENABLE, + IMXDPUV1_DISABLE); + + imxdpuv1_write(imxdpu, offset + IMXDPUV1_LAYERBLEND0_CONTROL_OFFSET, reg); + + return ret; +} + +/*! + * This function sets the position of the a blend layer secondary input + * + * @param imxdpuv1_id id of the diplay unit + * @param layer_idx layer index to use + * @param x x position + * @param y y position + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_set_layer_position(int8_t imxdpuv1_id, + imxdpuv1_layer_idx_t layer_idx, + int16_t x, int16_t y) +{ + int ret = 0; + uint32_t offset; + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + /* update imxdpu */ + + offset = id2blockoffset(layer_idx + IMXDPUV1_ID_LAYERBLEND0); + if (offset == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + reg = IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_POSITION_XPOS, x) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERBLEND0_POSITION_YPOS, y); + imxdpuv1_write(imxdpu, offset + IMXDPUV1_LAYERBLEND0_POSITION_OFFSET, reg); + + return ret; +} + +/*! + * This function sets the position of the a channel (window) layer + * + * @param imxdpuv1_id id of the diplay unit + * @param layer_idx layer index to use + * @param x x position + * @param y y position + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_set_chan_position(int8_t imxdpuv1_id, + imxdpuv1_chan_t chan, int16_t x, int16_t y) +{ + int ret = 0; + uint32_t offset; + int idx; + int sub_idx; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + /* update imxdpu */ + + offset = id2blockoffset(get_channel_blk(chan)); + if (offset == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + idx = get_channel_idx(chan); + if ((idx >= IMXDPUV1_CHAN_IDX_IN_MAX) || (idx < 0)) { + return -EINVAL; + } + + sub_idx = imxdpuv1_get_channel_subindex(chan); + + imxdpu->chan_data[idx].dest_top = y; + imxdpu->chan_data[idx].dest_left = x; + + imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_LAYEROFFSET0_LAYERXOFFSET0, + imxdpu->chan_data[idx].dest_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_LAYEROFFSET0_LAYERYOFFSET0, + imxdpu->chan_data[idx].dest_top); + + if (is_fetch_layer_chan(chan) || is_fetch_warp_chan(chan)) { + IMXDPUV1_TRACE("%s(): fetch layer or warp\n", __func__); + imxdpuv1_write(imxdpu, + offset + IMXDPUV1_FETCHLAYER0_LAYEROFFSET0_OFFSET + + ((IMXDPUV1_SUBCHAN_LAYER_OFFSET * sub_idx)), + imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0); + + } else if (is_fetch_decode_chan(chan)) { + if (imxdpu->chan_data[idx].use_eco_fetch) { + imxdpuv1_disp_set_chan_position(imxdpuv1_id, + imxdpuv1_get_eco(chan), + x, y); + } + imxdpuv1_write(imxdpu, + offset + IMXDPUV1_FETCHDECODE0_LAYEROFFSET0_OFFSET, + imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0); + } else if (is_fetch_eco_chan(chan)) { + imxdpuv1_write(imxdpu, + offset + IMXDPUV1_FETCHECO0_LAYEROFFSET0_OFFSET, + imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0); + } else { + return -EINVAL; + } + + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, + imxdpu->chan_data[idx].disp_id, + IMXDPUV1_SHDLD_IDX_CHAN_00 + idx); + + return ret; +} + +/*! + * This function sets the source and destination crop + * position of the a channel (window) layer + * + * @param imxdpuv1_id id of the diplay unit + * @param chan chan to use + * @param clip_top source y position + * @param clip_left source x position + * @param clip_width source width + * @param clip_height source height + * @param dest_top destination y + * @param dest_left destination x + * @param dest_width destination width + * @param dest_height destination height + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_set_chan_crop( + int8_t imxdpuv1_id, + imxdpuv1_chan_t chan, + int16_t clip_top, + int16_t clip_left, + uint16_t clip_width, + uint16_t clip_height, + int16_t dest_top, + int16_t dest_left, + uint16_t dest_width, + uint16_t dest_height) +{ + int ret = 0; + uint32_t offset; + int idx; + int sub_idx; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + offset = id2blockoffset(get_channel_blk(chan)); + if (offset == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + idx = get_channel_idx(chan); + if ((idx >= IMXDPUV1_CHAN_IDX_IN_MAX) || (idx < 0)) { + return -EINVAL; + } + + sub_idx = imxdpuv1_get_channel_subindex(chan); + + imxdpu->chan_data[idx].dest_top = dest_top; + imxdpu->chan_data[idx].dest_left = dest_left; + imxdpu->chan_data[idx].dest_width = IMXDPUV1_MIN(dest_width, clip_width); + imxdpu->chan_data[idx].dest_height = IMXDPUV1_MIN(dest_height, clip_height); + imxdpu->chan_data[idx].clip_top = clip_top; + imxdpu->chan_data[idx].clip_left = clip_left; + imxdpu->chan_data[idx].clip_width = IMXDPUV1_MIN(dest_width, clip_width); + imxdpu->chan_data[idx].clip_height = IMXDPUV1_MIN(dest_height, clip_height); + + /* Need to check more cases here */ + if ((imxdpu->chan_data[idx].clip_height != 0) && + (imxdpu->chan_data[idx].clip_width != 0)) { + imxdpu->chan_data[idx].fetch_layer_prop.layerproperty0 |= + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE, + IMXDPUV1_ENABLE); + imxdpu->chan_data[idx].fetch_layer_prop.clipwindowdimensions0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_HEIGHT, + imxdpu->chan_data[idx].clip_height - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_WIDTH, + imxdpu->chan_data[idx].clip_width - 1); + } else { + imxdpu->chan_data[idx].fetch_layer_prop.layerproperty0 &= + ~IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE_MASK; + imxdpu->chan_data[idx].fetch_layer_prop.clipwindowdimensions0 = 0; + } + imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYER_XOFFSET, + imxdpu->chan_data[idx].dest_left - imxdpu->chan_data[idx].clip_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYER_YOFFSET, + imxdpu->chan_data[idx].dest_top - imxdpu->chan_data[idx].clip_top); + imxdpu->chan_data[idx].fetch_layer_prop.clipwindowoffset0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_XOFFSET, + imxdpu->chan_data[idx].dest_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_YOFFSET, + imxdpu->chan_data[idx].dest_top); + + if (is_fetch_layer_chan(chan) || is_fetch_warp_chan(chan)) { + imxdpuv1_write_block(imxdpu, + offset + + IMXDPUV1_FETCHLAYER0_LAYEROFFSET0_OFFSET + + ((IMXDPUV1_SUBCHAN_LAYER_OFFSET * sub_idx)), + (void *)&imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0, + 5); + + } else if (is_fetch_decode_chan(chan)) { + if (imxdpu->chan_data[idx].use_eco_fetch) { + imxdpuv1_disp_set_chan_crop(imxdpuv1_id, + imxdpuv1_get_eco(chan), + clip_top, + clip_left, + clip_width, + clip_height, + dest_top, + dest_left, + dest_width, + dest_height); + } + imxdpuv1_write_block(imxdpu, + offset + + IMXDPUV1_FETCHDECODE0_LAYEROFFSET0_OFFSET, + (void *)&imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0, + 5); + } else if (is_fetch_eco_chan(chan)) { + imxdpuv1_write_block(imxdpu, + offset + IMXDPUV1_FETCHECO0_LAYEROFFSET0_OFFSET, + (void *)&imxdpu->chan_data[idx].fetch_layer_prop.layeroffset0, + 5); + + } else { + return -EINVAL; + } + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, + imxdpu->chan_data[idx].disp_id, + IMXDPUV1_SHDLD_IDX_CHAN_00 + idx); + + return ret; +} + +/*! + * This function sets initializes a channel and buffer + * + * @param imxdpuv1_id id of the diplay unit + * @param chan chan to use + * @param src_pixel_fmt source pixel format + * @param clip_top source y position + * @param clip_left source x position + * @param clip_width source width + * @param clip_height source height + * @param stride stride of the buffer + * @param disp_id display id + * @param dest_top destination y + * @param dest_left destination x + * @param dest_width destination width + * @param dest_height destination height + * @param const_color constant color for clip region + * @param disp_addr display buffer physical address + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_disp_setup_channel(int8_t imxdpuv1_id, + imxdpuv1_chan_t chan, + uint32_t src_pixel_fmt, + uint16_t src_width, + uint16_t src_height, + int16_t clip_top, + int16_t clip_left, + uint16_t clip_width, + uint16_t clip_height, + uint16_t stride, + uint8_t disp_id, + int16_t dest_top, + int16_t dest_left, + uint16_t dest_width, + uint16_t dest_height, + uint32_t const_color, + bool use_global_alpha, + bool use_local_alpha, + unsigned int disp_addr) +{ + int ret = 0; + imxdpuv1_channel_params_t channel; + uint32_t uv_offset = 0; + + IMXDPUV1_TRACE("%s(): " + "imxdpuv1_id %d\n" + "chan_t chan %x\n" + "src_pixel_fmt 0x%x\n" + "src_width %d\n" + "src_height %d\n" + "clip_top %d\n" + "clip_left %d\n" + "clip_width %d\n" + "clip_height %d\n" + "stride %d\n" + "disp_id %d\n" + "dest_top %d\n" + "dest_left %d\n" + "dest_width %d\n" + "dest_height %d\n" + "const_color 0x%x\n" + "disp_addr 0x%x\n", + __func__, + imxdpuv1_id, + chan, + src_pixel_fmt, + src_width, + src_height, + clip_top, + clip_left, + clip_width, + clip_height, + stride, + disp_id, + dest_top, + dest_left, + dest_width, + dest_height, + const_color, + disp_addr); + + channel.common.chan = chan; + channel.common.src_pixel_fmt = src_pixel_fmt; + channel.common.src_width = src_width; + channel.common.src_height = src_height; + channel.common.clip_top = clip_top; + channel.common.clip_left = clip_left; + channel.common.clip_width = clip_width; + channel.common.clip_height = clip_height; + channel.common.stride = stride; + channel.common.disp_id = disp_id; + channel.common.dest_top = dest_top; + channel.common.dest_left = dest_left; + channel.common.dest_width = dest_width; + channel.common.dest_height = dest_height; + channel.common.const_color = const_color; + channel.common.use_global_alpha = use_global_alpha; + channel.common.use_local_alpha = use_local_alpha; + + if (imxdpuv1_get_planes(src_pixel_fmt) == 2) { + uv_offset = src_width * src_height; /* works for NV12 and NV16*/ + } + ret = imxdpuv1_init_channel(imxdpuv1_id, &channel); + + ret = imxdpuv1_init_channel_buffer(imxdpuv1_id, channel.common.chan, channel.common.stride, IMXDPUV1_ROTATE_NONE, + disp_addr, + uv_offset, + 0); + + ret = imxdpuv1_disp_set_chan_crop(imxdpuv1_id, + channel.common.chan, + channel.common.clip_top, + channel.common.clip_left, + channel.common.clip_width, + channel.common.clip_height, + channel.common.dest_top, + channel.common.dest_left, + channel.common.dest_width, + channel.common.dest_height); + +#ifdef DEBUG + { + imxdpuv1_chan_t eco_chan; + imxdpuv1_dump_channel(imxdpuv1_id, channel.common.chan); + eco_chan = imxdpuv1_get_eco(channel.common.chan); + if (eco_chan != 0) { + imxdpuv1_dump_channel(imxdpuv1_id, eco_chan); + } + } +#endif + return ret; +} + +/*! + * This function prints the video mode passed as a parameter + * + * @param *mode pointer to video mode struct to show + */ +void imxdpuv1_disp_dump_mode(const struct imxdpuv1_videomode *mode) +{ + IMXDPUV1_PRINT("%s():\n", __func__); + IMXDPUV1_PRINT("\thlen %4d\n", mode->hlen); + IMXDPUV1_PRINT("\thfp %4d\n", mode->hfp); + IMXDPUV1_PRINT("\thbp %4d\n", mode->hbp); + IMXDPUV1_PRINT("\thsync %4d\n", mode->hsync); + IMXDPUV1_PRINT("\tvlen %4d\n", mode->vlen); + IMXDPUV1_PRINT("\tvfp %4d\n", mode->vfp); + IMXDPUV1_PRINT("\tvbp %4d\n", mode->vbp); + IMXDPUV1_PRINT("\tvsync %4d\n", mode->vsync); + IMXDPUV1_PRINT("\tvlen1 %4d\n", mode->vlen1); + IMXDPUV1_PRINT("\tvfp1 %4d\n", mode->vfp1); + IMXDPUV1_PRINT("\tvbp1 %4d\n", mode->vbp1); + IMXDPUV1_PRINT("\tvsync1 %4d\n", mode->vsync1); + + IMXDPUV1_PRINT("\tflags 0x%08x:\n", mode->flags); + + if (mode->flags & IMXDPUV1_MODE_FLAGS_HSYNC_POL) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_HSYNC_POL is high\n"); + else + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_HSYNC_POL is low\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_VSYNC_POL) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_VSYNC_POL is high\n"); + else + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_VSYNC_POL is low\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_DE_POL) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_DE_POL is high\n"); + else + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_DE_POL is low\n"); + + if (mode->flags & IMXDPUV1_MODE_FLAGS_INTERLACED) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_INTERLACED is set\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_LRSYNC) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_LRSYNC is set\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_SPLIT) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_SPLIT is set\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_32BIT) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_32BIT is set\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_BT656_10BIT) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_BT656_10BIT is set\n"); + if (mode->flags & IMXDPUV1_MODE_FLAGS_BT656_8BIT) + IMXDPUV1_PRINT("\t\tIMXDPUV1_MODE_FLAGS_BT656_8BIT is set\n"); +} + +/*! + * Returns the bytes per pixel + * + * @param pixel format + * + * @return returns number of bytes per pixel or zero + * if the format is not matched. + */ +int imxdpuv1_bytes_per_pixel(uint32_t fmt) +{ + IMXDPUV1_TRACE("%s():\n", __func__); + switch (fmt) { + /* todo add NV12, and NV16 */ + case IMXDPUV1_PIX_FMT_NV12: + return 1; /* luma */ + + case IMXDPUV1_PIX_FMT_RGB565: + case IMXDPUV1_PIX_FMT_YUYV: + case IMXDPUV1_PIX_FMT_UYVY: + return 2; + break; + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_RGB24: + case IMXDPUV1_PIX_FMT_YUV444: + return 3; + break; + case IMXDPUV1_PIX_FMT_GENERIC_32: + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_RGB32: + case IMXDPUV1_PIX_FMT_RGBA32: + case IMXDPUV1_PIX_FMT_ABGR32: + case IMXDPUV1_PIX_FMT_AYUV: + return 4; + break; + default: + IMXDPUV1_TRACE("%s(): unsupported pixel format", __func__); + return 0; + } +} + +/*! + * Returns the number of bits per color component for the color + * component bits register + * + * @param pixel format + * + * @return Returns the number of bits per color component for + * the color component bits register. + */ +uint32_t imxdpuv1_get_colorcomponentbits(uint32_t fmt) +{ + IMXDPUV1_TRACE("%s():\n", __func__); + switch (fmt) { + /* todo add NV12, NV16, YUYV, and UYVY */ + case IMXDPUV1_PIX_FMT_YUYV: + case IMXDPUV1_PIX_FMT_UYVY: + return + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSRED0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSGREEN0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSBLUE0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSALPHA0, 0x00); + case IMXDPUV1_PIX_FMT_NV12: + return + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSRED0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSGREEN0, 0x00) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSBLUE0, 0x00) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSALPHA0, 0x00); + + case IMXDPUV1_PIX_FMT_RGB565: + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSRED0, 0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSGREEN0, 5) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSBLUE0, 11) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSALPHA0, 0); + + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_RGB24: + case IMXDPUV1_PIX_FMT_YUV444: + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_RGB32: + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSRED0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSGREEN0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSBLUE0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSALPHA0, 0x0); + + case IMXDPUV1_PIX_FMT_GENERIC_32: + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_RGBA32: + case IMXDPUV1_PIX_FMT_ABGR32: + case IMXDPUV1_PIX_FMT_ARGB32: + case IMXDPUV1_PIX_FMT_AYUV: + return + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSRED0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSGREEN0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSBLUE0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSALPHA0, 0x08); + default: + IMXDPUV1_TRACE("%s(): unsupported pixel format 0x%08x", __func__, fmt); + return 0; + } + return 0; +} + +/*! + * Returns the number of planes for the pixel format + * + * @param pixel format + * + * @return returns number of bytes per pixel or zero + * if the format is not matched. + */ +uint32_t imxdpuv1_get_planes(uint32_t fmt) +{ + IMXDPUV1_TRACE("%s():\n", __func__); + switch (fmt) { + case IMXDPUV1_PIX_FMT_NV16: + case IMXDPUV1_PIX_FMT_NV12: + return 2; + + case IMXDPUV1_PIX_FMT_RGB565: + case IMXDPUV1_PIX_FMT_YUYV: + case IMXDPUV1_PIX_FMT_UYVY: + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_RGBA32: + case IMXDPUV1_PIX_FMT_ABGR32: + case IMXDPUV1_PIX_FMT_AYUV: + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_RGB24: + case IMXDPUV1_PIX_FMT_YUV444: + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_RGB32: + case IMXDPUV1_PIX_FMT_ARGB32: + return 1; + default: + return 0; + IMXDPUV1_TRACE("%s(): unsupported pixel format", __func__); + } +} + +/*! + * Returns the color component bit position shifts + * + * @param pixel format + * + * @return returns the register setting for the + * colorcomponentshift register + * + */ +uint32_t imxdpuv1_get_colorcomponentshift(uint32_t fmt) +{ + IMXDPUV1_TRACE("%s():\n", __func__); + switch (fmt) { + + case IMXDPUV1_PIX_FMT_NV12: + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x0); + + case IMXDPUV1_PIX_FMT_RGB565: + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 5) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 6) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 5) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0); + case IMXDPUV1_PIX_FMT_YUYV: + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x0); + case IMXDPUV1_PIX_FMT_UYVY: + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x0); + + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_BGRA32: + /* 0xaaRRGGBB */ + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x00) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x18); + case IMXDPUV1_PIX_FMT_AYUV: + /* 0xVVUUYYAA */ + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x18) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x00); + + case IMXDPUV1_PIX_FMT_ABGR32: + /* 0xRRGGBBAA */ + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x18) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x00); + + case IMXDPUV1_PIX_FMT_ARGB32: + /* 0xBBGGRRAA */ + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x18) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x00); + case IMXDPUV1_PIX_FMT_GENERIC_32: + case IMXDPUV1_PIX_FMT_RGB24: + case IMXDPUV1_PIX_FMT_YUV444: + case IMXDPUV1_PIX_FMT_RGB32: + case IMXDPUV1_PIX_FMT_RGBA32: + /* 0xaaBBGGRR or 0xaaUUVVYY */ + return IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x00) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x08) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x10) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x18); + default: + return 0; + IMXDPUV1_TRACE("%s(): unsupported pixel format", __func__); + } +} + +/*! + * Returns true is the format has local alpha + * + * @param pixel format + * + * @return Returns true is the format has local alpha + */ +uint32_t imxdpuv1_has_localalpha(uint32_t fmt) +{ + IMXDPUV1_TRACE("%s():\n", __func__); + switch (fmt) { + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_AYUV: + case IMXDPUV1_PIX_FMT_RGBA32: + return IMXDPUV1_TRUE; + default: + return IMXDPUV1_FALSE; + } +} + +/*! + * Returns the bits per pixel + * + * @param pixel format + * + * @return returns number of bits per pixel or zero + * if the format is not matched. + */ +int imxdpuv1_bits_per_pixel(uint32_t fmt) +{ + int ret = 0; + switch (fmt) { + case IMXDPUV1_PIX_FMT_NV12: + ret = 8; + break; + case IMXDPUV1_PIX_FMT_NV16: + case IMXDPUV1_PIX_FMT_RGB565: + case IMXDPUV1_PIX_FMT_YUYV: + case IMXDPUV1_PIX_FMT_UYVY: + case IMXDPUV1_PIX_FMT_YVYU: + ret = 16; + break; + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_RGB24: + case IMXDPUV1_PIX_FMT_YUV444: + ret = 24; + break; + + case IMXDPUV1_PIX_FMT_GENERIC_32: + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_RGB32: + case IMXDPUV1_PIX_FMT_RGBA32: + case IMXDPUV1_PIX_FMT_ABGR32: + case IMXDPUV1_PIX_FMT_ARGB32: + case IMXDPUV1_PIX_FMT_AYUV: + ret = 32; + break; + default: + IMXDPUV1_TRACE("%s(): unsupported pixel format\n", __func__); + ret = 1; + break; + } + IMXDPUV1_TRACE("%s(): fmt 0x%08x, ret %d\n", __func__, fmt, ret); + + return ret; +} + +/*! + * Tests for YUV + * + * @param pixel format + * + * @return returns true if the format is YUV. + */ +static bool imxdpuv1_is_yuv(uint32_t fmt) +{ + int ret = IMXDPUV1_FALSE; + switch (fmt) { + case IMXDPUV1_PIX_FMT_AYUV: + case IMXDPUV1_PIX_FMT_NV12: + case IMXDPUV1_PIX_FMT_NV16: + case IMXDPUV1_PIX_FMT_YUYV: + case IMXDPUV1_PIX_FMT_UYVY: + case IMXDPUV1_PIX_FMT_YUV444: + ret = IMXDPUV1_TRUE; + break; + case IMXDPUV1_PIX_FMT_GENERIC_32: + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_RGB32: + case IMXDPUV1_PIX_FMT_RGBA32: + case IMXDPUV1_PIX_FMT_ABGR32: + case IMXDPUV1_PIX_FMT_ARGB32: + case IMXDPUV1_PIX_FMT_RGB565: + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_RGB24: + ret = IMXDPUV1_FALSE; + break; + + default: + IMXDPUV1_TRACE("%s(): unsupported pixel format", __func__); + ret = IMXDPUV1_FALSE; + break; + } + IMXDPUV1_TRACE("%s(): fmt 0x%08x, ret %d\n", __func__, fmt, ret); + + return ret; +} + +/*! + * Tests for RGB formats + * + * @param pixel format + * + * @return returns true if the format is any supported RGB + */ +bool imxdpuv1_is_rgb(uint32_t fmt) +{ + int ret = IMXDPUV1_FALSE; + switch (fmt) { + case IMXDPUV1_PIX_FMT_AYUV: + case IMXDPUV1_PIX_FMT_NV12: + case IMXDPUV1_PIX_FMT_NV16: + case IMXDPUV1_PIX_FMT_YUYV: + case IMXDPUV1_PIX_FMT_UYVY: + case IMXDPUV1_PIX_FMT_YUV444: + case IMXDPUV1_PIX_FMT_GENERIC_32: + ret = IMXDPUV1_FALSE; + break; + case IMXDPUV1_PIX_FMT_BGR32: + case IMXDPUV1_PIX_FMT_BGRA32: + case IMXDPUV1_PIX_FMT_RGB32: + case IMXDPUV1_PIX_FMT_RGBA32: + case IMXDPUV1_PIX_FMT_ABGR32: + case IMXDPUV1_PIX_FMT_ARGB32: + case IMXDPUV1_PIX_FMT_RGB565: + case IMXDPUV1_PIX_FMT_BGR24: + case IMXDPUV1_PIX_FMT_RGB24: + ret = IMXDPUV1_TRUE; + break; + + default: + IMXDPUV1_TRACE("%s(): unsupported pixel format", __func__); + ret = IMXDPUV1_FALSE; + break; + } + IMXDPUV1_TRACE("%s(): fmt 0x%08x, ret %d\n", __func__, fmt, ret); + + return ret; +} + +/*! + * Intializes buffers to be used for a channel + * + * @param imxdpuv1_id id of the diplay unit + * @param chan channel to use for this buffer + * @param stride total width in the buffer in pixels + * @param rot_mode rotatation mode + * @param phyaddr_0 buffer 0 address + * @param u_offset U offset + * @param v_offset V offset + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_init_channel_buffer( + int8_t imxdpuv1_id, + imxdpuv1_chan_t chan, + uint32_t stride, + imxdpuv1_rotate_mode_t rot_mode, + dma_addr_t phyaddr_0, + uint32_t u_offset, + uint32_t v_offset) +{ + int ret = 0; + uint32_t b_off; + struct imxdpuv1_soc *imxdpu; + imxdpuv1_chan_idx_t chan_idx = get_channel_idx(chan); + int sub_idx = imxdpuv1_get_channel_subindex(chan); + bool enable_clip = IMXDPUV1_FALSE; + bool enable_buffer = IMXDPUV1_TRUE; + uint8_t enable_yuv = IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE__OFF; + uint8_t input_select = IMXDPUV1_FETCHDECODE0_CONTROL_INPUTSELECT__INACTIVE; + uint32_t fwidth; + uint32_t fheight; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (!is_chan(chan)) { + return -EINVAL; + } + + b_off = id2blockoffset(get_channel_blk(chan)); + if (b_off == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + imxdpu->chan_data[chan_idx].phyaddr_0 = phyaddr_0; + imxdpu->chan_data[chan_idx].u_offset = u_offset; + imxdpu->chan_data[chan_idx].v_offset = v_offset; + + /* update stride if provided */ + if (stride != 0) { + /* todo: check stride range */ + imxdpu->chan_data[chan_idx].stride = stride; + } + + /* common fetch setup */ + if (!is_store_chan(chan)) { + /* default horizontal scan + * todo: add support for vertical and warp scans + */ + if (sub_idx == 0) { + imxdpuv1_write(imxdpu, + b_off + + IMXDPUV1_FETCHDECODE0_BURSTBUFFERMANAGEMENT_OFFSET, + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE0_BURSTBUFFERMANAGEMENT_SETBURSTLENGTH, + burst_param[IMXDPUV1_BURST_HORIZONTAL]. + len) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_FETCHDECODE0_BURSTBUFFERMANAGEMENT_SETNUMBUFFERS, + burst_param[IMXDPUV1_BURST_HORIZONTAL].buffers)); + } + /* todo: Add range checking here */ + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0 = phyaddr_0; + imxdpu->chan_data[chan_idx].fetch_layer_prop.sourcebufferattributes0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_ATTR_BITSPERPIXEL, + imxdpuv1_bits_per_pixel( + imxdpu->chan_data[chan_idx].src_pixel_fmt)) | + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_ATTR_STRIDE, + imxdpu->chan_data[chan_idx].stride - 1); + imxdpu->chan_data[chan_idx].fetch_layer_prop.sourcebufferdimension0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_DIMEN_LINECOUNT, + imxdpu->chan_data[chan_idx].src_height - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_DIMEN_LINEWIDTH, + imxdpu->chan_data[chan_idx].src_width - 1); + imxdpu->chan_data[chan_idx].fetch_layer_prop.colorcomponentbits0 = + imxdpuv1_get_colorcomponentbits( + imxdpu->chan_data[chan_idx].src_pixel_fmt); + imxdpu->chan_data[chan_idx].fetch_layer_prop.colorcomponentshift0 = + imxdpuv1_get_colorcomponentshift( + imxdpu->chan_data[chan_idx].src_pixel_fmt); + + imxdpu->chan_data[chan_idx].fetch_layer_prop.layeroffset0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYER_XOFFSET, + imxdpu->chan_data[chan_idx].dest_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYER_YOFFSET, + imxdpu->chan_data[chan_idx].dest_top); + imxdpu->chan_data[chan_idx].fetch_layer_prop.clipwindowoffset0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_XOFFSET, + imxdpu->chan_data[chan_idx].clip_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_YOFFSET, + imxdpu->chan_data[chan_idx].clip_top); + imxdpu->chan_data[chan_idx].fetch_layer_prop.clipwindowdimensions0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_HEIGHT, + imxdpu->chan_data[chan_idx].clip_height - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_WIDTH, + imxdpu->chan_data[chan_idx].clip_width - 1); + if ((imxdpu->chan_data[chan_idx].clip_height != 0) && + (imxdpu->chan_data[chan_idx].clip_width != 0)) { + imxdpu->chan_data[chan_idx].fetch_layer_prop.clipwindowdimensions0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_HEIGHT, + imxdpu->chan_data[chan_idx].clip_height - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_CLIP_WIDTH, + imxdpu->chan_data[chan_idx].clip_width - 1); + + enable_clip = IMXDPUV1_ENABLE; + } else { + imxdpu->chan_data[chan_idx].fetch_layer_prop.clipwindowdimensions0 = 0; + } + + imxdpu->chan_data[chan_idx].fetch_layer_prop.constantcolor0 = + imxdpu->chan_data[chan_idx].const_color; + + if (imxdpu->chan_data[chan_idx].phyaddr_0 == 0) { + enable_buffer = IMXDPUV1_FALSE; + } + if (imxdpuv1_is_yuv(imxdpu->chan_data[chan_idx].src_pixel_fmt)) { + /* TODO: need to get correct encoding range */ + enable_yuv = IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE__ITU601; + } + } + + + if (is_fetch_decode_chan(chan)) { + IMXDPUV1_TRACE("%s(): fetch decode channel\n", __func__); + if (imxdpu->chan_data[chan_idx].use_eco_fetch) { + input_select = IMXDPUV1_FETCHDECODE0_CONTROL_INPUTSELECT__COMPPACK; + if (chan == IMXDPUV1_CHAN_01) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE0_DYNAMIC, + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_FETCHDECODE0_DYNAMIC_FETCHDECODE0_SRC_SEL__FETCHECO0)); + } else if (chan == IMXDPUV1_CHAN_19) { + imxdpuv1_write(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE1_DYNAMIC, + IMXDPUV1_SET_FIELD( + IMXDPUV1_PIXENGCFG_SRC_SEL, + IMXDPUV1_PIXENGCFG_FETCHDECODE1_DYNAMIC_FETCHDECODE1_SRC_SEL__FETCHECO1)); + } + imxdpuv1_init_channel_buffer(imxdpuv1_id, + imxdpuv1_get_eco(chan), + stride, + rot_mode, + phyaddr_0, + u_offset, v_offset); + + imxdpu->chan_data[chan_idx].fetch_layer_prop.colorcomponentbits0 = + (0x08 << IMXDPUV1_FETCHDECODE0_COLORCOMPONENTBITS0_COMPONENTBITSRED0_SHIFT); + imxdpu->chan_data[chan_idx].fetch_layer_prop.colorcomponentshift0 = + (0x00 << IMXDPUV1_FETCHDECODE0_COLORCOMPONENTSHIFT0_COMPONENTSHIFTRED0_SHIFT); + + } /* else need to handle Alpha, Warp, CLUT ... */ + + imxdpu->chan_data[chan_idx].fetch_layer_prop.layerproperty0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE, + enable_buffer) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE, + enable_yuv) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE, + enable_clip) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_ALPHACONSTENABLE, + imxdpu->chan_data[chan_idx].use_global_alpha) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_ALPHASRCENABLE, + imxdpu->chan_data[chan_idx].use_local_alpha); + + /* todo: handle all cases for control register */ + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHDECODE0_CONTROL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_CONTROL_YUV422UPSAMPLINGMODE, + IMXDPUV1_FETCHDECODE0_CONTROL_YUV422UPSAMPLINGMODE__INTERPOLATE) | + IMXDPUV1_FETCHDECODE0_CONTROL_PALETTEIDXWIDTH_MASK | /* needed ?*/ + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_CONTROL_CLIPCOLOR, 1) | /*needed for clip */ + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_CONTROL_INPUTSELECT, input_select)); /*needed for eco */ + + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHDECODE0_FRAMEDIMENSIONS_OFFSET, + IMXDPUV1_SET_FIELD + (IMXDPUV1_FETCHDECODE0_FRAMEDIMENSIONS_FRAMEHEIGHT, + imxdpu->chan_data[chan_idx].dest_height - + 1 /*fheight-1 */) | + IMXDPUV1_SET_FIELD + (IMXDPUV1_FETCHDECODE0_FRAMEDIMENSIONS_FRAMEWIDTH, + imxdpu->chan_data[chan_idx].dest_width - + 1 /*fwidth-1 */)); + + imxdpuv1_write_block(imxdpu, + b_off + IMXDPUV1_FETCHDECODE0_BASEADDRESS0_OFFSET, + (void *)&imxdpu->chan_data[chan_idx]. + fetch_layer_prop, + sizeof(fetch_layer_setup_t) / 4); + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, + imxdpu->chan_data[chan_idx]. + disp_id, + IMXDPUV1_SHDLD_IDX_CHAN_00 + + chan_idx); + } else if (is_fetch_layer_chan(chan)) { + IMXDPUV1_TRACE("%s(): fetch layer channel\n", __func__); + /* here the frame is shared for all sub layers so we use + the video mode dimensions. + fetch layer sub 1 must be setup first + todo: add a check so that any sub layer can set this */ + if (is_fetch_layer_sub_chan1(chan)) { + IMXDPUV1_TRACE("%s(): fetch layer sub channel 1\n", + __func__); + fwidth = + imxdpuv1_array[imxdpuv1_id]. + video_mode[imxdpuv1_array[imxdpuv1_id]. + chan_data[chan_idx].disp_id].hlen; + fheight = + imxdpuv1_array[imxdpuv1_id]. + video_mode[imxdpuv1_array[imxdpuv1_id]. + chan_data[chan_idx].disp_id].vlen; + + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHLAYER0_CONTROL_OFFSET, + IMXDPUV1_FETCHDECODE0_CONTROL_PALETTEIDXWIDTH_MASK | /* needed ?*/ + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHDECODE0_CONTROL_CLIPCOLOR, 1) + ); /*needed for eco */ + + imxdpuv1_write(imxdpu, + b_off + + IMXDPUV1_FETCHLAYER0_FRAMEDIMENSIONS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEHEIGHT, + /*imxdpu->chan_data[chan_idx].dest_height-1 */ + fheight - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEWIDTH, + /*imxdpu->chan_data[chan_idx].dest_width-1 */ + fwidth - 1)); + } + imxdpu->chan_data[chan_idx].fetch_layer_prop.layerproperty0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE, + enable_buffer) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE, + enable_yuv) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE, + enable_clip) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_ALPHACONSTENABLE, + imxdpu->chan_data[chan_idx].use_global_alpha) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_ALPHASRCENABLE, + imxdpu->chan_data[chan_idx].use_local_alpha); + + imxdpuv1_write_block(imxdpu, + b_off + + IMXDPUV1_FETCHLAYER0_BASEADDRESS0_OFFSET + + ((IMXDPUV1_SUBCHAN_LAYER_OFFSET * sub_idx)), + (void *)&imxdpu->chan_data[chan_idx]. + fetch_layer_prop, + sizeof(fetch_layer_setup_t) / 4); + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHLAYER0_TRIGGERENABLE_OFFSET, + get_channel_sub(chan)); + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, + imxdpu->chan_data[chan_idx]. + disp_id, + IMXDPUV1_SHDLD_IDX_CHAN_00 + + chan_idx); + } else if (is_fetch_warp_chan(chan)) { + /* here the frame is shared for all sub layers so we use + the video mode dimensions. + fetch layer sub 1 must be setup first + todo: add a check so that any sub layer can set this */ + if (is_fetch_layer_sub_chan1(chan)) { + IMXDPUV1_TRACE("%s(): fetch layer sub channel 1\n", + __func__); + fwidth = + imxdpuv1_array[imxdpuv1_id]. + video_mode[imxdpuv1_array[imxdpuv1_id]. + chan_data[chan_idx].disp_id].hlen; + fheight = + imxdpuv1_array[imxdpuv1_id]. + video_mode[imxdpuv1_array[imxdpuv1_id]. + chan_data[chan_idx].disp_id].vlen; + + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHWARP2_CONTROL_OFFSET, 0x700); + + imxdpuv1_write(imxdpu, + b_off + + IMXDPUV1_FETCHLAYER0_FRAMEDIMENSIONS_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEHEIGHT, + /*imxdpu->chan_data[chan_idx].dest_height-1 */ + fheight - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FRAMEWIDTH, + /*imxdpu->chan_data[chan_idx].dest_width-1 */ + fwidth - 1)); + } + imxdpu->chan_data[chan_idx].fetch_layer_prop.layerproperty0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE, + enable_buffer) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE, + enable_yuv) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE, + enable_clip) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_ALPHACONSTENABLE, + imxdpu->chan_data[chan_idx].use_global_alpha) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_ALPHASRCENABLE, + imxdpu->chan_data[chan_idx].use_local_alpha); + + imxdpuv1_write_block(imxdpu, + b_off + + IMXDPUV1_FETCHWARP2_BASEADDRESS0_OFFSET + + (IMXDPUV1_SUBCHAN_LAYER_OFFSET * sub_idx), + (void *)&imxdpu->chan_data[chan_idx]. + fetch_layer_prop, + sizeof(fetch_layer_setup_t) / 4); + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHWARP2_TRIGGERENABLE_OFFSET, + get_channel_sub(chan)); + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, + imxdpu->chan_data[chan_idx]. + disp_id, + IMXDPUV1_SHDLD_IDX_CHAN_00 + + chan_idx); + } else if (is_fetch_eco_chan(chan)) { + IMXDPUV1_TRACE("%s(): fetch eco setup\n", __func__); + if (imxdpu->chan_data[chan_idx].src_pixel_fmt == IMXDPUV1_PIX_FMT_NV12) { + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0 = phyaddr_0 + u_offset; + imxdpu->chan_data[chan_idx].fetch_layer_prop.sourcebufferattributes0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_ATTR_BITSPERPIXEL, 16) | + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_ATTR_STRIDE, + imxdpu->chan_data[chan_idx].stride - 1); + + /* chroma resolution*/ + imxdpu->chan_data[chan_idx].fetch_layer_prop.sourcebufferdimension0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_DIMEN_LINECOUNT, + imxdpu->chan_data[chan_idx].src_height / 2 - 1) | + IMXDPUV1_SET_FIELD(IMXDPUV1_BUFF_DIMEN_LINEWIDTH, + imxdpu->chan_data[chan_idx].src_width / 2 - 1); + + imxdpu->chan_data[chan_idx].fetch_layer_prop.colorcomponentbits0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSRED0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSGREEN0, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSBLUE0, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_BITSALPHA0, 0x0); + + imxdpu->chan_data[chan_idx].fetch_layer_prop.colorcomponentshift0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTRED0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTGREEN0, 0x0) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTBLUE0, 0x8) | + IMXDPUV1_SET_FIELD(IMXDPUV1_COLOR_SHIFTALPHA0, 0x0); + imxdpu->chan_data[chan_idx].fetch_layer_prop.layerproperty0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE, + enable_buffer) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE, + enable_clip); + + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHECO0_FRAMERESAMPLING_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO0_FRAMERESAMPLING_DELTAX, 0x2) | + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO0_FRAMERESAMPLING_DELTAY, 0x2) + ); + + /* todo: handle all cases for control register */ + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHECO0_CONTROL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_FETCHECO0_CONTROL_CLIPCOLOR, 1)); + + /* luma resolution */ + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_FETCHECO0_FRAMEDIMENSIONS_OFFSET, + IMXDPUV1_SET_FIELD + (IMXDPUV1_FETCHECO0_FRAMEDIMENSIONS_FRAMEHEIGHT, + imxdpu->chan_data[chan_idx].dest_height - + 1 /*fheight-1 */) | + IMXDPUV1_SET_FIELD + (IMXDPUV1_FETCHECO0_FRAMEDIMENSIONS_FRAMEWIDTH, + imxdpu->chan_data[chan_idx].dest_width - + 1 /*fwidth-1 */)); + + } /* else need to handle Alpha, Warp, CLUT ... */ + + imxdpu->chan_data[chan_idx].fetch_layer_prop.layerproperty0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE, + enable_buffer) | + IMXDPUV1_SET_FIELD(IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE, + enable_clip); + + imxdpuv1_write_block(imxdpu, + b_off + IMXDPUV1_FETCHECO0_BASEADDRESS0_OFFSET, + (void *)&imxdpu->chan_data[chan_idx]. + fetch_layer_prop, + sizeof(fetch_layer_setup_t) / 4); + + imxdpuv1_disp_request_shadow_load(imxdpuv1_id, + imxdpu->chan_data[chan_idx]. + disp_id, + IMXDPUV1_SHDLD_IDX_CHAN_00 + + chan_idx); + + } else if (is_store_chan(chan)) { + imxdpu->chan_data[chan_idx].store_layer_prop.baseaddress0 = phyaddr_0; + imxdpu->chan_data[chan_idx].store_layer_prop.destbufferattributes0 = + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE9_DESTINATIONBUFFERATTRIBUTES_BITSPERPIXEL, + imxdpuv1_bits_per_pixel( + imxdpu->chan_data[chan_idx].dest_pixel_fmt)) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE9_DESTINATIONBUFFERATTRIBUTES_STRIDE, + imxdpu->chan_data[chan_idx].stride-1); + imxdpu->chan_data[chan_idx].store_layer_prop.destbufferdimension0 = + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE9_DESTINATIONBUFFERDIMENSION_LINECOUNT, + imxdpu->chan_data[chan_idx].dest_height - 1) | + IMXDPUV1_SET_FIELD( + IMXDPUV1_STORE9_DESTINATIONBUFFERDIMENSION_LINEWIDTH, + imxdpu->chan_data[chan_idx].dest_width - 1); + imxdpu->chan_data[chan_idx].store_layer_prop.colorcomponentbits0 = + imxdpuv1_get_colorcomponentbits( + imxdpu->chan_data[chan_idx].dest_pixel_fmt); + imxdpu->chan_data[chan_idx].store_layer_prop.colorcomponentshift0 = + imxdpuv1_get_colorcomponentshift( + imxdpu->chan_data[chan_idx].dest_pixel_fmt); + imxdpu->chan_data[chan_idx].store_layer_prop.frameoffset0 = + IMXDPUV1_SET_FIELD(IMXDPUV1_STORE9_FRAMEOFFSET_FRAMEXOFFSET, + -imxdpu->chan_data[chan_idx].dest_left) | + IMXDPUV1_SET_FIELD(IMXDPUV1_STORE9_FRAMEOFFSET_FRAMEYOFFSET, + -imxdpu->chan_data[chan_idx].dest_top); + + + imxdpuv1_write_block(imxdpu, + b_off + IMXDPUV1_STORE9_BASEADDRESS_OFFSET, + (void *)&imxdpu->chan_data[chan_idx]. + store_layer_prop, + sizeof(store_layer_setup_t) / 4); + + if ((imxdpu->chan_data[chan_idx].dest_pixel_fmt == IMXDPUV1_PIX_FMT_YUYV) || + (imxdpu->chan_data[chan_idx].dest_pixel_fmt == IMXDPUV1_PIX_FMT_YVYU) || + (imxdpu->chan_data[chan_idx].dest_pixel_fmt == IMXDPUV1_PIX_FMT_UYVY)) { + imxdpuv1_write(imxdpu, + b_off + IMXDPUV1_STORE9_CONTROL_OFFSET, + IMXDPUV1_SET_FIELD(IMXDPUV1_STORE9_CONTROL_RASTERMODE, + IMXDPUV1_STORE9_CONTROL_RASTERMODE__YUV422)); + } + + } + + /* imxdpuv1_dump_channel(imxdpuv1_id, chan); */ + + return ret; +} + +/*! + * Intializes a channel + * + * @param imxdpuv1_id id of the diplay unit + * @param chan channel to update + * @param phyaddr_0 physical address + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t imxdpuv1_update_channel_buffer( + int8_t imxdpuv1_id, + imxdpuv1_chan_t chan, + dma_addr_t phyaddr_0) +{ + int ret = 0; + uint32_t b_off; /* block offset for frame generator */ + struct imxdpuv1_soc *imxdpu; + imxdpuv1_chan_idx_t chan_idx = get_channel_idx(chan); + + IMXDPUV1_TRACE_IRQ("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (!is_chan(chan)) { + return -EINVAL; + } + + b_off = id2blockoffset(get_channel_blk(chan)); + if (b_off == IMXDPUV1_OFFSET_INVALID) { + return -EINVAL; + } + + if (imxdpu->chan_data[chan_idx].use_eco_fetch == IMXDPUV1_FALSE) { + imxdpu->chan_data[chan_idx].phyaddr_0 = phyaddr_0; + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0 = phyaddr_0; + } +#ifdef IMXDPUV1_VERSION_0 + if (is_store_chan(chan)) { + IMXDPUV1_TRACE_IRQ("%s(): store channel\n", __func__); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_STORE4_BASEADDRESS_OFFSET, + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0); + + /* fixme: need to handle all pipline elements */ + imxdpuv1_write_irq(imxdpu, IMXDPUV1_PIXENGCFG_STORE4_REQUEST, 1); + + return ret; + } +#endif + if (is_fetch_decode_chan(chan)) { + IMXDPUV1_TRACE_IRQ("%s(): fetch decode channel\n", __func__); + if (imxdpu->chan_data[chan_idx].use_eco_fetch) { + imxdpuv1_update_channel_buffer(imxdpuv1_id, + imxdpuv1_get_eco(chan), + phyaddr_0); + } + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHDECODE0_BASEADDRESS0_OFFSET, + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHDECODE0_CONTROLTRIGGER_OFFSET, + IMXDPUV1_FETCHDECODE0_CONTROLTRIGGER_SHDTOKGEN_MASK); + } else if (is_fetch_layer_chan(chan)) { + IMXDPUV1_TRACE_IRQ("%s(): fetch layer channel\n", __func__); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHLAYER0_BASEADDRESS0_OFFSET, + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHLAYER0_TRIGGERENABLE_OFFSET, + get_channel_sub(chan)); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHLAYER0_CONTROLTRIGGER_OFFSET, + IMXDPUV1_FETCHLAYER0_CONTROLTRIGGER_SHDTOKGEN_MASK); + } else if (is_fetch_warp_chan(chan)) { + IMXDPUV1_TRACE_IRQ("%s(): fetch warp channel\n", __func__); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHWARP2_BASEADDRESS0_OFFSET, + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHWARP2_TRIGGERENABLE_OFFSET, + get_channel_sub(chan)); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHWARP2_CONTROLTRIGGER_OFFSET, + IMXDPUV1_FETCHWARP2_CONTROLTRIGGER_SHDTOKGEN_MASK); + } else if (is_fetch_eco_chan(chan)) { + IMXDPUV1_TRACE_IRQ("%s(): fetch eco channel\n", __func__); + + imxdpu->chan_data[chan_idx].phyaddr_0 = phyaddr_0 + imxdpu->chan_data[chan_idx].u_offset; + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0 = imxdpu->chan_data[chan_idx].phyaddr_0; + + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHDECODE0_BASEADDRESS0_OFFSET, + imxdpu->chan_data[chan_idx].fetch_layer_prop.baseaddress0); + imxdpuv1_write_irq(imxdpu, + b_off + IMXDPUV1_FETCHECO0_CONTROLTRIGGER_OFFSET, + IMXDPUV1_FETCHECO0_CONTROLTRIGGER_SHDTOKGEN_MASK); + } + + return ret; +} + +/*! + * Intializes a channel + * + * @param imxdpuv1_id id of the diplay unit + * @param params pointer to channel parameters + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_init_channel(int8_t imxdpuv1_id, imxdpuv1_channel_params_t *params) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + imxdpuv1_chan_t chan = params->common.chan; + imxdpuv1_chan_idx_t chan_idx = get_channel_idx(chan); + /* here we use the video mode for channel frame width, todo: we may need to + add a paramter for this */ + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (!is_chan(chan)) { + return -EINVAL; + } + imxdpu->chan_data[chan_idx].chan = chan; + + memset(&imxdpu->chan_data[chan_idx].fetch_layer_prop, 0, + sizeof(fetch_layer_setup_t)); + imxdpu->chan_data[chan_idx].use_eco_fetch = IMXDPUV1_FALSE; + + if (is_fetch_decode_chan(chan)) { + IMXDPUV1_TRACE("%s(): decode channel setup\n", __func__); + imxdpu->chan_data[chan_idx].src_pixel_fmt = + params->fetch_decode.src_pixel_fmt; + imxdpu->chan_data[chan_idx].src_width = + params->fetch_decode.src_width; + imxdpu->chan_data[chan_idx].src_height = + params->fetch_decode.src_height; + imxdpu->chan_data[chan_idx].clip_top = + params->fetch_decode.clip_top; + imxdpu->chan_data[chan_idx].clip_left = + params->fetch_decode.clip_left; + imxdpu->chan_data[chan_idx].clip_width = + params->fetch_decode.clip_width; + imxdpu->chan_data[chan_idx].clip_height = + params->fetch_decode.clip_height; + imxdpu->chan_data[chan_idx].stride = + params->fetch_decode.stride; + imxdpu->chan_data[chan_idx].dest_pixel_fmt = + params->fetch_decode.dest_pixel_fmt; + imxdpu->chan_data[chan_idx].dest_top = + params->fetch_decode.dest_top; + imxdpu->chan_data[chan_idx].dest_left = + params->fetch_decode.dest_left; + imxdpu->chan_data[chan_idx].dest_width = + params->fetch_decode.dest_width; + imxdpu->chan_data[chan_idx].dest_height = + params->fetch_decode.dest_height; + imxdpu->chan_data[chan_idx].const_color = + params->fetch_decode.const_color; + imxdpu->chan_data[chan_idx].use_global_alpha = + params->fetch_decode.use_global_alpha; + imxdpu->chan_data[chan_idx].use_local_alpha = + params->fetch_decode.use_local_alpha; + imxdpu->chan_data[chan_idx].disp_id = + params->fetch_decode.disp_id; + + if (imxdpu->chan_data[chan_idx].use_video_proc == + IMXDPUV1_TRUE) { + imxdpu->chan_data[chan_idx].h_scale_factor = + params->fetch_decode.h_scale_factor; + imxdpu->chan_data[chan_idx].h_phase = + params->fetch_decode.h_phase; + imxdpu->chan_data[chan_idx].v_scale_factor = + params->fetch_decode.v_scale_factor; + imxdpu->chan_data[chan_idx].v_phase[0][0] = + params->fetch_decode.v_phase[0][0]; + imxdpu->chan_data[chan_idx].v_phase[0][1] = + params->fetch_decode.v_phase[0][1]; + imxdpu->chan_data[chan_idx].v_phase[1][0] = + params->fetch_decode.v_phase[1][0]; + imxdpu->chan_data[chan_idx].v_phase[1][1] = + params->fetch_decode.v_phase[1][1]; + } + + if (imxdpuv1_get_planes(imxdpu->chan_data[chan_idx].src_pixel_fmt) == 2) { + if (has_fetch_eco_chan(chan)) { + imxdpuv1_channel_params_t temp_params = *params; + + imxdpu->chan_data[chan_idx].use_eco_fetch = IMXDPUV1_TRUE; + temp_params.fetch_decode.chan = imxdpuv1_get_eco(params->fetch_decode.chan); + imxdpuv1_init_channel(imxdpuv1_id, &temp_params); + } else { + return -EINVAL; + } + } + } else if (is_fetch_layer_chan(chan)) { + IMXDPUV1_TRACE("%s(): layer channel setup\n", __func__); + imxdpu->chan_data[chan_idx].src_pixel_fmt = + params->fetch_layer.src_pixel_fmt; + imxdpu->chan_data[chan_idx].src_width = + params->fetch_layer.src_width; + imxdpu->chan_data[chan_idx].src_height = + params->fetch_layer.src_height; + imxdpu->chan_data[chan_idx].clip_top = + params->fetch_layer.clip_top; + imxdpu->chan_data[chan_idx].clip_left = + params->fetch_layer.clip_left; + imxdpu->chan_data[chan_idx].clip_width = + params->fetch_layer.clip_width; + imxdpu->chan_data[chan_idx].clip_height = + params->fetch_layer.clip_height; + imxdpu->chan_data[chan_idx].stride = + params->fetch_layer.stride; + imxdpu->chan_data[chan_idx].dest_pixel_fmt = + params->fetch_layer.dest_pixel_fmt; + imxdpu->chan_data[chan_idx].dest_top = + params->fetch_layer.dest_top; + imxdpu->chan_data[chan_idx].dest_left = + params->fetch_layer.dest_left; + imxdpu->chan_data[chan_idx].dest_width = + params->fetch_layer.dest_width; + imxdpu->chan_data[chan_idx].dest_height = + params->fetch_layer.dest_height; + imxdpu->chan_data[chan_idx].const_color = + params->fetch_layer.const_color; + imxdpu->chan_data[chan_idx].use_global_alpha = + params->fetch_layer.use_global_alpha; + imxdpu->chan_data[chan_idx].use_local_alpha = + params->fetch_layer.use_local_alpha; + imxdpu->chan_data[chan_idx].disp_id = + params->fetch_layer.disp_id; + + } else if (is_fetch_warp_chan(chan)) { + IMXDPUV1_TRACE("%s(): warp channel setup\n", __func__); + + imxdpu->chan_data[chan_idx].src_pixel_fmt = + params->fetch_warp.src_pixel_fmt; + imxdpu->chan_data[chan_idx].src_width = + params->fetch_warp.src_width; + imxdpu->chan_data[chan_idx].src_height = + params->fetch_warp.src_height; + imxdpu->chan_data[chan_idx].clip_top = + params->fetch_warp.clip_top; + imxdpu->chan_data[chan_idx].clip_left = + params->fetch_warp.clip_left; + imxdpu->chan_data[chan_idx].clip_width = + params->fetch_warp.clip_width; + imxdpu->chan_data[chan_idx].clip_height = + params->fetch_warp.clip_height; + imxdpu->chan_data[chan_idx].stride = + params->fetch_warp.stride; + imxdpu->chan_data[chan_idx].dest_pixel_fmt = + params->fetch_warp.dest_pixel_fmt; + imxdpu->chan_data[chan_idx].dest_top = + params->fetch_warp.dest_top; + imxdpu->chan_data[chan_idx].dest_left = + params->fetch_warp.dest_left; + imxdpu->chan_data[chan_idx].dest_width = + params->fetch_warp.dest_width; + imxdpu->chan_data[chan_idx].dest_height = + params->fetch_warp.dest_height; + imxdpu->chan_data[chan_idx].const_color = + params->fetch_warp.const_color; + imxdpu->chan_data[chan_idx].use_global_alpha = + params->fetch_warp.use_global_alpha; + imxdpu->chan_data[chan_idx].use_local_alpha = + params->fetch_warp.use_local_alpha; + imxdpu->chan_data[chan_idx].disp_id = + params->fetch_warp.disp_id; + + } else if (is_fetch_eco_chan(chan)) { + + IMXDPUV1_TRACE("%s(): fetch eco channel setup\n", __func__); + imxdpu->chan_data[chan_idx].src_pixel_fmt = + params->fetch_decode.src_pixel_fmt; + imxdpu->chan_data[chan_idx].src_width = + params->fetch_decode.src_width; + imxdpu->chan_data[chan_idx].src_height = + params->fetch_decode.src_height; + imxdpu->chan_data[chan_idx].clip_top = + params->fetch_decode.clip_top; + imxdpu->chan_data[chan_idx].clip_left = + params->fetch_decode.clip_left; + imxdpu->chan_data[chan_idx].clip_width = + params->fetch_decode.clip_width; + imxdpu->chan_data[chan_idx].clip_height = + params->fetch_decode.clip_height; + imxdpu->chan_data[chan_idx].stride = + params->fetch_decode.stride; + imxdpu->chan_data[chan_idx].dest_pixel_fmt = + params->fetch_decode.dest_pixel_fmt; + imxdpu->chan_data[chan_idx].dest_top = + params->fetch_decode.dest_top; + imxdpu->chan_data[chan_idx].dest_left = + params->fetch_decode.dest_left; + imxdpu->chan_data[chan_idx].dest_width = + params->fetch_decode.dest_width; + imxdpu->chan_data[chan_idx].dest_height = + params->fetch_decode.dest_height; + imxdpu->chan_data[chan_idx].const_color = + params->fetch_decode.const_color; + imxdpu->chan_data[chan_idx].use_global_alpha = + params->fetch_decode.use_global_alpha; + imxdpu->chan_data[chan_idx].use_local_alpha = + params->fetch_decode.use_local_alpha; + imxdpu->chan_data[chan_idx].disp_id = + params->fetch_decode.disp_id; + + if (imxdpu->chan_data[chan_idx].use_video_proc == + IMXDPUV1_TRUE) { + imxdpu->chan_data[chan_idx].h_scale_factor = + params->fetch_decode.h_scale_factor; + imxdpu->chan_data[chan_idx].h_phase = + params->fetch_decode.h_phase; + imxdpu->chan_data[chan_idx].v_scale_factor = + params->fetch_decode.v_scale_factor; + imxdpu->chan_data[chan_idx].v_phase[0][0] = + params->fetch_decode.v_phase[0][0]; + imxdpu->chan_data[chan_idx].v_phase[0][1] = + params->fetch_decode.v_phase[0][1]; + imxdpu->chan_data[chan_idx].v_phase[1][0] = + params->fetch_decode.v_phase[1][0]; + imxdpu->chan_data[chan_idx].v_phase[1][1] = + params->fetch_decode.v_phase[1][1]; + } + + } else if (is_store_chan(chan)) { + IMXDPUV1_TRACE("%s(): store setup\n", __func__); + imxdpu->chan_data[chan_idx].src_pixel_fmt = + params->store.src_pixel_fmt; + imxdpu->chan_data[chan_idx].src_width = + params->store.src_width; + imxdpu->chan_data[chan_idx].src_height = + params->store.src_height; + imxdpu->chan_data[chan_idx].clip_top = + params->store.clip_top; + imxdpu->chan_data[chan_idx].clip_left = + params->store.clip_left; + imxdpu->chan_data[chan_idx].clip_width = + params->store.clip_width; + imxdpu->chan_data[chan_idx].clip_height = + params->store.clip_height; + imxdpu->chan_data[chan_idx].stride = + params->store.stride; + imxdpu->chan_data[chan_idx].dest_pixel_fmt = + params->store.dest_pixel_fmt; + imxdpu->chan_data[chan_idx].dest_top = + params->store.dest_top; + imxdpu->chan_data[chan_idx].dest_left = + params->store.dest_left; + imxdpu->chan_data[chan_idx].dest_width = + params->store.dest_width; + imxdpu->chan_data[chan_idx].dest_height = + params->store.dest_height; + imxdpu->chan_data[chan_idx].const_color = + params->store.const_color; + imxdpu->chan_data[chan_idx].source_id = + params->store.capture_id; + + if (imxdpu->chan_data[chan_idx].use_video_proc == + IMXDPUV1_TRUE) { + imxdpu->chan_data[chan_idx].h_scale_factor = + params->store.h_scale_factor; + imxdpu->chan_data[chan_idx].h_phase = + params->store.h_phase; + imxdpu->chan_data[chan_idx].v_scale_factor = + params->store.v_scale_factor; + imxdpu->chan_data[chan_idx].v_phase[0][0] = + params->store.v_phase[0][0]; + imxdpu->chan_data[chan_idx].v_phase[0][1] = + params->store.v_phase[0][1]; + imxdpu->chan_data[chan_idx].v_phase[1][0] = + params->store.v_phase[1][0]; + imxdpu->chan_data[chan_idx].v_phase[1][1] = + params->store.v_phase[1][1]; + } + + } else { + IMXDPUV1_TRACE("%s(): ERROR, invalid channel type!\n", __func__); + return -EINVAL; + } + + /* imxdpuv1_dump_channel(imxdpuv1_id, chan); */ + + return ret; +} + +/*! + * Dumps the fetch layer properties structure for a channel. + * + * @param layer id of the diplay unit + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +void imxdpuv1_dump_fetch_layer(fetch_layer_setup_t *layer) +{ + IMXDPUV1_PRINT("baseaddress 0x%08x\n" + "sourcebufferattributes 0x%08x\n" + "sourcebufferdimension h %d w %d\n" + "colorcomponentbits 0x%08x\n" + "colorcomponentshift 0x%08x\n" + "layeroffset y(top) %d x(left) %d\n" + "clipwindowoffset y(top) %d x(left) %d\n" + "clipwindowdimensions h %d w %d\n" + "constantcolor 0x%08x\n" + "layerproperty 0x%08x\n", + layer->baseaddress0, + layer->sourcebufferattributes0, + layer->sourcebufferdimension0 >> 16, + layer->sourcebufferdimension0 & 0x3fff, + layer->colorcomponentbits0, layer->colorcomponentshift0, + layer->layeroffset0 >> 16, layer->layeroffset0 & 0x3fff, + layer->clipwindowoffset0 >> 16, + layer->clipwindowoffset0 & 0x3fff, + layer->clipwindowdimensions0 >> 16, + layer->clipwindowdimensions0 & 0x3fff, + layer->constantcolor0, layer->layerproperty0); + return; +} +/*! + * Dumps the store layer properties structure for a channel. + * + * @param layer id of the diplay unit + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +void imxdpuv1_dump_store_layer(store_layer_setup_t *layer) +{ + IMXDPUV1_TRACE( + "baseaddress0 0x%08x\n" + "destbufferattributes0 0x%08x\n" + "destbufferdimension0 h %d w %d\n" + "frameoffset0 %d\n" + "colorcomponentbits0 0x%08x\n" + "colorcomponentshift0 0x%08x\n", + layer->baseaddress0, + layer->destbufferattributes0, + layer->destbufferdimension0 >> 16, layer->destbufferdimension0 & 0x3fff, + layer->frameoffset0, + layer->colorcomponentbits0, + layer->colorcomponentshift0); + return; +} + +/*! + * Dumps the pixel engine configuration status + * + * @param imxdpuv1_id id of the diplay unit + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +void imxdpuv1_dump_layerblend(int8_t imxdpuv1_id) +{ + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND0_STATUS); + IMXDPUV1_TRACE("LAYERBLEND0_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND0_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND0_LOCKSTATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND1_STATUS); + IMXDPUV1_PRINT("LAYERBLEND1_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND1_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND1_LOCKSTATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND2_STATUS); + IMXDPUV1_PRINT("LAYERBLEND2_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND2_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND2_LOCKSTATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND3_STATUS); + IMXDPUV1_PRINT("LAYERBLEND3_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND3_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND3_LOCKSTATUS: 0x%08x\n", reg); +#ifdef IMXDPUV1_VERSION_0 + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND4_STATUS); + IMXDPUV1_PRINT("LAYERBLEND4_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND4_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND4_LOCKSTATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND5_STATUS); + IMXDPUV1_PRINT("LAYERBLEND5_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND5_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND5_LOCKSTATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND6_STATUS); + IMXDPUV1_PRINT("LAYERBLEND6_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_LAYERBLEND6_LOCKSTATUS); + IMXDPUV1_PRINT("LAYERBLEND6_LOCKSTATUS: 0x%08x\n", reg); +#endif + return; +} + +/*! + * Dumps the pixel engine configuration status + * + * @param imxdpuv1_id id of the diplay unit + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +void imxdpuv1_dump_pixencfg_status(int8_t imxdpuv1_id) +{ + uint32_t reg; + struct imxdpuv1_soc *imxdpu; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return; + } + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_REQUEST); + IMXDPUV1_PRINT("EXTDST0_REQUEST: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_REQUEST); + IMXDPUV1_PRINT("EXTDST1_REQUEST: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST4_REQUEST); + IMXDPUV1_PRINT("EXTDST4_REQUEST: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST5_REQUEST); + IMXDPUV1_PRINT("EXTDST5_REQUEST: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST0_STATUS); + IMXDPUV1_PRINT("EXTDST0_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST1_STATUS); + IMXDPUV1_PRINT("EXTDST1_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST4_STATUS); + IMXDPUV1_PRINT("EXTDST4_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_EXTDST5_STATUS); + IMXDPUV1_PRINT("EXTDST5_STATUS: 0x%08x\n", reg); +#ifdef IMXDPUV1_VERSION_0 + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE2_STATUS); + IMXDPUV1_PRINT("FETCHDECODE2_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE3_STATUS); + IMXDPUV1_PRINT("FETCHDECODE3_STATUS: 0x%08x\n", reg); +#endif + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHWARP2_STATUS); + IMXDPUV1_PRINT("FETCHWARP2_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHECO2_STATUS); + IMXDPUV1_PRINT("FETCHECO2_STATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE0_STATUS); + IMXDPUV1_PRINT("FETCHDECODE0_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHECO0_STATUS); + IMXDPUV1_PRINT("FETCHECO0_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHDECODE1_STATUS); + IMXDPUV1_PRINT("FETCHDECODE1_STATUS: 0x%08x\n", reg); + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHECO1_STATUS); + IMXDPUV1_PRINT("FETCHECO1_STATUS: 0x%08x\n", reg); + + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHLAYER0_STATUS); + IMXDPUV1_PRINT("FETCHLAYER0_STATUS: 0x%08x\n", reg); +#ifdef IMXDPUV1_VERSION_0 + reg = imxdpuv1_read(imxdpu, IMXDPUV1_PIXENGCFG_FETCHLAYER1_STATUS); + IMXDPUV1_PRINT("FETCHLAYER1_STATUS: 0x%08x\n", reg); +#endif + return; +} + +/*! + * Dumps the channel data + * + * @param imxdpuv1_id id of the diplay unit + * @param chan channel to dump + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int imxdpuv1_dump_channel(int8_t imxdpuv1_id, imxdpuv1_chan_t chan) +{ + int ret = 0; + struct imxdpuv1_soc *imxdpu; + imxdpuv1_chan_idx_t chan_idx = get_channel_idx(chan); + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return -EINVAL; + } + + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + if (!is_chan(chan)) { + return -EINVAL; + } + if (is_store_chan(chan)) { + IMXDPUV1_PRINT("chan_id 0x%x\n" + "src_pixel_fmt 0x%08x\n" + "src_width %d\n" + "src_height %d\n" + "clip_top %d(0x%04x)\n" + "clip_left %d(0x%04x)\n" + "clip_width %d\n" + "clip_height %d\n" + "stride %d\n" + "dest_pixel_fmt 0x%08x\n" + "dest_top %d(0x%04x)\n" + "dest_left %d(0x%04x)\n" + "dest_width %d\n" + "dest_height %d\n", + (uint32_t)imxdpu->chan_data[chan_idx].chan, + imxdpu->chan_data[chan_idx].src_pixel_fmt, + imxdpu->chan_data[chan_idx].src_width, + imxdpu->chan_data[chan_idx].src_height, + imxdpu->chan_data[chan_idx].clip_top, + imxdpu->chan_data[chan_idx].clip_top, + imxdpu->chan_data[chan_idx].clip_left, + imxdpu->chan_data[chan_idx].clip_left, + imxdpu->chan_data[chan_idx].clip_width, + imxdpu->chan_data[chan_idx].clip_height, + imxdpu->chan_data[chan_idx].stride, + imxdpu->chan_data[chan_idx].dest_pixel_fmt, + imxdpu->chan_data[chan_idx].dest_top, + imxdpu->chan_data[chan_idx].dest_top, + imxdpu->chan_data[chan_idx].dest_left, + imxdpu->chan_data[chan_idx].dest_left, + imxdpu->chan_data[chan_idx].dest_width, + imxdpu->chan_data[chan_idx].dest_height); + + IMXDPUV1_PRINT( + "use_video_proc %d\n" + "use_eco_fetch %d\n" + "interlaced %d\n" + "phyaddr_0 0x%08x\n" + "rot_mode %d\n" + "in_use %d\n" + "use_global_alpha %d\n" + "use_local_alpha %d\n", + imxdpu->chan_data[chan_idx].use_video_proc, + imxdpu->chan_data[chan_idx].use_eco_fetch, + imxdpu->chan_data[chan_idx].interlaced, + ptr_to_uint32(imxdpu->chan_data[chan_idx].phyaddr_0), + imxdpu->chan_data[chan_idx].rot_mode, + imxdpu->chan_data[chan_idx].in_use, + imxdpu->chan_data[chan_idx].use_global_alpha, + imxdpu->chan_data[chan_idx].use_local_alpha + ); + + imxdpuv1_dump_store_layer(&imxdpu->chan_data[chan_idx].store_layer_prop); + + } else { + IMXDPUV1_PRINT("chan_id 0x%x\n" + "src_pixel_fmt 0x%08x\n" + "src_width %d\n" + "src_height %d\n" + "clip_top %d(0x%04x)\n" + "clip_left %d(0x%04x)\n" + "clip_width %d\n" + "clip_height %d\n" + "stride %d\n" + "dest_pixel_fmt 0x%08x\n" + "dest_top %d(0x%04x)\n" + "dest_left %d(0x%04x)\n" + "dest_width %d\n" + "dest_height %d\n", + (uint32_t)imxdpu->chan_data[chan_idx].chan, + imxdpu->chan_data[chan_idx].src_pixel_fmt, + imxdpu->chan_data[chan_idx].src_width, + imxdpu->chan_data[chan_idx].src_height, + imxdpu->chan_data[chan_idx].clip_top, + imxdpu->chan_data[chan_idx].clip_top, + imxdpu->chan_data[chan_idx].clip_left, + imxdpu->chan_data[chan_idx].clip_left, + imxdpu->chan_data[chan_idx].clip_width, + imxdpu->chan_data[chan_idx].clip_height, + imxdpu->chan_data[chan_idx].stride, + imxdpu->chan_data[chan_idx].dest_pixel_fmt, + imxdpu->chan_data[chan_idx].dest_top, + imxdpu->chan_data[chan_idx].dest_top, + imxdpu->chan_data[chan_idx].dest_left, + imxdpu->chan_data[chan_idx].dest_left, + imxdpu->chan_data[chan_idx].dest_width, + imxdpu->chan_data[chan_idx].dest_height); + + + IMXDPUV1_PRINT( + "use_video_proc %d\n" + "use_eco_fetch %d\n" + "interlaced %d\n" + "phyaddr_0 0x%08x\n" + "u_offset 0x%08x\n" + "v_offset 0x%08x\n" + "rot_mode %d\n" + "in_use %d\n" + "use_global_alpha %d\n" + "use_local_alpha %d\n", + imxdpu->chan_data[chan_idx].use_video_proc, + imxdpu->chan_data[chan_idx].use_eco_fetch, + imxdpu->chan_data[chan_idx].interlaced, + ptr_to_uint32(imxdpu->chan_data[chan_idx].phyaddr_0), + imxdpu->chan_data[chan_idx].u_offset, + imxdpu->chan_data[chan_idx].v_offset, + imxdpu->chan_data[chan_idx].rot_mode, + imxdpu->chan_data[chan_idx].in_use, + imxdpu->chan_data[chan_idx].use_global_alpha, + imxdpu->chan_data[chan_idx].use_local_alpha + ); + + imxdpuv1_dump_fetch_layer(&imxdpu->chan_data[chan_idx].fetch_layer_prop); + } + return ret; +} + +/*! + * Shows the interrupt status registers + * + * @param id of the diplay unit + * + */ +void imxdpuv1_dump_int_stat(int8_t imxdpuv1_id) +{ + int i; + struct imxdpuv1_soc *imxdpu; + uint32_t reg; + + IMXDPUV1_TRACE("%s()\n", __func__); + + if (!((imxdpuv1_id >= 0) && (imxdpuv1_id < IMXDPUV1_MAX_NUM))) { + return; + } + + imxdpu = &imxdpuv1_array[imxdpuv1_id]; + + for (i = 0; i < 3; i++) { + reg = imxdpuv1_read_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTMASK0 + + (i * 4)); + IMXDPUV1_PRINT("USERINTERRUPTMASK%d: 0x%08x\n", i, reg); + } + for (i = 0; i < 3; i++) { + reg = imxdpuv1_read_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTENABLE0 + + (i * 4)); + IMXDPUV1_PRINT("USERINTERRUPTENABLE%d: 0x%08x\n", i, reg); + } + for (i = 0; i < 3; i++) { + reg = imxdpuv1_read_irq(imxdpu, + IMXDPUV1_COMCTRL_USERINTERRUPTSTATUS0 + + (i * 4)); + IMXDPUV1_PRINT("USERINTERRUPTSTATUS%d: 0x%08x\n", i, reg); + } + for (i = 0; i < 3; i++) { + reg = imxdpuv1_read_irq(imxdpu, + IMXDPUV1_COMCTRL_INTERRUPTENABLE0 + (i * 4)); + IMXDPUV1_PRINT("INTERRUPTENABLE%i: 0x%08x\n", i, reg); + } + for (i = 0; i < 3; i++) { + reg = imxdpuv1_read_irq(imxdpu, + IMXDPUV1_COMCTRL_INTERRUPTSTATUS0 + (i * 4)); + IMXDPUV1_PRINT("INTERRUPTSTATUS%i: 0x%08x\n", i, reg); + } +} diff --git a/drivers/video/nxp/imx/imxdpuv1_be.h b/drivers/video/nxp/imx/imxdpuv1_be.h new file mode 100644 index 00000000000..a004bf82447 --- /dev/null +++ b/drivers/video/nxp/imx/imxdpuv1_be.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef IMXDPUV1_BE_H +#define IMXDPUV1_BE_H + +struct fetch_unit { + uint32_t in_pipeline; + uint32_t control; + uint32_t burst_buf; + uint32_t buf_address; + uint32_t buf_attributes; + uint32_t buf_dimension; + uint32_t color_bits; + uint32_t color_shift; + uint32_t layer_offset; + uint32_t clip_offset; + uint32_t clip_dimension; + uint32_t const_color; + uint32_t layer_property; + uint32_t frame_dimension; + uint32_t frame_resample; +}; + +struct store_unit { + uint32_t in_pipeline; + uint32_t control; + uint32_t burst_buf; + uint32_t buf_address; + uint32_t buf_attributes; + uint32_t buf_dimension; + uint32_t frame_offset; + uint32_t color_bits; + uint32_t color_shift; +}; +struct rop_unit { + uint32_t in_pipeline; + uint32_t control; +}; +struct matrix_unit { + uint32_t in_pipeline; + uint32_t control; +}; +struct hscaler_unit { + uint32_t in_pipeline; + uint32_t control; + uint32_t setup1; + uint32_t setup2; +}; +struct vscaler_unit { + uint32_t in_pipeline; + uint32_t control; + uint32_t setup1; + uint32_t setup2; + uint32_t setup3; + uint32_t setup4; + uint32_t setup5; +}; +struct blitblend_unit { + uint32_t in_pipeline; + uint32_t control; + uint32_t const_color; + uint32_t red_func; + uint32_t green_func; + uint32_t blue_func; + uint32_t alpha_func; + uint32_t blend_mode1; + uint32_t blend_mode2; +}; +struct engcfg_unit { + uint32_t fetchpersp9_dynamic; + uint32_t fetchdecode9_dynamic; + uint32_t rop9_dynamic; + uint32_t matrix9_dynamic; + uint32_t hscaler9_dynamic; + uint32_t vscaler9_dynamic; + uint32_t blitblend9_dynamic; + uint32_t store9_dynamic; +}; + +struct be_blit_cfg { + struct fetch_unit fetch_decode; + struct fetch_unit fetch_persp; + struct fetch_unit fetch_eco; + struct store_unit store; + struct rop_unit rop; + struct matrix_unit matrix; + struct hscaler_unit hscaler; + struct vscaler_unit vscaler; + struct blitblend_unit blitblend; + struct engcfg_unit engcfg; +}; + +/* PRIVATE DATA */ +struct imxdpuv1_info { + /*reg */ + void __iomem *base; +}; + +#define IMXDPUV1_IOC_MAGIC 'i' +#define IMXDPUV1_IOC_BLIT _IOW(IMXDPUV1_IOC_MAGIC, 1, struct be_blit_cfg) +#define IMXDPUV1_IOC_WAIT _IO(IMXDPUV1_IOC_MAGIC, 2) + +void imxdpuv1_be_irq_handler(int8_t imxdpuv1_id, int8_t irq); +int imxdpuv1_be_init(int8_t imxdpuv1_id, void __iomem *imxdpuv1_base); +int imxdpuv1_be_blit(struct imxdpuv1_info *imxdpu, struct be_blit_cfg *cfg); +int imxdpuv1_be_wait_shadow_load(struct imxdpuv1_info *imxdpu); +int imxdpuv1_be_wait_complete(struct imxdpuv1_info *imxdpu); +int imxdpuv1_be_load(struct imxdpuv1_info *imxdpu, void __user *p); +int imxdpuv1_be_wait(struct imxdpuv1_info *imxdpu); + +#endif diff --git a/drivers/video/nxp/imx/imxdpuv1_private.h b/drivers/video/nxp/imx/imxdpuv1_private.h new file mode 100644 index 00000000000..b874c38b47e --- /dev/null +++ b/drivers/video/nxp/imx/imxdpuv1_private.h @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2005-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Instance: imxdpuv1_private.h */ +#ifndef IMXDPUV1_PRIVATE_H +#define IMXDPUV1_PRIVATE_H + +#include <asm/io.h> +#include <asm/string.h> + +#include <linux/types.h> +#include "imxdpuv1.h" + +typedef enum { + IMXDPUV1_BURST_UNKNOWN = 0, + IMXDPUV1_BURST_LEFT_RIGHT_DOWN, + IMXDPUV1_BURST_HORIZONTAL, + IMXDPUV1_BURST_VERTICAL, + IMXDPUV1_BURST_FREE, +} imxdpuv1_burst_t; + +#define INTSTAT0_BIT(__bit__) (1U<<(__bit__)) +#define INTSTAT1_BIT(__bit__) (1U<<((__bit__)-32)) +#define INTSTAT2_BIT(__bit__) (1U<<((__bit__)-64)) + +struct imxdpuv1_irq_node { + int(*handler) (int, void *); + const char *name; + void *data; + uint32_t flags; +}; + +/* Generic definitions that are common to many registers */ +#define IMXDPUV1_COLOR_BITSALPHA0_MASK 0xFU +#define IMXDPUV1_COLOR_BITSALPHA0_SHIFT 0U +#define IMXDPUV1_COLOR_BITSBLUE0_MASK 0xF00U +#define IMXDPUV1_COLOR_BITSBLUE0_SHIFT 8U +#define IMXDPUV1_COLOR_BITSGREEN0_MASK 0xF0000U +#define IMXDPUV1_COLOR_BITSGREEN0_SHIFT 16U +#define IMXDPUV1_COLOR_BITSRED0_MASK 0xF000000U +#define IMXDPUV1_COLOR_BITSRED0_SHIFT 24U + +#define IMXDPUV1_COLOR_SHIFTALPHA0_MASK 0x1FU +#define IMXDPUV1_COLOR_SHIFTALPHA0_SHIFT 0U +#define IMXDPUV1_COLOR_SHIFTBLUE0_MASK 0x1F00U +#define IMXDPUV1_COLOR_SHIFTBLUE0_SHIFT 8U +#define IMXDPUV1_COLOR_SHIFTGREEN0_MASK 0x1F0000U +#define IMXDPUV1_COLOR_SHIFTGREEN0_SHIFT 16U +#define IMXDPUV1_COLOR_SHIFTRED0_MASK 0x1F000000U +#define IMXDPUV1_COLOR_SHIFTRED0_SHIFT 24U + +#define IMXDPUV1_COLOR_CONSTALPHA_MASK 0xFFU +#define IMXDPUV1_COLOR_CONSTALPHA_SHIFT 0U +#define IMXDPUV1_COLOR_CONSTBLUE_MASK 0xFF00U +#define IMXDPUV1_COLOR_CONSTBLUE_SHIFT 8U +#define IMXDPUV1_COLOR_CONSTGREEN_MASK 0xFF0000U +#define IMXDPUV1_COLOR_CONSTGREEN_SHIFT 16U +#define IMXDPUV1_COLOR_CONSTRED_MASK 0xFF000000U +#define IMXDPUV1_COLOR_CONSTRED_SHIFT 24U + +/* these are common for fetch but not store */ +#define IMXDPUV1_BUFF_ATTR_STRIDE_MASK 0xFFFFU +#define IMXDPUV1_BUFF_ATTR_STRIDE_SHIFT 0U +#define IMXDPUV1_BUFF_ATTR_BITSPERPIXEL_MASK 0x3F0000U +#define IMXDPUV1_BUFF_ATTR_BITSPERPIXEL_SHIFT 16U + +#define IMXDPUV1_BUFF_DIMEN_LINECOUNT_SHIFT 16U +#define IMXDPUV1_BUFF_DIMEN_LINEWIDTH_MASK 0x3FFFU +#define IMXDPUV1_BUFF_DIMEN_LINEWIDTH_SHIFT 0U +#define IMXDPUV1_BUFF_DIMEN_LINECOUNT_MASK 0x3FFF0000U + +#define IMXDPUV1_LAYER_XOFFSET_MASK 0x7FFFU +#define IMXDPUV1_LAYER_XOFFSET_SHIFT 0U +#define IMXDPUV1_LAYER_XSBIT_MASK 0x4000U +#define IMXDPUV1_LAYER_XSBIT_SHIFT 0U + +#define IMXDPUV1_LAYER_YOFFSET_MASK 0x7FFF0000U +#define IMXDPUV1_LAYER_YOFFSET_SHIFT 16U +#define IMXDPUV1_LAYER_YSBIT_MASK 0x4000U +#define IMXDPUV1_LAYER_YSBIT_SHIFT 16U + +#define IMXDPUV1_CLIP_XOFFSET_MASK 0x7FFFU +#define IMXDPUV1_CLIP_XOFFSET_SHIFT 0U +#define IMXDPUV1_CLIP_YOFFSET_MASK 0x7FFF0000U +#define IMXDPUV1_CLIP_YOFFSET_SHIFT 16U + +#define IMXDPUV1_CLIP_WIDTH_MASK 0x3FFFU +#define IMXDPUV1_CLIP_WIDTH_SHIFT 0U +#define IMXDPUV1_CLIP_HEIGHT_MASK 0x3FFF0000U +#define IMXDPUV1_CLIP_HEIGHT_SHIFT 16U + +#define IMXDPUV1_FRAMEWIDTH_MASK 0x3FFFU +#define IMXDPUV1_FRAMEWIDTH_SHIFT 0U +#define IMXDPUV1_FRAMEHEIGHT_MASK 0x3FFF0000U +#define IMXDPUV1_FRAMEHEIGHT_SHIFT 16U +#define IMXDPUV1_EMPTYFRAME_MASK 0x80000000U +#define IMXDPUV1_EMPTYFRAME_SHIFT 31U + +#define IMXDPUV1_PIXENGCFG_SRC_SEL__DISABLE 0U +#define IMXDPUV1_PIXENGCFG_SRC_SEL_MASK 0x3FU +#define IMXDPUV1_PIXENGCFG_SRC_SEL_SHIFT 0U + +#define IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL_MASK 0x3FU +#define IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL_SHIFT 0U +#define IMXDPUV1_PIXENGCFG_LAYERBLEND_PRIM_SEL__DISABLE 0U + +#define IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL_MASK 0x3F00U +#define IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL_SHIFT 8U +#define IMXDPUV1_PIXENGCFG_LAYERBLEND_SEC_SEL__DISABLE 0U + +#define IMXDPUV1_PIXENGCFG_CLKEN_MASK 0x3000000U +#define IMXDPUV1_PIXENGCFG_CLKEN_SHIFT 24U +/* Field Value: _CLKEN__DISABLE, Clock for block is disabled */ +#define IMXDPUV1_PIXENGCFG_CLKEN__DISABLE 0U +/* Field Value: _CLKEN__AUTOMATIC, Clock is enabled if unit is used, + * frequency is defined by the register setting for this pipeline (see + * [endpoint_name]_Static register) */ +#define IMXDPUV1_PIXENGCFG_CLKEN__AUTOMATIC 0x1U +/* Field Value: _CLKEN__FULL, Clock for block is without gating */ +#define IMXDPUV1_PIXENGCFG_CLKEN__FULL 0x3U + + +/* Register: IMXDPUV1_LayerProperty0 Common Bits */ +#define IMXDPUV1_LAYERPROPERTY_OFFSET ((uint32_t)(0x40)) +#define IMXDPUV1_LAYERPROPERTY_RESET_VALUE 0x80000100U +#define IMXDPUV1_LAYERPROPERTY_RESET_MASK 0xFFFFFFFFU +#define IMXDPUV1_LAYERPROPERTY_PALETTEENABLE_MASK 0x1U +#define IMXDPUV1_LAYERPROPERTY_PALETTEENABLE_SHIFT 0U +#define IMXDPUV1_LAYERPROPERTY_TILEMODE_MASK 0x30U +#define IMXDPUV1_LAYERPROPERTY_TILEMODE_SHIFT 4U +/* Field Value: TILEMODE0__TILE_FILL_ZERO, Use zero value */ +#define IMXDPUV1_LAYERPROPERTY_TILEMODE__TILE_FILL_ZERO 0U +/* Field Value: TILEMODE0__TILE_FILL_CONSTANT, Use constant color register + * value */ +#define IMXDPUV1_LAYERPROPERTY_TILEMODE__TILE_FILL_CONSTANT 0x1U +/* Field Value: TILEMODE0__TILE_PAD, Use closest pixel from source buffer. + * Must not be used for DECODE or YUV422 operations or when SourceBufferEnable + * is 0. */ +#define IMXDPUV1_LAYERPROPERTY_TILEMODE__TILE_PAD 0x2U +/* Field Value: TILEMODE0__TILE_PAD_ZERO, Use closest pixel from source buffer + * but zero for alpha component. Must not be used for DECODE or YUV422 + * operations or when SourceBufferEnable is 0. */ +#define IMXDPUV1_LAYERPROPERTY_TILEMODE__TILE_PAD_ZERO 0x3U +#define IMXDPUV1_LAYERPROPERTY_ALPHASRCENABLE_MASK 0x100U +#define IMXDPUV1_LAYERPROPERTY_ALPHASRCENABLE_SHIFT 8U +#define IMXDPUV1_LAYERPROPERTY_ALPHACONSTENABLE_MASK 0x200U +#define IMXDPUV1_LAYERPROPERTY_ALPHACONSTENABLE_SHIFT 9U +#define IMXDPUV1_LAYERPROPERTY_ALPHAMASKENABLE_MASK 0x400U +#define IMXDPUV1_LAYERPROPERTY_ALPHAMASKENABLE_SHIFT 10U +#define IMXDPUV1_LAYERPROPERTY_ALPHATRANSENABLE_MASK 0x800U +#define IMXDPUV1_LAYERPROPERTY_ALPHATRANSENABLE_SHIFT 11U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHASRCENABLE_MASK 0x1000U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHASRCENABLE_SHIFT 12U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHACONSTENABLE_MASK 0x2000U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHACONSTENABLE_SHIFT 13U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHAMASKENABLE_MASK 0x4000U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHAMASKENABLE_SHIFT 14U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHATRANSENABLE_MASK 0x8000U +#define IMXDPUV1_LAYERPROPERTY_RGBALPHATRANSENABLE_SHIFT 15U +#define IMXDPUV1_LAYERPROPERTY_PREMULCONSTRGB_MASK 0x10000U +#define IMXDPUV1_LAYERPROPERTY_PREMULCONSTRGB_SHIFT 16U +#define IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE_MASK 0x60000U +#define IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE_SHIFT 17U +/* Field Value: YUVCONVERSIONMODE0__OFF, No conversion. */ +#define IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE__OFF 0U +/* Field Value: YUVCONVERSIONMODE0__ITU601, Conversion from YCbCr (YUV) to + * RGB according to ITU recommendation BT.601-6 (standard definition TV). + * Input range is 16..235 for Y and 16..240 for U/V. */ +#define IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE__ITU601 0x1U +/* Field Value: YUVCONVERSIONMODE0__ITU601_FR, Conversion from YCbCr (YUV) + * to RGB according to ITU recommendation BT.601-6, but assuming full range + * YUV inputs (0..255). Most typically used for computer graphics (e.g. + * for JPEG encoding). */ +#define IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE__ITU601_FR 0x2U +/* Field Value: YUVCONVERSIONMODE0__ITU709, Conversion from YCbCr (YUV) to + * RGB according to ITU recommendation BT.709-5 part 2 (high definition + * TV). Input range is 16..235 for Y and 16..240 for U/V. */ +#define IMXDPUV1_LAYERPROPERTY_YUVCONVERSIONMODE__ITU709 0x3U +#define IMXDPUV1_LAYERPROPERTY_GAMMAREMOVEENABLE_MASK 0x100000U +#define IMXDPUV1_LAYERPROPERTY_GAMMAREMOVEENABLE_SHIFT 20U +#define IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE_MASK 0x40000000U +#define IMXDPUV1_LAYERPROPERTY_CLIPWINDOWENABLE_SHIFT 30U +#define IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE_MASK 0x80000000U +#define IMXDPUV1_LAYERPROPERTY_SOURCEBUFFERENABLE_SHIFT 31U + +typedef struct { + /* Source buffer base address of layer 0. */ + uint32_t baseaddress0; + /* Source buffer attributes for layer 0. */ + uint32_t sourcebufferattributes0; + /* Source buffer dimension of layer 0. */ + uint32_t sourcebufferdimension0; + /* Size of color components for RGB, YUV and index formats (layer 0). */ + uint32_t colorcomponentbits0; + /* Bit position of color components for RGB, YUV and index + formats (layer 0). */ + uint32_t colorcomponentshift0; + /* Position of layer 0 within the destination frame. */ + uint32_t layeroffset0; + /* Clip window position for layer 0. */ + uint32_t clipwindowoffset0; + /* Clip window size for layer 0. */ + uint32_t clipwindowdimensions0; + /* Constant color for layer 0. */ + uint32_t constantcolor0; + /* Common properties of layer 0. */ + uint32_t layerproperty0; +} fetch_layer_setup_t; + +typedef struct { + /* Destination buffer base address of layer 0. */ + uint32_t baseaddress0; + /* Destination buffer attributes for layer 0. */ + uint32_t destbufferattributes0; + /* Source buffer dimension of layer 0. */ + uint32_t destbufferdimension0; + /* Frame offset of layer 0. */ + uint32_t frameoffset0; + /* Size of color components for RGB, YUV and index formats (layer 0). */ + uint32_t colorcomponentbits0; + /* Bit position of color components for RGB, YUV and index + formats (layer 0). */ + uint32_t colorcomponentshift0; +} store_layer_setup_t; + +typedef enum { + IMXDPUV1_SHDLD_IDX_DISP0 = (0), + IMXDPUV1_SHDLD_IDX_DISP1 = (1), + IMXDPUV1_SHDLD_IDX_CONST0 = (2), /* IMXDPUV1_ID_CONSTFRAME0 */ + IMXDPUV1_SHDLD_IDX_CONST1 = (3), /* IMXDPUV1_ID_CONSTFRAME1 */ + IMXDPUV1_SHDLD_IDX_CHAN_00 = (4), /* IMXDPUV1_ID_FETCHDECODE2 */ + IMXDPUV1_SHDLD_IDX_CHAN_01 = (5), /* IMXDPUV1_ID_FETCHDECODE0 */ + IMXDPUV1_SHDLD_IDX_CHAN_02 = (6), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_03 = (7), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_04 = (8), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_05 = (9), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_06 = (10), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_07 = (11), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_08 = (12), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_09 = (13), /* IMXDPUV1_ID_FETCHLAYER0 */ + IMXDPUV1_SHDLD_IDX_CHAN_10 = (14), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_11 = (15), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_12 = (16), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_13 = (17), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_14 = (18), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_15 = (19), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_16 = (20), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_17 = (21), /* IMXDPUV1_ID_FETCHWARP2 */ + IMXDPUV1_SHDLD_IDX_CHAN_18 = (22), /* IMXDPUV1_ID_FETCHDECODE3 */ + IMXDPUV1_SHDLD_IDX_CHAN_19 = (23), /* IMXDPUV1_ID_FETCHDECODE1 */ + IMXDPUV1_SHDLD_IDX_CHAN_20 = (24), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_21 = (25), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_22 = (26), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_23 = (27), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_24 = (28), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_25 = (29), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_26 = (30), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_27 = (31), /* IMXDPUV1_ID_FETCHLAYER1*/ + IMXDPUV1_SHDLD_IDX_CHAN_28 = (32), /* IMXDPUV1_ID_FETCHECO0*/ + IMXDPUV1_SHDLD_IDX_CHAN_29 = (33), /* IMXDPUV1_ID_FETCHECO1*/ + IMXDPUV1_SHDLD_IDX_CHAN_30 = (34), /* IMXDPUV1_ID_FETCHECO2*/ + IMXDPUV1_SHDLD_IDX_MAX = (35), +} imxdpuv1_shadow_load_index_t; + +typedef struct { + bool prim_sync_state; + bool sec_sync_state; + uint32_t prim_sync_count; + uint32_t sec_sync_count; + uint32_t skew_error_count; + uint32_t prim_fifo_empty_count; + uint32_t sec_fifo_empty_count; + uint32_t frame_count; +} frame_gen_stats_t; + +/*! + * Definition of IMXDPU channel structure + */ +typedef struct { + int8_t disp_id; /* Iris instance id of "owner" */ + + imxdpuv1_chan_t chan; + uint32_t src_pixel_fmt; + int16_t src_top; + int16_t src_left; + uint16_t src_width; + uint16_t src_height; + int16_t clip_top; + int16_t clip_left; + uint16_t clip_width; + uint16_t clip_height; + uint16_t stride; + uint32_t dest_pixel_fmt; + int16_t dest_top; + int16_t dest_left; + uint16_t dest_width; + uint16_t dest_height; + uint16_t const_color; + + uint32_t h_scale_factor; /* downscaling out/in */ + uint32_t h_phase; + uint32_t v_scale_factor; /* downscaling out/in */ + uint32_t v_phase[2][2]; + + bool use_video_proc; + bool interlaced; + bool use_eco_fetch; + bool use_global_alpha; + bool use_local_alpha; + + /* note: dma_addr_t changes for 64-bit arch */ + dma_addr_t phyaddr_0; + + uint32_t u_offset; + uint32_t v_offset; + + uint8_t blend_layer; + uint8_t destination_stream; + uint8_t source_id; + + imxdpuv1_rotate_mode_t rot_mode; + + /* todo add features sub-windows, upscaling, warping */ + fetch_layer_setup_t fetch_layer_prop; + store_layer_setup_t store_layer_prop; + + bool in_use; + + /* todo: add channel features */ +} chan_private_t; + +typedef union { + struct { + uint8_t request; + uint8_t processing; + uint8_t complete; + uint8_t trys; + } state; + uint32_t word; +} imxdpuv1_shadow_state_t; + +/* PRIVATE DATA */ +struct imxdpuv1_soc { + int8_t devtype; + int8_t online; + uint32_t enabled_int[3]; + struct imxdpuv1_irq_node irq_list[IMXDPUV1_INTERRUPT_MAX]; + + struct device *dev; + struct imxdpuv1_videomode video_mode[IMXDPUV1_NUM_DI]; + struct imxdpuv1_videomode capture_mode[IMXDPUV1_NUM_CI]; + frame_gen_stats_t fgen_stats[IMXDPUV1_NUM_DI]; + uint32_t irq_count; + + + /* + * Bypass reset to avoid display channel being + * stopped by probe since it may starts to work + * in bootloader. + */ + int8_t bypass_reset; + + /* todo: need to decide where the locking is implemented */ + + /*clk*/ + + /*irq*/ + + /*reg*/ + void __iomem *base; + + /*use count*/ + imxdpuv1_layer_t blend_layer[IMXDPUV1_LAYER_MAX]; + chan_private_t chan_data[IMXDPUV1_CHAN_IDX_MAX]; + + uint8_t shadow_load_pending[IMXDPUV1_NUM_DI][IMXDPUV1_SHDLD_IDX_MAX]; + imxdpuv1_shadow_state_t shadow_load_state[IMXDPUV1_NUM_DI][IMXDPUV1_SHDLD_IDX_MAX]; +}; + + + +/* PRIVATE FUNCTIONS */ +#ifdef ENABLE_IMXDPUV1_TRACE_REG +uint32_t _imxdpuv1_read(struct imxdpuv1_soc *dpu, u32 offset, char *file, int line); +#define imxdpuv1_read(_inst_, _offset_) _imxdpuv1_read(_inst_, _offset_, __FILE__, __LINE__) +#else +static inline uint32_t imxdpuv1_read(struct imxdpuv1_soc *dpu, uint32_t offset) +{ + return __raw_readl(dpu->base + offset); +} +#endif + +#ifdef ENABLE_IMXDPUV1_TRACE_IRQ_READ +uint32_t _imxdpuv1_read_irq(struct imxdpuv1_soc *dpu, u32 offset, char *file, int line); +#define imxdpuv1_read_irq(_inst_, _offset_) _imxdpuv1_read_irq(_inst_, _offset_, __FILE__, __LINE__) +#else +static inline uint32_t imxdpuv1_read_irq(struct imxdpuv1_soc *dpu, uint32_t offset) +{ + return __raw_readl(dpu->base + offset); +} +#endif + +#ifdef ENABLE_IMXDPUV1_TRACE_REG +void _imxdpuv1_write(struct imxdpuv1_soc *dpu, uint32_t value, uint32_t offset, char *file, int line); +#define imxdpuv1_write(_inst_, _value_, _offset_) _imxdpuv1_write(_inst_, _value_, _offset_, __FILE__, __LINE__) +#else +static inline void imxdpuv1_write(struct imxdpuv1_soc *dpu, uint32_t offset, uint32_t value) +{ + __raw_writel(value, dpu->base + offset); +} +#endif + +#ifdef ENABLE_IMXDPUV1_TRACE_IRQ_WRITE +void _imxdpuv1_write_irq(struct imxdpuv1_soc *dpu, uint32_t value, uint32_t offset, char *file, int line); +#define imxdpuv1_write_irq(_inst_, _value_, _offset_) _imxdpuv1_write_irq(_inst_, _value_, _offset_, __FILE__, __LINE__) +#else +static inline void imxdpuv1_write_irq(struct imxdpuv1_soc *dpu, uint32_t offset, uint32_t value) +{ + __raw_writel(value, dpu->base + offset); +} +#endif + +void _imxdpuv1_write_block(struct imxdpuv1_soc *imxdpu, uint32_t offset, void *values, uint32_t cnt, char *file, int line); +#define imxdpuv1_write_block(_inst_, _values_, _offset_, _cnt_) _imxdpuv1_write_block(_inst_, _values_, _offset_, _cnt_, __FILE__, __LINE__) + +/* mapping of RGB, Tcon, or static values to output */ +#define IMXDPUV1_TCON_MAPBIT__RGB(_x_) ((_x_)) +#define IMXDPUV1_TCON_MAPBIT__Tsig(_x_) ((_x_) + 30) +#define IMXDPUV1_TCON_MAPBIT__HIGH 42U +#define IMXDPUV1_TCON_MAPBIT__LOW 43U + +/* these match the bit definitions for the shadlow load + request registers + */ +typedef enum { + IMXDPUV1_SHLDREQID_FETCHDECODE9 = 0, + IMXDPUV1_SHLDREQID_FETCHPERSP9, + IMXDPUV1_SHLDREQID_FETCHECO9, + IMXDPUV1_SHLDREQID_CONSTFRAME0, + IMXDPUV1_SHLDREQID_CONSTFRAME4, + IMXDPUV1_SHLDREQID_CONSTFRAME1, + IMXDPUV1_SHLDREQID_CONSTFRAME5, +#ifdef IMXDPUV1_VERSION_0 + IMXDPUV1_SHLDREQID_EXTSRC4, + IMXDPUV1_SHLDREQID_EXTSRC5, + IMXDPUV1_SHLDREQID_FETCHDECODE2, + IMXDPUV1_SHLDREQID_FETCHDECODE3, +#endif + IMXDPUV1_SHLDREQID_FETCHWARP2, + IMXDPUV1_SHLDREQID_FETCHECO2, + IMXDPUV1_SHLDREQID_FETCHDECODE0, + IMXDPUV1_SHLDREQID_FETCHECO0, + IMXDPUV1_SHLDREQID_FETCHDECODE1, + IMXDPUV1_SHLDREQID_FETCHECO1, + IMXDPUV1_SHLDREQID_FETCHLAYER0, +#ifdef IMXDPUV1_VERSION_0 + IMXDPUV1_SHLDREQID_FETCHLAYER1, + IMXDPUV1_SHLDREQID_EXTSRC0, + IMXDPUV1_SHLDREQID_EXTSRC1 +#endif +} imxdpuv1_shadow_load_req_t; + +#define IMXDPUV1_PIXENGCFG_DIVIDER_RESET 0x80 + +#endif /* IMXDPUV1_PRIVATE_H */ + diff --git a/drivers/video/nxp/imx/ipu.h b/drivers/video/nxp/imx/ipu.h new file mode 100644 index 00000000000..1e02c7ab6d5 --- /dev/null +++ b/drivers/video/nxp/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 <linux/types.h> +#include <ipu_pixfmt.h> + +#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/nxp/imx/ipu_common.c b/drivers/video/nxp/imx/ipu_common.c new file mode 100644 index 00000000000..54d1efc8f5f --- /dev/null +++ b/drivers/video/nxp/imx/ipu_common.c @@ -0,0 +1,1267 @@ +// 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 <common.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/err.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/sys_proto.h> +#include <div64.h> +#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/nxp/imx/ipu_disp.c b/drivers/video/nxp/imx/ipu_disp.c new file mode 100644 index 00000000000..e1ab05535e3 --- /dev/null +++ b/drivers/video/nxp/imx/ipu_disp.c @@ -0,0 +1,1286 @@ +// 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 <common.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#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); + if (rounded_pixel_clk == 0) { + debug("IPU_DISP: get round rate error\n"); + return -EINVAL; + } + + 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 (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); + + 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; + + 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); + + return 0; +} diff --git a/drivers/video/nxp/imx/ipu_regs.h b/drivers/video/nxp/imx/ipu_regs.h new file mode 100644 index 00000000000..deb44002d75 --- /dev/null +++ b/drivers/video/nxp/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/nxp/imx/lcdifv3-regs.h b/drivers/video/nxp/imx/lcdifv3-regs.h new file mode 100644 index 00000000000..b902e03644d --- /dev/null +++ b/drivers/video/nxp/imx/lcdifv3-regs.h @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + */ + +#ifndef __LCDIFV3_REGS_H +#define __LCDIFV3_REGS_H + +/* regs offset */ +#define LCDIFV3_CTRL 0x00 +#define LCDIFV3_CTRL_SET 0x04 +#define LCDIFV3_CTRL_CLR 0x08 +#define LCDIFV3_CTRL_TOG 0x0c +#define LCDIFV3_DISP_PARA 0x10 +#define LCDIFV3_DISP_SIZE 0x14 +#define LCDIFV3_HSYN_PARA 0x18 +#define LCDIFV3_VSYN_PARA 0x1c +#define LCDIFV3_VSYN_HSYN_WIDTH 0x20 +#define LCDIFV3_INT_STATUS_D0 0x24 +#define LCDIFV3_INT_ENABLE_D0 0x28 +#define LCDIFV3_INT_STATUS_D1 0x30 +#define LCDIFV3_INT_ENABLE_D1 0x34 + +#define LCDIFV3_CTRLDESCL0_1 0x200 +#define LCDIFV3_CTRLDESCL0_3 0x208 +#define LCDIFV3_CTRLDESCL_LOW0_4 0x20c +#define LCDIFV3_CTRLDESCL_HIGH0_4 0x210 +#define LCDIFV3_CTRLDESCL0_5 0x214 +#define LCDIFV3_CSC0_CTRL 0x21c +#define LCDIFV3_CSC0_COEF0 0x220 +#define LCDIFV3_CSC0_COEF1 0x224 +#define LCDIFV3_CSC0_COEF2 0x228 +#define LCDIFV3_CSC0_COEF3 0x22c +#define LCDIFV3_CSC0_COEF4 0x230 +#define LCDIFV3_CSC0_COEF5 0x234 +#define LCDIFV3_PANIC0_THRES 0x238 + +/* reg bit manipulation */ +#define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s)) +#define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s)) +#define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s)) + +/* regs bit fields */ +#define CTRL_SW_RESET BIT(31) +#define CTRL_FETCH_START_OPTION(x) REG_PUT((x), 9, 8) + #define FPV 0 + #define PWV 1 + #define BPV 2 + #define RESV 3 +#define CTRL_NEG BIT(4) +#define CTRL_INV_PXCK BIT(3) +#define CTRL_INV_DE BIT(2) +#define CTRL_INV_VS BIT(1) +#define CTRL_INV_HS BIT(0) + +#define DISP_PARA_DISP_ON BIT(31) +#define DISP_PARA_SWAP_EN BIT(30) +#define DISP_PARA_LINE_PATTERN(x) REG_PUT((x), 29, 26) + /* line pattern formats (output) */ + #define LP_RGB888_OR_YUV444 0x0 + #define LP_RBG888 0x1 + #define LP_GBR888 0x2 + #define LP_GRB888_OR_UYV444 0x3 + #define LP_BRG888 0x4 + #define LP_BGR888 0x5 + #define LP_RGB555 0x6 + #define LP_RGB565 0x7 + #define LP_YUYV_16_0 0x8 + #define LP_UYVY_16_0 0x9 + #define LP_YVYU_16_0 0xa + #define LP_VYUY_16_0 0xb + #define LP_YUYV_23_8 0xc + #define LP_UYVY_23_8 0xd + #define LP_YVYU_23_8 0xe + #define LP_VYUY_23_8 0xf + +#define DISP_PARA_DISP_MODE(x) REG_PUT((x), 25, 24) +#define DISP_PARA_BGND_R(x) REG_PUT((x), 23, 16) +#define DISP_PARA_BGND_G(x) REG_PUT((x), 15, 8) +#define DISP_PARA_BGND_B(x) REG_PUT((x), 7, 0) + +#define DISP_SIZE_DELTA_Y(x) REG_PUT((x), 31, 16) +#define DISP_SIZE_DELTA_X(x) REG_PUT((x), 15, 0) + +#define HSYN_PARA_BP_H(x) REG_PUT((x), 31, 16) +#define HSYN_PARA_FP_H(x) REG_PUT((x), 15, 0) + +#define VSYN_PARA_BP_V(x) REG_PUT((x), 31, 16) +#define VSYN_PARA_FP_V(x) REG_PUT((x), 15, 0) + +#define VSYN_HSYN_WIDTH_PW_V(x) REG_PUT((x), 31, 16) +#define VSYN_HSYN_WIDTH_PW_H(x) REG_PUT((x), 15, 0) + +#define INT_STATUS_D0_FIFO_EMPTY BIT(24) +#define INT_STATUS_D0_DMA_DONE BIT(16) +#define INT_STATUS_D0_DMA_ERR BIT(8) +#define INT_STATUS_D0_VS_BLANK BIT(2) +#define INT_STATUS_D0_UNDERRUN BIT(1) +#define INT_STATUS_D0_VSYNC BIT(0) + +#define INT_ENABLE_D0_FIFO_EMPTY_EN BIT(24) +#define INT_ENABLE_D0_DMA_DONE_EN BIT(16) +#define INT_ENABLE_D0_DMA_ERR_EN BIT(8) +#define INT_ENABLE_D0_VS_BLANK_EN BIT(2) +#define INT_ENABLE_D0_UNDERRUN_EN BIT(1) +#define INT_ENABLE_D0_VSYNC_EN BIT(0) + +#define INT_STATUS_D1_PLANE_PANIC BIT(0) +#define INT_ENABLE_D1_PLANE_PANIC_EN BIT(0) + +#define CTRLDESCL0_1_HEIGHT(x) REG_PUT((x), 31, 16) +#define CTRLDESCL0_1_WIDTH(x) REG_PUT((x), 15, 0) +#define CTRLDESCL0_3_STATE_CLEAR_VSYNC BIT(23) +#define CTRLDESCL0_3_P_SIZE(x) REG_PUT((x), 22, 20) +#define CTRLDESCL0_3_T_SIZE(x) REG_PUT((x), 17, 16) +#define CTRLDESCL0_3_PITCH(x) REG_PUT((x), 15, 0) +//#define CTRLDESCL_LOW0_4_ADDR_LOW(x) REG_PUT((x), 31, 0) +#define CTRLDESCL_HIGH0_4_ADDR_HIGH(x) REG_PUT((x), 3, 0) +#define CTRLDESCL0_5_EN BIT(31) /* enable layer for DMA */ +#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30) +#define CTRLDESCL0_5_BPP(x) REG_PUT((x), 27, 24) + /* layer encoding formats (input) */ + #define BPP16_RGB565 0x4 + #define BPP16_ARGB1555 0x5 + #define BPP16_ARGB4444 0x6 + #define BPP16_YCbCr422 0x7 + #define BPP24_RGB888 0x8 + #define BPP32_ARGB8888 0x9 + #define BPP32_ABGR8888 0xa +#define CTRLDESCL0_5_YUV_FORMAT(x) REG_PUT((x), 15, 14) + +#define CSC0_CTRL_CSC_MODE(x) REG_PUT((x), 2, 1) +#define CSC0_CTRL_BYPASS BIT(0) +#define CSC0_COEF0_A2(x) REG_PUT((x), 26, 16) +#define CSC0_COEF0_A1(x) REG_PUT((x), 10, 0) +#define CSC0_COEF1_B1(x) REG_PUT((x), 26, 16) +#define CSC0_COEF1_A3(x) REG_PUT((x), 10, 0) +#define CSC0_COEF2_B3(x) REG_PUT((x), 26, 16) +#define CSC0_COEF2_B2(x) REG_PUT((x), 10, 0) +#define CSC0_COEF3_C2(x) REG_PUT((x), 26, 16) +#define CSC0_COEF3_C1(x) REG_PUT((x), 10, 0) +#define CSC0_COEF4_D1(x) REG_PUT((x), 24, 16) +#define CSC0_COEF4_C3(x) REG_PUT((x), 10, 0) +#define CSC0_COEF5_D3(x) REG_PUT((x), 24, 16) +#define CSC0_COEF5_D2(x) REG_PUT((x), 8, 0) + +#define PANIC0_THRES_PANIC_THRES_LOW(x) REG_PUT((x), 24, 16) +#define PANIC0_THRES_PANIC_THRES_HIGH(x) REG_PUT((x), 8, 0) + +#endif /* __LCDIFV3_REGS_H */ diff --git a/drivers/video/nxp/imx/mipi_dsi_northwest.c b/drivers/video/nxp/imx/mipi_dsi_northwest.c new file mode 100644 index 00000000000..399fd8f9c89 --- /dev/null +++ b/drivers/video/nxp/imx/mipi_dsi_northwest.c @@ -0,0 +1,896 @@ +/* + * Copyright 2016-2019 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#include <common.h> +#include <malloc.h> +#include <dm.h> + +#include <asm/arch/clock.h> +#include <asm/arch/sys_proto.h> +#include <linux/err.h> +#include <linux/bug.h> +#include <asm/io.h> +#include <linux/string.h> + +#include "mipi_dsi_northwest_regs.h" +#include <video_bridge.h> +#include <panel.h> +#include <dsi_host.h> +#include <div64.h> +#include <asm/arch/imx-regs.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/arch/clock.h> + +#define MIPI_LCD_SLEEP_MODE_DELAY (120) +#define MIPI_FIFO_TIMEOUT 250000 /* 250ms */ +#define PS2KHZ(ps) (1000000000UL / (ps)) + +#define DIV_ROUND_CLOSEST_ULL(x, divisor)( \ +{ \ + typeof(divisor) __d = divisor; \ + unsigned long long _tmp = (x) + (__d) / 2; \ + do_div(_tmp, __d); \ + _tmp; \ +} \ +) + +enum mipi_dsi_mode { + DSI_COMMAND_MODE, + DSI_VIDEO_MODE +}; + +#define DSI_LP_MODE 0 +#define DSI_HS_MODE 1 + +enum mipi_dsi_payload { + DSI_PAYLOAD_CMD, + DSI_PAYLOAD_VIDEO, +}; + +/* + * mipi-dsi northwest driver information structure, holds useful data for the driver. + */ +struct mipi_dsi_northwest_info { + void __iomem *mmio_base; + struct mipi_dsi_device *device; + struct mipi_dsi_host dsi_host; + struct display_timing timings; + struct regmap *sim; + + uint32_t max_data_lanes; + uint32_t max_data_rate; + uint32_t pll_ref; +}; + +struct pll_divider { + unsigned int cm; /* multiplier */ + unsigned int cn; /* predivider */ + unsigned int co; /* outdivider */ +}; + +/** + * 'CM' value to 'CM' reigister config value map + * 'CM' = [16, 255]; + */ +static unsigned int cm_map_table[240] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 16 ~ 23 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 24 ~ 31 */ + + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 32 ~ 39 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 40 ~ 47 */ + + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 48 ~ 55 */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 56 ~ 63 */ + + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 64 ~ 71 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 72 ~ 79 */ + + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 80 ~ 87 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 88 ~ 95 */ + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 96 ~ 103 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 104 ~ 111 */ + + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 112 ~ 119 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 120 ~ 127 */ + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 128 ~ 135 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 136 ~ 143 */ + + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 144 ~ 151 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 152 ~ 159 */ + + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 160 ~ 167 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 168 ~ 175 */ + + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 176 ~ 183 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 184 ~ 191 */ + + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 192 ~ 199 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 200 ~ 207 */ + + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 208 ~ 215 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 216 ~ 223 */ + + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 224 ~ 231 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 232 ~ 239 */ + + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 240 ~ 247 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f /* 248 ~ 255 */ +}; + +/** + * map 'CN' value to 'CN' reigister config value + * 'CN' = [1, 32]; + */ +static unsigned int cn_map_table[32] = { + 0x1f, 0x00, 0x10, 0x18, 0x1c, 0x0e, 0x07, 0x13, /* 1 ~ 8 */ + 0x09, 0x04, 0x02, 0x11, 0x08, 0x14, 0x0a, 0x15, /* 9 ~ 16 */ + 0x1a, 0x1d, 0x1e, 0x0f, 0x17, 0x1b, 0x0d, 0x16, /* 17 ~ 24 */ + 0x0b, 0x05, 0x12, 0x19, 0x0c, 0x06, 0x03, 0x01 /* 25 ~ 32 */ +}; + +/** + * map 'CO' value to 'CO' reigister config value + * 'CO' = { 1, 2, 4, 8 }; + */ +static unsigned int co_map_table[4] = { + 0x0, 0x1, 0x2, 0x3 +}; + +unsigned long gcd(unsigned long a, unsigned long b) +{ + unsigned long r = a | b; + + if (!a || !b) + return r; + + /* Isolate lsbit of r */ + r &= -r; + + while (!(b & r)) + b >>= 1; + if (b == r) + return r; + + for (;;) { + while (!(a & r)) + a >>= 1; + if (a == r) + return r; + if (a == b) + return a; + + if (a < b) + swap(a, b); + a -= b; + a >>= 1; + if (a & r) + a += b; + a >>= 1; + } +} + +static void mipi_dsi_set_mode(struct mipi_dsi_northwest_info *mipi_dsi, + uint8_t mode); +static int mipi_dsi_dcs_cmd(struct mipi_dsi_northwest_info *mipi_dsi, + u8 cmd, const u32 *param, int num); + +static void mipi_dsi_set_mode(struct mipi_dsi_northwest_info *mipi_dsi, + uint8_t mode) +{ + switch (mode) { + case DSI_LP_MODE: + writel(0x1, mipi_dsi->mmio_base + HOST_CFG_NONCONTINUOUS_CLK); + break; + case DSI_HS_MODE: + writel(0x0, mipi_dsi->mmio_base + HOST_CFG_NONCONTINUOUS_CLK); + break; + default: + printf("invalid dsi mode\n"); + return; + } + + mdelay(1); +} + +static int mipi_dsi_dphy_init(struct mipi_dsi_northwest_info *mipi_dsi) +{ + uint32_t time_out = 100; + uint32_t lock; + uint32_t req_bit_clk; + uint32_t bpp; + + int i, best_div = -1; + int64_t delta; + uint64_t least_delta = ~0U; + uint64_t limit, div_result; + uint64_t denominator, numerator, divisor; + uint64_t norm_denom, norm_num, split_denom; + struct pll_divider div = { 0 }; + + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1, MIPI_ISO_DISABLE, MIPI_ISO_DISABLE); + + bpp = mipi_dsi_pixel_format_to_bpp(mipi_dsi->device->format); + + /* req_bit_clk is PLL out, clk_byte is 1/8th of the req_bit_clk + * We need meet clk_byte_freq >= dpi_pclk_freq * DPI_pixel_size / ( 8 * (cfg_num_lanes + 1)) + */ + + req_bit_clk = mipi_dsi->timings.pixelclock.typ; + req_bit_clk = req_bit_clk * bpp; + + switch (mipi_dsi->device->lanes) { + case 1: + break; + case 2: + req_bit_clk = req_bit_clk >> 1; + break; + case 4: + req_bit_clk = req_bit_clk >> 2; + break; + default: + printf("requested data lane num is invalid\n"); + return -EINVAL; + } + + debug("req_bit_clk %u\n", req_bit_clk); + + /* The max rate for PLL out is 800Mhz */ + if (req_bit_clk > mipi_dsi->max_data_rate) + return -EINVAL; + + /* calc CM, CN and CO according to PHY PLL formula: + * + * 'PLL out bitclk = refclk * CM / (CN * CO);' + * + * Let: + * 'numerator = bitclk / divisor'; + * 'denominator = refclk / divisor'; + * Then: + * 'numerator / denominator = CM / (CN * CO)'; + * + * CM is in [16, 255] + * CN is in [1, 32] + * CO is in { 1, 2, 4, 8 }; + */ + divisor = gcd(mipi_dsi->pll_ref, req_bit_clk); + WARN_ON(divisor == 1); + + div_result = req_bit_clk; + do_div(div_result, divisor); + numerator = div_result; + + div_result = mipi_dsi->pll_ref; + do_div(div_result, divisor); + denominator = div_result; + + /* denominator & numerator out of range check */ + if (DIV_ROUND_CLOSEST_ULL(numerator, denominator) > 255 || + DIV_ROUND_CLOSEST_ULL(denominator, numerator) > 32 * 8) + return -EINVAL; + + /* Normalization: reduce or increase + * numerator to [16, 255] + * denominator to [1, 32 * 8] + * Reduce normalization result is 'approximiate' + * Increase nomralization result is 'precise' + */ + if (numerator > 255 || denominator > 32 * 8) { + /* approximate */ + if (likely(numerator > denominator)) { + /* 'numerator > 255'; + * 'limit' should meet below conditions: + * a. '(numerator / limit) >= 16' + * b. '(denominator / limit) >= 1' + */ + limit = min(denominator, + DIV_ROUND_CLOSEST_ULL(numerator, 16)); + + /* Let: + * norm_num = numerator / i; + * norm_denom = denominator / i; + * + * So: + * delta = numerator * norm_denom - + * denominator * norm_num + */ + for (i = 2; i <= limit; i++) { + norm_num = DIV_ROUND_CLOSEST_ULL(numerator, i); + if (norm_num > 255) + continue; + + norm_denom = DIV_ROUND_CLOSEST_ULL(denominator, i); + + /* 'norm_num <= 255' && 'norm_num > norm_denom' + * so, 'norm_denom < 256' + */ + delta = numerator * norm_denom - + denominator * norm_num; + delta = abs(delta); + if (delta < least_delta) { + least_delta = delta; + best_div = i; + } else if (delta == least_delta) { + /* choose better one IF: + * 'norm_denom' derived from last 'best_div' + * needs later split, i.e, 'norm_denom > 32'. + */ + if (DIV_ROUND_CLOSEST_ULL(denominator, best_div) > 32) { + least_delta = delta; + best_div = i; + } + } + } + } else { + /* 'denominator > 32 * 8'; + * 'limit' should meet below conditions: + * a. '(numerator / limit >= 16' + * b. '(denominator / limit >= 1': obviously. + */ + limit = DIV_ROUND_CLOSEST_ULL(numerator, 16); + if (!limit || + DIV_ROUND_CLOSEST_ULL(denominator, limit) > 32 * 8) + return -EINVAL; + + for (i = 2; i <= limit; i++) { + norm_denom = DIV_ROUND_CLOSEST_ULL(denominator, i); + if (norm_denom > 32 * 8) + continue; + + norm_num = DIV_ROUND_CLOSEST_ULL(numerator, i); + + /* 'norm_denom <= 256' && 'norm_num < norm_denom' + * so, 'norm_num <= 255' + */ + delta = numerator * norm_denom - + denominator * norm_num; + delta = abs(delta); + if (delta < least_delta) { + least_delta = delta; + best_div = i; + } else if (delta == least_delta) { + if (DIV_ROUND_CLOSEST_ULL(denominator, best_div) > 32) { + least_delta = delta; + best_div = i; + } + } + } + } + + numerator = DIV_ROUND_CLOSEST_ULL(numerator, best_div); + denominator = DIV_ROUND_CLOSEST_ULL(denominator, best_div); + } else if (numerator < 16) { + /* precise */ + + /* 'limit' should meet below conditions: + * a. 'denominator * limit <= 32 * 8' + * b. '16 <= numerator * limit <= 255' + * Choose 'limit' to be the least value + * which makes 'numerator * limit' to be + * in [16, 255]. + */ + limit = min(256 / (uint32_t)denominator, + 255 / (uint32_t)numerator); + if (limit == 1 || limit < DIV_ROUND_UP_ULL(16, numerator)) + return -EINVAL; + + /* choose the least available value for 'limit' */ + limit = DIV_ROUND_UP_ULL(16, numerator); + numerator = numerator * limit; + denominator = denominator * limit; + + WARN_ON(numerator < 16 || denominator > 32 * 8); + } + + div.cm = cm_map_table[numerator - 16]; + + /* split 'denominator' to 'CN' and 'CO' */ + if (denominator > 32) { + /* traverse four possible values of 'CO' + * there must be some value of 'CO' can be used + */ + least_delta = ~0U; + for (i = 0; i < 4; i++) { + split_denom = DIV_ROUND_CLOSEST_ULL(denominator, 1 << i); + if (split_denom > 32) + continue; + + /* calc deviation to choose the best one */ + delta = denominator - split_denom * (1 << i); + delta = abs(delta); + if (delta < least_delta) { + least_delta = delta; + div.co = co_map_table[i]; + div.cn = cn_map_table[split_denom - 1]; + } + } + } else { + div.co = co_map_table[1 >> 1]; + div.cn = cn_map_table[denominator - 1]; + } + + debug("cn 0x%x, cm 0x%x, co 0x%x\n", div.cn, div.cm, div.co); + + writel(div.cn, mipi_dsi->mmio_base + DPHY_CN); + writel(div.cm, mipi_dsi->mmio_base + DPHY_CM); + writel(div.co, mipi_dsi->mmio_base + DPHY_CO); + + writel(0x25, mipi_dsi->mmio_base + DPHY_TST); + writel(0x0, mipi_dsi->mmio_base + DPHY_PD_PLL); + + while (!(lock = readl(mipi_dsi->mmio_base + DPHY_LOCK))) { + udelay(10); + time_out--; + if (time_out == 0) { + printf("cannot get the dphy lock = 0x%x\n", lock); + return -EINVAL; + } + } + debug("%s: dphy lock = 0x%x\n", __func__, lock); + + writel(0x0, mipi_dsi->mmio_base + DPHY_LOCK_BYP); + writel(0x1, mipi_dsi->mmio_base + DPHY_RTERM_SEL); + writel(0x0, mipi_dsi->mmio_base + DPHY_AUTO_PD_EN); + writel(0x1, mipi_dsi->mmio_base + DPHY_RXLPRP); + writel(0x1, mipi_dsi->mmio_base + DPHY_RXCDRP); + writel(0x0, mipi_dsi->mmio_base + DPHY_M_PRG_HS_PREPARE); + writel(0x0, mipi_dsi->mmio_base + DPHY_MC_PRG_HS_PREPARE); + writel(0x9, mipi_dsi->mmio_base + DPHY_M_PRG_HS_ZERO); + writel(0x20, mipi_dsi->mmio_base + DPHY_MC_PRG_HS_ZERO); + writel(0x5, mipi_dsi->mmio_base + DPHY_M_PRG_HS_TRAIL); + writel(0x5, mipi_dsi->mmio_base + DPHY_MC_PRG_HS_TRAIL); + writel(0x0, mipi_dsi->mmio_base + DPHY_PD_DPHY); + + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1CFG, DSI_PLL_EN, DSI_PLL_EN); + return 0; +} + +static int mipi_dsi_host_init(struct mipi_dsi_northwest_info *mipi_dsi) +{ + uint32_t lane_num; + + switch (mipi_dsi->device->lanes) { + case 1: + lane_num = 0x0; + break; + case 2: + lane_num = 0x1; + break; + default: + /* Invalid lane num */ + return -EINVAL; + } + + writel(lane_num, mipi_dsi->mmio_base + HOST_CFG_NUM_LANES); + writel(0x1, mipi_dsi->mmio_base + HOST_CFG_NONCONTINUOUS_CLK); + writel(0x1, mipi_dsi->mmio_base + HOST_CFG_T_PRE); + writel(52, mipi_dsi->mmio_base + HOST_CFG_T_POST); + writel(13, mipi_dsi->mmio_base + HOST_CFG_TX_GAP); + writel(0x1, mipi_dsi->mmio_base + HOST_CFG_AUTOINSERT_EOTP); + writel(0x0, mipi_dsi->mmio_base + HOST_CFG_EXTRA_CMDS_AFTER_EOTP); + writel(0x0, mipi_dsi->mmio_base + HOST_CFG_HTX_TO_COUNT); + writel(0x0, mipi_dsi->mmio_base + HOST_CFG_LRX_H_TO_COUNT); + writel(0x0, mipi_dsi->mmio_base + HOST_CFG_BTA_H_TO_COUNT); + writel(0x3A98, mipi_dsi->mmio_base + HOST_CFG_TWAKEUP); + + return 0; +} + +static int mipi_dsi_dpi_init(struct mipi_dsi_northwest_info *mipi_dsi) +{ + uint32_t color_coding, pixel_fmt; + int bpp; + struct display_timing *timings = &(mipi_dsi->timings); + + bpp = mipi_dsi_pixel_format_to_bpp(mipi_dsi->device->format); + if (bpp < 0) + return -EINVAL; + + writel(timings->hactive.typ, mipi_dsi->mmio_base + DPI_PIXEL_PAYLOAD_SIZE); + writel(timings->hactive.typ, mipi_dsi->mmio_base + DPI_PIXEL_FIFO_SEND_LEVEL); + + switch (bpp) { + case 24: + color_coding = 5; + pixel_fmt = 3; + break; + case 16: + case 18: + default: + /* Not supported */ + return -EINVAL; + } + writel(color_coding, mipi_dsi->mmio_base + DPI_INTERFACE_COLOR_CODING); + writel(pixel_fmt, mipi_dsi->mmio_base + DPI_PIXEL_FORMAT); + writel(0x0, mipi_dsi->mmio_base + DPI_VSYNC_POLARITY); + writel(0x0, mipi_dsi->mmio_base + DPI_HSYNC_POLARITY); + writel(0x2, mipi_dsi->mmio_base + DPI_VIDEO_MODE); + + writel(timings->hfront_porch.typ * (bpp >> 3), mipi_dsi->mmio_base + DPI_HFP); + writel(timings->hback_porch.typ * (bpp >> 3), mipi_dsi->mmio_base + DPI_HBP); + writel(timings->hsync_len.typ * (bpp >> 3), mipi_dsi->mmio_base + DPI_HSA); + writel(0x0, mipi_dsi->mmio_base + DPI_ENABLE_MULT_PKTS); + + writel(timings->vback_porch.typ, mipi_dsi->mmio_base + DPI_VBP); + writel(timings->vfront_porch.typ, mipi_dsi->mmio_base + DPI_VFP); + writel(0x1, mipi_dsi->mmio_base + DPI_BLLP_MODE); + writel(0x0, mipi_dsi->mmio_base + DPI_USE_NULL_PKT_BLLP); + + writel(timings->vactive.typ - 1, mipi_dsi->mmio_base + DPI_VACTIVE); + + writel(0x0, mipi_dsi->mmio_base + DPI_VC); + + return 0; +} + +static void mipi_dsi_init_interrupt(struct mipi_dsi_northwest_info *mipi_dsi) +{ + /* disable all the irqs */ + writel(0xffffffff, mipi_dsi->mmio_base + HOST_IRQ_MASK); + writel(0x7, mipi_dsi->mmio_base + HOST_IRQ_MASK2); +} + +static int mipi_display_enter_sleep(struct mipi_dsi_northwest_info *mipi_dsi) +{ + int err; + + err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_SET_DISPLAY_OFF, + NULL, 0); + if (err) + return -EINVAL; + mdelay(50); + + err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_ENTER_SLEEP_MODE, + NULL, 0); + if (err) + printf("MIPI DSI DCS Command sleep in error!\n"); + + mdelay(MIPI_LCD_SLEEP_MODE_DELAY); + + return err; +} + +static void mipi_dsi_wr_tx_header(struct mipi_dsi_northwest_info *mipi_dsi, + u8 di, u8 data0, u8 data1, u8 mode, u8 need_bta) +{ + uint32_t pkt_control = 0; + uint16_t word_count = 0; + + word_count = data0 | (data1 << 8); + pkt_control = HOST_PKT_CONTROL_WC(word_count) | + HOST_PKT_CONTROL_VC(0) | + HOST_PKT_CONTROL_DT(di) | + HOST_PKT_CONTROL_HS_SEL(mode) | + HOST_PKT_CONTROL_BTA_TX(need_bta); + + debug("pkt_control = %x\n", pkt_control); + writel(pkt_control, mipi_dsi->mmio_base + HOST_PKT_CONTROL); +} + +static void mipi_dsi_wr_tx_data(struct mipi_dsi_northwest_info *mipi_dsi, + uint32_t tx_data) +{ + writel(tx_data, mipi_dsi->mmio_base + HOST_TX_PAYLOAD); +} + +static void mipi_dsi_long_data_wr(struct mipi_dsi_northwest_info *mipi_dsi, + const uint8_t *data0, uint32_t data_size) +{ + uint32_t data_cnt = 0, payload = 0; + + /* in case that data count is more than 4 */ + for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) { + /* + * after sending 4bytes per one time, + * send remainder data less then 4. + */ + if ((data_size - data_cnt) < 4) { + if ((data_size - data_cnt) == 3) { + payload = data0[data_cnt] | + (data0[data_cnt + 1] << 8) | + (data0[data_cnt + 2] << 16); + debug("count = 3 payload = %x, %x %x %x\n", + payload, data0[data_cnt], data0[data_cnt + 1], data0[data_cnt + 2]); + } else if ((data_size - data_cnt) == 2) { + payload = data0[data_cnt] | + (data0[data_cnt + 1] << 8); + debug("count = 2 payload = %x, %x %x\n", + payload, data0[data_cnt], data0[data_cnt + 1]); + } else if ((data_size - data_cnt) == 1) { + payload = data0[data_cnt]; + debug("count = 1 payload = %x, %x\n", + payload, data0[data_cnt]); + } + + mipi_dsi_wr_tx_data(mipi_dsi, payload); + } else { + payload = data0[data_cnt] | + (data0[data_cnt + 1] << 8) | + (data0[data_cnt + 2] << 16) | + (data0[data_cnt + 3] << 24); + + debug("count = 4 payload = %x, %x %x %x %x\n", + payload, *(u8 *)(data0 + data_cnt), + data0[data_cnt + 1], + data0[data_cnt + 2], + data0[data_cnt + 3]); + + mipi_dsi_wr_tx_data(mipi_dsi, payload); + } + } +} + +static int wait_for_pkt_done(struct mipi_dsi_northwest_info *mipi_dsi, unsigned long timeout) +{ + uint32_t irq_status; + + do { + irq_status = readl(mipi_dsi->mmio_base + HOST_IRQ_STATUS); + if (irq_status & HOST_IRQ_STATUS_TX_PKT_DONE) + return timeout; + + udelay(1); + } while (--timeout); + + return 0; +} + +static int mipi_dsi_pkt_write(struct mipi_dsi_northwest_info *mipi_dsi, + u8 data_type, const u8 *buf, int len) +{ + int ret = 0; + const uint8_t *data = (const uint8_t *)buf; + + debug("mipi_dsi_pkt_write data_type 0x%x, buf 0x%x, len %u\n", data_type, (u32)buf, len); + + if (len == 0) + /* handle generic long write command */ + mipi_dsi_wr_tx_header(mipi_dsi, data_type, data[0], data[1], DSI_LP_MODE, 0); + else { + /* handle generic long write command */ + mipi_dsi_long_data_wr(mipi_dsi, data, len); + mipi_dsi_wr_tx_header(mipi_dsi, data_type, len & 0xff, + (len & 0xff00) >> 8, DSI_LP_MODE, 0); + } + + /* send packet */ + writel(0x1, mipi_dsi->mmio_base + HOST_SEND_PACKET); + ret = wait_for_pkt_done(mipi_dsi, MIPI_FIFO_TIMEOUT); + + if (!ret) { + printf("wait tx done timeout!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +#define DSI_CMD_BUF_MAXSIZE (128) + +static int mipi_dsi_dcs_cmd(struct mipi_dsi_northwest_info *mipi_dsi, + u8 cmd, const u32 *param, int num) +{ + int err = 0; + u32 buf[DSI_CMD_BUF_MAXSIZE]; + + switch (cmd) { + case MIPI_DCS_EXIT_SLEEP_MODE: + case MIPI_DCS_ENTER_SLEEP_MODE: + case MIPI_DCS_SET_DISPLAY_ON: + case MIPI_DCS_SET_DISPLAY_OFF: + buf[0] = cmd; + buf[1] = 0x0; + err = mipi_dsi_pkt_write(mipi_dsi, + MIPI_DSI_DCS_SHORT_WRITE, (u8 *)buf, 0); + break; + + default: + printf("MIPI DSI DCS Command:0x%x Not supported!\n", cmd); + break; + } + + return err; +} + +static void reset_dsi_domains(struct mipi_dsi_northwest_info *mipi_dsi, bool reset) +{ + /* escape domain */ + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1CFG, + DSI_RST_ESC_N, (reset ? 0 : DSI_RST_ESC_N)); + /* byte domain */ + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1CFG, + DSI_RST_BYTE_N, (reset ? 0 : DSI_RST_BYTE_N)); + + /* dpi domain */ + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1CFG, + DSI_RST_DPI_N, (reset ? 0 : DSI_RST_DPI_N)); +} + +static void mipi_dsi_shutdown(struct mipi_dsi_northwest_info *mipi_dsi) +{ + mipi_display_enter_sleep(mipi_dsi); + + writel(0x1, mipi_dsi->mmio_base + DPHY_PD_PLL); + writel(0x1, mipi_dsi->mmio_base + DPHY_PD_DPHY); + + enable_mipi_dsi_clk(false); + + reset_dsi_domains(mipi_dsi, true); +} + +static inline struct mipi_dsi_northwest_info *host_to_dsi(struct mipi_dsi_host *host) +{ + return container_of(host, struct mipi_dsi_northwest_info, dsi_host); +} + +static int mipi_dsi_northwest_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct mipi_dsi_northwest_info *mipi_dsi = host_to_dsi(host); + int ret; + + /* Assert resets */ + reset_dsi_domains(mipi_dsi, true); + + /* Enable mipi relevant clocks */ + enable_mipi_dsi_clk(true); + + ret = mipi_dsi_dphy_init(mipi_dsi); + if (ret < 0) + return ret; + + ret = mipi_dsi_host_init(mipi_dsi); + if (ret < 0) + return ret; + + ret = mipi_dsi_dpi_init(mipi_dsi); + if (ret < 0) + return ret; + + /* Deassert resets */ + reset_dsi_domains(mipi_dsi, false); + + /* display_en */ + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1CFG, DSI_SD, 0); + + /* normal cm */ + regmap_update_bits(mipi_dsi->sim, SIM_SOPT1CFG, DSI_CM, 0); + mdelay(20); + + /* Disable all interrupts, since we use polling */ + mipi_dsi_init_interrupt(mipi_dsi); + + return 0; +} + +static ssize_t mipi_dsi_northwest_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct mipi_dsi_northwest_info *dsi = host_to_dsi(host); + + if (!msg) + return -EINVAL; + + /* do some minimum sanity checking */ + if (!mipi_dsi_packet_format_is_short(msg->type) && + !mipi_dsi_packet_format_is_long(msg->type)) + return -EINVAL; + +#ifdef DEBUG + int i = 0; + u8 *p = msg->tx_buf; + + printf("sec_mipi_dsi_host_transfer\n"); + for (i; i < msg->tx_len; i++) { + printf("0x%.2x ", *(u8 *)p); + p++; + } + printf("\n"); +#endif + + if (mipi_dsi_packet_format_is_long(msg->type)) { + return mipi_dsi_pkt_write(dsi, msg->type, msg->tx_buf, msg->tx_len); + } else { + return mipi_dsi_pkt_write(dsi, msg->type, msg->tx_buf, 0); + } +} + + +static const struct mipi_dsi_host_ops mipi_dsi_northwest_host_ops = { + .attach = mipi_dsi_northwest_host_attach, + .transfer = mipi_dsi_northwest_host_transfer, +}; + +static int mipi_dsi_northwest_init(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops) +{ + struct mipi_dsi_northwest_info *dsi = dev_get_priv(dev); + int ret; + + dsi->max_data_lanes = max_data_lanes; + dsi->device = device; + dsi->dsi_host.ops = &mipi_dsi_northwest_host_ops; + device->host = &dsi->dsi_host; + + dsi->timings = *timings; + dsi->mmio_base = (void *)dev_read_addr(device->dev); + if ((fdt_addr_t)dsi->mmio_base == FDT_ADDR_T_NONE) { + dev_err(device->dev, "dsi dt register address error\n"); + return -EINVAL; + } + + ret = dev_read_u32(device->dev, "max-data-rate", &dsi->max_data_rate); + if (ret) { + dev_err(device->dev, "fail to get max-data-rate\n"); + return -EINVAL; + } + + ret = dev_read_u32(device->dev, "phy-ref-clkfreq", &dsi->pll_ref); + if (ret) { + dev_err(device->dev, "fail to get phy-ref-clkfreq\n"); + return -EINVAL; + } + + dsi->sim = syscon_regmap_lookup_by_phandle(device->dev, "sim"); + if (IS_ERR(dsi->sim)) { + dev_err(device->dev, "fail to get sim regmap\n"); + return PTR_ERR(dsi->sim); + } + + return 0; +} + +static int mipi_dsi_northwest_enable(struct udevice *dev) +{ + struct mipi_dsi_northwest_info *mipi_dsi = dev_get_priv(dev); + + /* Enter the HS mode for video stream */ + mipi_dsi_set_mode(mipi_dsi, DSI_HS_MODE); + + return 0; +} + +static int mipi_dsi_northwest_disable(struct udevice *dev) +{ + struct mipi_dsi_northwest_info *mipi_dsi = dev_get_priv(dev); + + mipi_dsi_shutdown(mipi_dsi); + return 0; +} + +struct dsi_host_ops mipi_dsi_northwest_ops = { + .init = mipi_dsi_northwest_init, + .enable = mipi_dsi_northwest_enable, + .disable = mipi_dsi_northwest_disable, +}; + +static int mipi_dsi_northwest_probe(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id mipi_dsi_northwest_ids[] = { + { .compatible = "northwest,mipi-dsi" }, + { } +}; + +U_BOOT_DRIVER(mipi_dsi_northwest) = { + .name = "mipi_dsi_northwest", + .id = UCLASS_DSI_HOST, + .of_match = mipi_dsi_northwest_ids, + .probe = mipi_dsi_northwest_probe, + .remove = mipi_dsi_northwest_disable, + .ops = &mipi_dsi_northwest_ops, + .priv_auto = sizeof(struct mipi_dsi_northwest_info), +}; diff --git a/drivers/video/nxp/imx/mipi_dsi_northwest_regs.h b/drivers/video/nxp/imx/mipi_dsi_northwest_regs.h new file mode 100644 index 00000000000..6493403a0c4 --- /dev/null +++ b/drivers/video/nxp/imx/mipi_dsi_northwest_regs.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#ifndef __MIPI_DSI_NORTHWEST_REGS_H +#define __MIPI_DSI_NORTHWEST_REGS_H + +/* ---------------------------- register offsets --------------------------- */ + +/* sim */ +#define SIM_SOPT1 0x0 +#define MIPI_ISO_DISABLE 0x8 + +#define SIM_SOPT1CFG 0x4 +#define DSI_RST_DPI_N 0x80000000 +#define DSI_RST_ESC_N 0x40000000 +#define DSI_RST_BYTE_N 0x20000000 +#define DSI_SD 0x200 +#define DSI_CM 0x100 +#define DSI_PLL_EN 0x80 + +/* dphy */ +#define DPHY_PD_DPHY 0x300 +#define DPHY_M_PRG_HS_PREPARE 0x304 +#define DPHY_MC_PRG_HS_PREPARE 0x308 +#define DPHY_M_PRG_HS_ZERO 0x30c +#define DPHY_MC_PRG_HS_ZERO 0x310 +#define DPHY_M_PRG_HS_TRAIL 0x314 +#define DPHY_MC_PRG_HS_TRAIL 0x318 +#define DPHY_PD_PLL 0x31c +#define DPHY_TST 0x320 +#define DPHY_CN 0x324 +#define DPHY_CM 0x328 +#define DPHY_CO 0x32c +#define DPHY_LOCK 0x330 +#define DPHY_LOCK_BYP 0x334 +#define DPHY_RTERM_SEL 0x338 +#define DPHY_AUTO_PD_EN 0x33c +#define DPHY_RXLPRP 0x340 +#define DPHY_RXCDRP 0x344 + +/* host */ +#define HOST_CFG_NUM_LANES 0x0 +#define HOST_CFG_NONCONTINUOUS_CLK 0x4 +#define HOST_CFG_T_PRE 0x8 +#define HOST_CFG_T_POST 0xc +#define HOST_CFG_TX_GAP 0x10 +#define HOST_CFG_AUTOINSERT_EOTP 0x14 +#define HOST_CFG_EXTRA_CMDS_AFTER_EOTP 0x18 +#define HOST_CFG_HTX_TO_COUNT 0x1c +#define HOST_CFG_LRX_H_TO_COUNT 0x20 +#define HOST_CFG_BTA_H_TO_COUNT 0x24 +#define HOST_CFG_TWAKEUP 0x28 +#define HOST_CFG_STATUS_OUT 0x2c +#define HOST_RX_ERROR_STATUS 0x30 + +/* dpi */ +#define DPI_PIXEL_PAYLOAD_SIZE 0x200 +#define DPI_PIXEL_FIFO_SEND_LEVEL 0x204 +#define DPI_INTERFACE_COLOR_CODING 0x208 +#define DPI_PIXEL_FORMAT 0x20c +#define DPI_VSYNC_POLARITY 0x210 +#define DPI_HSYNC_POLARITY 0x214 +#define DPI_VIDEO_MODE 0x218 +#define DPI_HFP 0x21c +#define DPI_HBP 0x220 +#define DPI_HSA 0x224 +#define DPI_ENABLE_MULT_PKTS 0x228 +#define DPI_VBP 0x22c +#define DPI_VFP 0x230 +#define DPI_BLLP_MODE 0x234 +#define DPI_USE_NULL_PKT_BLLP 0x238 +#define DPI_VACTIVE 0x23c +#define DPI_VC 0x240 + +/* apb pkt */ +#define HOST_TX_PAYLOAD 0x280 + +#define HOST_PKT_CONTROL 0x284 +#define HOST_PKT_CONTROL_WC(x) (((x) & 0xffff) << 0) +#define HOST_PKT_CONTROL_VC(x) (((x) & 0x3) << 16) +#define HOST_PKT_CONTROL_DT(x) (((x) & 0x3f) << 18) +#define HOST_PKT_CONTROL_HS_SEL(x) (((x) & 0x1) << 24) +#define HOST_PKT_CONTROL_BTA_TX(x) (((x) & 0x1) << 25) +#define HOST_PKT_CONTROL_BTA_NO_TX(x) (((x) & 0x1) << 26) + +#define HOST_SEND_PACKET 0x288 +#define HOST_PKT_STATUS 0x28c +#define HOST_PKT_FIFO_WR_LEVEL 0x290 +#define HOST_PKT_FIFO_RD_LEVEL 0x294 +#define HOST_PKT_RX_PAYLOAD 0x298 + +#define HOST_PKT_RX_PKT_HEADER 0x29c +#define HOST_PKT_RX_PKT_HEADER_WC(x) (((x) & 0xffff) << 0) +#define HOST_PKT_RX_PKT_HEADER_DT(x) (((x) & 0x3f) << 16) +#define HOST_PKT_RX_PKT_HEADER_VC(x) (((x) & 0x3) << 22) + +#define HOST_IRQ_STATUS 0x2a0 +#define HOST_IRQ_STATUS_SM_NOT_IDLE (1 << 0) +#define HOST_IRQ_STATUS_TX_PKT_DONE (1 << 1) +#define HOST_IRQ_STATUS_DPHY_DIRECTION (1 << 2) +#define HOST_IRQ_STATUS_TX_FIFO_OVFLW (1 << 3) +#define HOST_IRQ_STATUS_TX_FIFO_UDFLW (1 << 4) +#define HOST_IRQ_STATUS_RX_FIFO_OVFLW (1 << 5) +#define HOST_IRQ_STATUS_RX_FIFO_UDFLW (1 << 6) +#define HOST_IRQ_STATUS_RX_PKT_HDR_RCVD (1 << 7) +#define HOST_IRQ_STATUS_RX_PKT_PAYLOAD_DATA_RCVD (1 << 8) +#define HOST_IRQ_STATUS_HOST_BTA_TIMEOUT (1 << 29) +#define HOST_IRQ_STATUS_LP_RX_TIMEOUT (1 << 30) +#define HOST_IRQ_STATUS_HS_TX_TIMEOUT (1 << 31) + +#define HOST_IRQ_STATUS2 0x2a4 +#define HOST_IRQ_STATUS2_SINGLE_BIT_ECC_ERR (1 << 0) +#define HOST_IRQ_STATUS2_MULTI_BIT_ECC_ERR (1 << 1) +#define HOST_IRQ_STATUS2_CRC_ERR (1 << 2) + +#define HOST_IRQ_MASK 0x2a8 +#define HOST_IRQ_MASK_SM_NOT_IDLE_MASK (1 << 0) +#define HOST_IRQ_MASK_TX_PKT_DONE_MASK (1 << 1) +#define HOST_IRQ_MASK_DPHY_DIRECTION_MASK (1 << 2) +#define HOST_IRQ_MASK_TX_FIFO_OVFLW_MASK (1 << 3) +#define HOST_IRQ_MASK_TX_FIFO_UDFLW_MASK (1 << 4) +#define HOST_IRQ_MASK_RX_FIFO_OVFLW_MASK (1 << 5) +#define HOST_IRQ_MASK_RX_FIFO_UDFLW_MASK (1 << 6) +#define HOST_IRQ_MASK_RX_PKT_HDR_RCVD_MASK (1 << 7) +#define HOST_IRQ_MASK_RX_PKT_PAYLOAD_DATA_RCVD_MASK (1 << 8) +#define HOST_IRQ_MASK_HOST_BTA_TIMEOUT_MASK (1 << 29) +#define HOST_IRQ_MASK_LP_RX_TIMEOUT_MASK (1 << 30) +#define HOST_IRQ_MASK_HS_TX_TIMEOUT_MASK (1 << 31) + +#define HOST_IRQ_MASK2 0x2ac +#define HOST_IRQ_MASK2_SINGLE_BIT_ECC_ERR_MASK (1 << 0) +#define HOST_IRQ_MASK2_MULTI_BIT_ECC_ERR_MASK (1 << 1) +#define HOST_IRQ_MASK2_CRC_ERR_MASK (1 << 2) + +/* ------------------------------------- end -------------------------------- */ + +#endif diff --git a/drivers/video/nxp/imx/mxc_ipuv3_fb.c b/drivers/video/nxp/imx/mxc_ipuv3_fb.c new file mode 100644 index 00000000000..788cea7b890 --- /dev/null +++ b/drivers/video/nxp/imx/mxc_ipuv3_fb.c @@ -0,0 +1,676 @@ +// 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 <common.h> +#include <log.h> +#include <part.h> +#include <asm/cache.h> +#include <linux/errno.h> +#include <asm/global_data.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/fb.h> +#include <asm/io.h> +#include <asm/mach-imx/video.h> +#include <malloc.h> +#include <video_fb.h> +#include "../../videomodes.h" +#include "ipu.h" +#include "mxcfb.h" +#include "ipu_regs.h" +#include "display.h" +#include <panel.h> + +#include <dm.h> +#include <video.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int mxcfb_map_video_memory(struct fb_info *fbi); +static int mxcfb_unmap_video_memory(struct fb_info *fbi); + +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 { + struct udevice *udev; + 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; +} + +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); + + 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) +{ + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + struct video_uc_plat *plat = dev_get_uclass_plat(mxc_fbi->udev); + + 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); + + fbi->screen_base = (char *)plat->base; + + 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; + gd->fb_base = fbi->fix.smem_start; + + /* 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; +} + +extern struct clk *g_ipu_clk; + +/* + * 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(struct udevice *dev, u32 interface_pix_fmt, + uint8_t disp, struct fb_videomode const *mode) +{ + struct fb_info *fbi; + struct mxcfb_info *mxcfbi; + + /* + * Initialize FB structures + */ + fbi = mxcfb_init_fbinfo(); + if (!fbi) + return -ENOMEM; + + 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; + mxcfbi->udev = dev; + + if (!ipu_clk_enabled()) + clk_enable(g_ipu_clk); + + ipu_disp_set_global_alpha(mxcfbi->ipu_ch, 1, 0x80); + ipu_disp_set_color_key(mxcfbi->ipu_ch, 0, 0); + + 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_virtual * + (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; + + /* allocate fb first */ + if (mxcfb_map_video_memory(fbi) < 0) + return -ENOMEM; + + mxcfb_set_par(fbi); + +#ifdef DEBUG + ipu_dump_registers(); +#endif + + return 0; +} + +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]); + } +} + +int ipuv3_fb_init(struct fb_videomode const *mode, + uint8_t disp, + uint32_t pixfmt) +{ + gmode = mode; + gdisp = disp; + gpixfmt = pixfmt; + + return 0; +} + +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_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); +#if defined(CONFIG_DISPLAY) + struct udevice *disp_dev; +#endif + 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(dev, gpixfmt, gdisp, gmode); + if (ret < 0) + return ret; + +#if defined(CONFIG_DISPLAY) + ret = uclass_first_device(UCLASS_DISPLAY, &disp_dev); + if (disp_dev) { + ret = display_enable(disp_dev, 16, NULL); + if (ret < 0) + return ret; + } +#endif + if (CONFIG_IS_ENABLED(PANEL)) { + struct udevice *panel_dev; + + ret = uclass_get_device(UCLASS_PANEL, 0, &panel_dev); + if (panel_dev) + panel_enable_backlight(panel_dev); + } + + 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); + gd->fb_base = fb_start; + + return 0; +} + +struct ipuv3_video_priv { + ulong regs; +}; + +static int ipuv3_video_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * + (1 << VIDEO_BPP32) / 8; + + return 0; +} + +static const struct udevice_id ipuv3_video_ids[] = { +#ifdef CONFIG_ARCH_MX6 + { .compatible = "fsl,imx6q-ipu" }, +#endif +#ifdef CONFIG_ARCH_MX5 + { .compatible = "fsl,imx53-ipu" }, +#endif + { } +}; + +U_BOOT_DRIVER(fsl_imx6q_ipu) = { + .name = "fsl_imx6q_ipu", + .id = UCLASS_VIDEO, + .of_match = ipuv3_video_ids, + .bind = ipuv3_video_bind, + .probe = ipuv3_video_probe, + .priv_auto = sizeof(struct ipuv3_video_priv), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/video/nxp/imx/mxcfb.h b/drivers/video/nxp/imx/mxcfb.h new file mode 100644 index 00000000000..0dc38861938 --- /dev/null +++ b/drivers/video/nxp/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/nxp/imx/nw_dsi_imx.c b/drivers/video/nxp/imx/nw_dsi_imx.c new file mode 100644 index 00000000000..5daf86d4e42 --- /dev/null +++ b/drivers/video/nxp/imx/nw_dsi_imx.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dsi_host.h> +#include <mipi_dsi.h> +#include <panel.h> +#include <reset.h> +#include <video.h> +#include <video_bridge.h> +#include <video_link.h> +#include <asm/io.h> +#include <asm/arch/gpio.h> +#include <dm/device-internal.h> +#include <linux/iopoll.h> +#include <linux/err.h> +#include <power/regulator.h> +#include <regmap.h> +#include <syscon.h> + +struct nw_dsi_imx_priv { + struct mipi_dsi_device device; + struct udevice *panel; + struct udevice *dsi_host; + unsigned int data_lanes; +}; + +static int nw_dsi_imx_attach(struct udevice *dev) +{ + struct nw_dsi_imx_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct mipi_dsi_panel_plat *mplat; + struct display_timing timings; + int ret; + + priv->panel = video_link_get_next_device(dev); + if (!priv->panel || + device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + dev_err(dev, "get panel device error\n"); + return -ENODEV; + } + + mplat = dev_get_plat(priv->panel); + mplat->device = &priv->device; + + ret = video_link_get_display_timings(&timings); + if (ret) { + dev_err(dev, "decode display timing error %d\n", ret); + return ret; + } + + ret = uclass_get_device(UCLASS_DSI_HOST, 0, &priv->dsi_host); + if (ret) { + dev_err(dev, "No video dsi host detected %d\n", ret); + return ret; + } + + ret = dsi_host_init(priv->dsi_host, device, &timings, + priv->data_lanes, + NULL); + if (ret) { + dev_err(dev, "failed to initialize mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int nw_dsi_imx_set_backlight(struct udevice *dev, int percent) +{ + struct nw_dsi_imx_priv *priv = dev_get_priv(dev); + int ret; + + ret = panel_enable_backlight(priv->panel); + if (ret) { + dev_err(dev, "panel %s enable backlight error %d\n", + priv->panel->name, ret); + return ret; + } + + ret = dsi_host_enable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int nw_dsi_imx_probe(struct udevice *dev) +{ + struct nw_dsi_imx_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + int ret; + + device->dev = dev; + + ret = dev_read_u32(dev, "data-lanes-num", &priv->data_lanes); + if (ret) { + printf("fail to get data lanes property %d\n", ret); + return -EINVAL; + } + + return ret; +} + +static int nw_dsi_imx_remove(struct udevice *dev) +{ + struct nw_dsi_imx_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->panel) + device_remove(priv->panel, DM_REMOVE_NORMAL); + + ret = dsi_host_disable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } + + return 0; +} + +struct video_bridge_ops nw_dsi_imx_ops = { + .attach = nw_dsi_imx_attach, + .set_backlight = nw_dsi_imx_set_backlight, +}; + +static const struct udevice_id nw_dsi_imx_ids[] = { + { .compatible = "fsl,imx7ulp-mipi-dsi" }, + { } +}; + +U_BOOT_DRIVER(nw_dsi_imx) = { + .name = "nw_dsi_imx", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = nw_dsi_imx_ids, + .bind = dm_scan_fdt_dev, + .remove = nw_dsi_imx_remove, + .probe = nw_dsi_imx_probe, + .ops = &nw_dsi_imx_ops, + .priv_auto = sizeof(struct nw_dsi_imx_priv), +}; diff --git a/drivers/video/nxp/imx/sec_dsim_imx.c b/drivers/video/nxp/imx/sec_dsim_imx.c new file mode 100644 index 00000000000..644dafb2992 --- /dev/null +++ b/drivers/video/nxp/imx/sec_dsim_imx.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dsi_host.h> +#include <mipi_dsi.h> +#include <panel.h> +#include <reset.h> +#include <video.h> +#include <video_bridge.h> +#include <video_link.h> +#include <asm/io.h> +#include <asm/arch/gpio.h> +#include <dm/device-internal.h> +#include <linux/iopoll.h> +#include <linux/err.h> +#include <power/regulator.h> +#include <regmap.h> +#include <syscon.h> + +/* fixed phy ref clk rate */ +#define PHY_REF_CLK 27000000 + +struct imx_sec_dsim_priv { + struct mipi_dsi_device device; + void __iomem *base; + struct udevice *panel; + struct udevice *dsi_host; + struct reset_ctl_bulk soft_resetn; + struct reset_ctl_bulk clk_enable; + struct reset_ctl_bulk mipi_reset; +}; + +#if IS_ENABLED(CONFIG_DM_RESET) +static int sec_dsim_rstc_reset(struct reset_ctl_bulk *rstc, bool assert) +{ + int ret; + + if (!rstc) + return 0; + + ret = assert ? reset_assert_bulk(rstc) : + reset_deassert_bulk(rstc); + + return ret; +} + +static int sec_dsim_of_parse_resets(struct udevice *dev) +{ + int ret; + ofnode parent, child; + struct ofnode_phandle_args args; + struct reset_ctl_bulk rstc; + const char *compat; + uint32_t rstc_num = 0; + + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); + + ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, + 0, &args); + if (ret) + return ret; + + parent = args.node; + ofnode_for_each_subnode(child, parent) { + compat = ofnode_get_property(child, "compatible", NULL); + if (!compat) + continue; + + ret = reset_get_bulk_nodev(child, &rstc); + if (ret) + continue; + + if (!of_compat_cmp("dsi,soft-resetn", compat, 0)) { + priv->soft_resetn = rstc; + rstc_num++; + } else if (!of_compat_cmp("dsi,clk-enable", compat, 0)) { + priv->clk_enable = rstc; + rstc_num++; + } else if (!of_compat_cmp("dsi,mipi-reset", compat, 0)) { + priv->mipi_reset = rstc; + rstc_num++; + } else + dev_warn(dev, "invalid dsim reset node: %s\n", compat); + } + + if (!rstc_num) { + dev_err(dev, "no invalid reset control exists\n"); + return -EINVAL; + } + + return 0; +} +#endif + +static int imx_sec_dsim_attach(struct udevice *dev) +{ + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct mipi_dsi_panel_plat *mplat; + struct display_timing timings; + int ret; + + priv->panel = video_link_get_next_device(dev); + if (!priv->panel || + device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + dev_err(dev, "get panel device error\n"); + return -ENODEV; + } + + mplat = dev_get_plat(priv->panel); + mplat->device = &priv->device; + + ret = video_link_get_display_timings(&timings); + if (ret) { + dev_err(dev, "decode display timing error %d\n", ret); + return ret; + } + + ret = uclass_get_device(UCLASS_DSI_HOST, 0, &priv->dsi_host); + if (ret) { + dev_err(dev, "No video dsi host detected %d\n", ret); + return ret; + } + + ret = dsi_host_init(priv->dsi_host, device, &timings, 4, + NULL); + if (ret) { + dev_err(dev, "failed to initialize mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int imx_sec_dsim_set_backlight(struct udevice *dev, int percent) +{ + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); + int ret; + + ret = panel_enable_backlight(priv->panel); + if (ret) { + dev_err(dev, "panel %s enable backlight error %d\n", + priv->panel->name, ret); + return ret; + } + + ret = dsi_host_enable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } + + return 0; +} + +static int imx_sec_dsim_probe(struct udevice *dev) +{ + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + + device->dev = dev; + +#if IS_ENABLED(CONFIG_DM_RESET) + int ret; + /* Allow to not have resets */ + ret = sec_dsim_of_parse_resets(dev); + if (!ret) { + ret = sec_dsim_rstc_reset(&priv->soft_resetn, false); + if (ret) { + dev_err(dev, "deassert soft_resetn failed\n"); + return ret; + } + + ret = sec_dsim_rstc_reset(&priv->clk_enable, true); + if (ret) { + dev_err(dev, "assert clk_enable failed\n"); + return ret; + } + + ret = sec_dsim_rstc_reset(&priv->mipi_reset, false); + if (ret) { + dev_err(dev, "deassert mipi_reset failed\n"); + return ret; + } + } +#endif + + return 0; +} + +static int imx_sec_dsim_remove(struct udevice *dev) +{ + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->panel) + device_remove(priv->panel, DM_REMOVE_NORMAL); + + ret = dsi_host_disable(priv->dsi_host); + if (ret) { + dev_err(dev, "failed to enable mipi dsi host\n"); + return ret; + } + + return 0; +} + +struct video_bridge_ops imx_sec_dsim_ops = { + .attach = imx_sec_dsim_attach, + .set_backlight = imx_sec_dsim_set_backlight, +}; + +static const struct udevice_id imx_sec_dsim_ids[] = { + { .compatible = "fsl,imx8mm-mipi-dsim" }, + { .compatible = "fsl,imx8mn-mipi-dsim" }, + { .compatible = "fsl,imx8mp-mipi-dsim" }, + { } +}; + +U_BOOT_DRIVER(imx_sec_dsim) = { + .name = "imx_sec_dsim", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = imx_sec_dsim_ids, + .bind = dm_scan_fdt_dev, + .remove = imx_sec_dsim_remove, + .probe = imx_sec_dsim_probe, + .ops = &imx_sec_dsim_ops, + .priv_auto = sizeof(struct imx_sec_dsim_priv), +}; diff --git a/drivers/video/nxp/imx/sec_mipi_dsim.c b/drivers/video/nxp/imx/sec_mipi_dsim.c new file mode 100644 index 00000000000..d30021e2cd2 --- /dev/null +++ b/drivers/video/nxp/imx/sec_mipi_dsim.c @@ -0,0 +1,1068 @@ +/* + * Copyright 2018 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <asm/io.h> +#include <linux/err.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <asm/unaligned.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <div64.h> +#include <video_bridge.h> +#include <panel.h> +#include <dsi_host.h> +#include <asm/arch/gpio.h> +#include <dm/device-internal.h> + +#define MIPI_FIFO_TIMEOUT 250000 /* 250ms */ + +#define DRIVER_NAME "sec_mipi_dsim" + +/* dsim registers */ +#define DSIM_VERSION 0x00 +#define DSIM_STATUS 0x04 +#define DSIM_RGB_STATUS 0x08 +#define DSIM_SWRST 0x0c +#define DSIM_CLKCTRL 0x10 +#define DSIM_TIMEOUT 0x14 +#define DSIM_CONFIG 0x18 +#define DSIM_ESCMODE 0x1c +#define DSIM_MDRESOL 0x20 +#define DSIM_MVPORCH 0x24 +#define DSIM_MHPORCH 0x28 +#define DSIM_MSYNC 0x2c +#define DSIM_SDRESOL 0x30 +#define DSIM_INTSRC 0x34 +#define DSIM_INTMSK 0x38 + +/* packet */ +#define DSIM_PKTHDR 0x3c +#define DSIM_PAYLOAD 0x40 +#define DSIM_RXFIFO 0x44 +#define DSIM_FIFOTHLD 0x48 +#define DSIM_FIFOCTRL 0x4c +#define DSIM_MEMACCHR 0x50 +#define DSIM_MULTI_PKT 0x78 + +/* pll control */ +#define DSIM_PLLCTRL_1G 0x90 +#define DSIM_PLLCTRL 0x94 +#define DSIM_PLLCTRL1 0x98 +#define DSIM_PLLCTRL2 0x9c +#define DSIM_PLLTMR 0xa0 + +/* dphy */ +#define DSIM_PHYTIMING 0xb4 +#define DSIM_PHYTIMING1 0xb8 +#define DSIM_PHYTIMING2 0xbc + +/* reg bit manipulation */ +#define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s)) +#define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s)) +#define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s)) + +/* register bit fields */ +#define STATUS_PLLSTABLE BIT(31) +#define STATUS_SWRSTRLS BIT(20) +#define STATUS_TXREADYHSCLK BIT(10) +#define STATUS_ULPSCLK BIT(9) +#define STATUS_STOPSTATECLK BIT(8) +#define STATUS_GET_ULPSDAT(x) REG_GET(x, 7, 4) +#define STATUS_GET_STOPSTATEDAT(x) REG_GET(x, 3, 0) + +#define RGB_STATUS_CMDMODE_INSEL BIT(31) +#define RGB_STATUS_GET_RGBSTATE(x) REG_GET(x, 12, 0) + +#define CLKCTRL_TXREQUESTHSCLK BIT(31) +#define CLKCTRL_DPHY_SEL_1G BIT(29) +#define CLKCTRL_DPHY_SEL_1P5G (0x0 << 29) +#define CLKCTRL_ESCCLKEN BIT(28) +#define CLKCTRL_PLLBYPASS BIT(29) +#define CLKCTRL_BYTECLKSRC_DPHY_PLL REG_PUT(0, 26, 25) +#define CLKCTRL_BYTECLKEN BIT(24) +#define CLKCTRL_SET_LANEESCCLKEN(x) REG_PUT(x, 23, 19) +#define CLKCTRL_SET_ESCPRESCALER(x) REG_PUT(x, 15, 0) + +#define TIMEOUT_SET_BTAOUT(x) REG_PUT(x, 23, 16) +#define TIMEOUT_SET_LPDRTOUT(x) REG_PUT(x, 15, 0) + +#define CONFIG_NON_CONTINOUS_CLOCK_LANE BIT(31) +#define CONFIG_CLKLANE_STOP_START BIT(30) +#define CONFIG_MFLUSH_VS BIT(29) +#define CONFIG_EOT_R03 BIT(28) +#define CONFIG_SYNCINFORM BIT(27) +#define CONFIG_BURSTMODE BIT(26) +#define CONFIG_VIDEOMODE BIT(25) +#define CONFIG_AUTOMODE BIT(24) +#define CONFIG_HSEDISABLEMODE BIT(23) +#define CONFIG_HFPDISABLEMODE BIT(22) +#define CONFIG_HBPDISABLEMODE BIT(21) +#define CONFIG_HSADISABLEMODE BIT(20) +#define CONFIG_SET_MAINVC(x) REG_PUT(x, 19, 18) +#define CONFIG_SET_SUBVC(x) REG_PUT(x, 17, 16) +#define CONFIG_SET_MAINPIXFORMAT(x) REG_PUT(x, 14, 12) +#define CONFIG_SET_SUBPIXFORMAT(x) REG_PUT(x, 10, 8) +#define CONFIG_SET_NUMOFDATLANE(x) REG_PUT(x, 6, 5) +#define CONFIG_SET_LANEEN(x) REG_PUT(x, 4, 0) + +#define ESCMODE_SET_STOPSTATE_CNT(X) REG_PUT(x, 31, 21) +#define ESCMODE_FORCESTOPSTATE BIT(20) +#define ESCMODE_FORCEBTA BIT(16) +#define ESCMODE_CMDLPDT BIT(7) +#define ESCMODE_TXLPDT BIT(6) +#define ESCMODE_TXTRIGGERRST BIT(5) + +#define MDRESOL_MAINSTANDBY BIT(31) +#define MDRESOL_SET_MAINVRESOL(x) REG_PUT(x, 27, 16) +#define MDRESOL_SET_MAINHRESOL(x) REG_PUT(x, 11, 0) + +#define MVPORCH_SET_CMDALLOW(x) REG_PUT(x, 31, 28) +#define MVPORCH_SET_STABLEVFP(x) REG_PUT(x, 26, 16) +#define MVPORCH_SET_MAINVBP(x) REG_PUT(x, 10, 0) + +#define MHPORCH_SET_MAINHFP(x) REG_PUT(x, 31, 16) +#define MHPORCH_SET_MAINHBP(x) REG_PUT(x, 15, 0) + +#define MSYNC_SET_MAINVSA(x) REG_PUT(x, 31, 22) +#define MSYNC_SET_MAINHSA(x) REG_PUT(x, 15, 0) + +#define INTSRC_PLLSTABLE BIT(31) +#define INTSRC_SWRSTRELEASE BIT(30) +#define INTSRC_SFRPLFIFOEMPTY BIT(29) +#define INTSRC_SFRPHFIFOEMPTY BIT(28) +#define INTSRC_FRAMEDONE BIT(24) +#define INTSRC_LPDRTOUT BIT(21) +#define INTSRC_TATOUT BIT(20) +#define INTSRC_RXDATDONE BIT(18) +#define INTSRC_MASK (INTSRC_PLLSTABLE | \ + INTSRC_SWRSTRELEASE | \ + INTSRC_SFRPLFIFOEMPTY | \ + INTSRC_SFRPHFIFOEMPTY | \ + INTSRC_FRAMEDONE | \ + INTSRC_LPDRTOUT | \ + INTSRC_TATOUT | \ + INTSRC_RXDATDONE) + +#define INTMSK_MSKPLLSTABLE BIT(31) +#define INTMSK_MSKSWRELEASE BIT(30) +#define INTMSK_MSKSFRPLFIFOEMPTY BIT(29) +#define INTMSK_MSKSFRPHFIFOEMPTY BIT(28) +#define INTMSK_MSKFRAMEDONE BIT(24) +#define INTMSK_MSKLPDRTOUT BIT(21) +#define INTMSK_MSKTATOUT BIT(20) +#define INTMSK_MSKRXDATDONE BIT(18) + +#define PKTHDR_SET_DATA1(x) REG_PUT(x, 23, 16) +#define PKTHDR_GET_DATA1(x) REG_GET(x, 23, 16) +#define PKTHDR_SET_DATA0(x) REG_PUT(x, 15, 8) +#define PKTHDR_GET_DATA0(x) REG_GET(x, 15, 8) +#define PKTHDR_GET_WC(x) REG_GET(x, 23, 8) +#define PKTHDR_SET_DI(x) REG_PUT(x, 7, 0) +#define PKTHDR_GET_DI(x) REG_GET(x, 7, 0) +#define PKTHDR_SET_DT(x) REG_PUT(x, 5, 0) +#define PKTHDR_GET_DT(x) REG_GET(x, 5, 0) +#define PKTHDR_SET_VC(x) REG_PUT(x, 7, 6) +#define PKTHDR_GET_VC(x) REG_GET(x, 7, 6) + +#define FIFOCTRL_FULLRX BIT(25) +#define FIFOCTRL_EMPTYRX BIT(24) +#define FIFOCTRL_FULLHSFR BIT(23) +#define FIFOCTRL_EMPTYHSFR BIT(22) +#define FIFOCTRL_FULLLSFR BIT(21) +#define FIFOCTRL_EMPTYLSFR BIT(20) +#define FIFOCTRL_FULLHMAIN BIT(11) +#define FIFOCTRL_EMPTYHMAIN BIT(10) +#define FIFOCTRL_FULLLMAIN BIT(9) +#define FIFOCTRL_EMPTYLMAIN BIT(8) +#define FIFOCTRL_NINITRX BIT(4) +#define FIFOCTRL_NINITSFR BIT(3) +#define FIFOCTRL_NINITI80 BIT(2) +#define FIFOCTRL_NINITSUB BIT(1) +#define FIFOCTRL_NINITMAIN BIT(0) + +#define PLLCTRL_DPDNSWAP_CLK BIT(25) +#define PLLCTRL_DPDNSWAP_DAT BIT(24) +#define PLLCTRL_PLLEN BIT(23) +#define PLLCTRL_SET_PMS(x) REG_PUT(x, 19, 1) + +#define PHYTIMING_SET_M_TLPXCTL(x) REG_PUT(x, 15, 8) +#define PHYTIMING_SET_M_THSEXITCTL(x) REG_PUT(x, 7, 0) + +#define PHYTIMING1_SET_M_TCLKPRPRCTL(x) REG_PUT(x, 31, 24) +#define PHYTIMING1_SET_M_TCLKZEROCTL(x) REG_PUT(x, 23, 16) +#define PHYTIMING1_SET_M_TCLKPOSTCTL(x) REG_PUT(x, 15, 8) +#define PHYTIMING1_SET_M_TCLKTRAILCTL(x) REG_PUT(x, 7, 0) + +#define PHYTIMING2_SET_M_THSPRPRCTL(x) REG_PUT(x, 23, 16) +#define PHYTIMING2_SET_M_THSZEROCTL(x) REG_PUT(x, 15, 8) +#define PHYTIMING2_SET_M_THSTRAILCTL(x) REG_PUT(x, 7, 0) + +#define dsim_read(dsim, reg) readl(dsim->base + reg) +#define dsim_write(dsim, val, reg) writel(val, dsim->base + reg) + +#define MAX_MAIN_HRESOL 2047 +#define MAX_MAIN_VRESOL 2047 +#define MAX_SUB_HRESOL 1024 +#define MAX_SUB_VRESOL 1024 + +/* in KHZ */ +#define MAX_ESC_CLK_FREQ 20000 + +/* dsim all irqs index */ +#define PLLSTABLE 1 +#define SWRSTRELEASE 2 +#define SFRPLFIFOEMPTY 3 +#define SFRPHFIFOEMPTY 4 +#define SYNCOVERRIDE 5 +#define BUSTURNOVER 6 +#define FRAMEDONE 7 +#define LPDRTOUT 8 +#define TATOUT 9 +#define RXDATDONE 10 +#define RXTE 11 +#define RXACK 12 +#define ERRRXECC 13 +#define ERRRXCRC 14 +#define ERRESC3 15 +#define ERRESC2 16 +#define ERRESC1 17 +#define ERRESC0 18 +#define ERRSYNC3 19 +#define ERRSYNC2 20 +#define ERRSYNC1 21 +#define ERRSYNC0 22 +#define ERRCONTROL3 23 +#define ERRCONTROL2 24 +#define ERRCONTROL1 25 +#define ERRCONTROL0 26 + +/* Dispmix Control & GPR Registers */ +#define DISPLAY_MIX_SFT_RSTN_CSR 0x00 +#ifdef CONFIG_IMX8MN +#define MIPI_DSI_I_PRESETn_SFT_EN BIT(0) | BIT(1) +#else + #define MIPI_DSI_I_PRESETn_SFT_EN BIT(5) +#endif +#define DISPLAY_MIX_CLK_EN_CSR 0x04 + +#ifdef CONFIG_IMX8MN +#define MIPI_DSI_PCLK_SFT_EN BIT(0) +#define MIPI_DSI_CLKREF_SFT_EN BIT(1) +#else + #define MIPI_DSI_PCLK_SFT_EN BIT(8) + #define MIPI_DSI_CLKREF_SFT_EN BIT(9) +#endif +#define GPR_MIPI_RESET_DIV 0x08 + /* Clock & Data lanes reset: Active Low */ + #define GPR_MIPI_S_RESETN BIT(16) + #define GPR_MIPI_M_RESETN BIT(17) + +#define PS2KHZ(ps) (1000000000UL / (ps)) + +#define MIPI_HFP_PKT_OVERHEAD 6 +#define MIPI_HBP_PKT_OVERHEAD 6 +#define MIPI_HSA_PKT_OVERHEAD 6 + + +/* DSIM PLL configuration from spec: + * + * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S) + * Fin_pll = Fin / P (6 ~ 12 MHz) + * S: [2:0], M: [12:3], P: [18:13], so + * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33] + * + */ + +struct sec_mipi_dsim { + void __iomem *base; + + /* kHz clocks */ + uint64_t pix_clk; + uint64_t bit_clk; + + unsigned int lanes; + unsigned int channel; /* virtual channel */ + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + unsigned int pms; + unsigned int p; + unsigned int m; + unsigned int s; + + struct mipi_dsi_device *device; + uint32_t max_data_lanes; + uint64_t max_data_rate; + + struct mipi_dsi_host dsi_host; + + struct display_timing timings; +}; + +static int sec_mipi_dsim_wait_for_pkt_done(struct sec_mipi_dsim *dsim, unsigned long timeout) +{ + uint32_t intsrc; + + do { + intsrc = dsim_read(dsim, DSIM_INTSRC); + if (intsrc & INTSRC_SFRPLFIFOEMPTY) { + dsim_write(dsim, INTSRC_SFRPLFIFOEMPTY, DSIM_INTSRC); + return 0; + } + + udelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + +static int sec_mipi_dsim_wait_for_hdr_done(struct sec_mipi_dsim *dsim, unsigned long timeout) +{ + uint32_t intsrc; + + do { + intsrc = dsim_read(dsim, DSIM_INTSRC); + if (intsrc & INTSRC_SFRPHFIFOEMPTY) { + dsim_write(dsim, INTSRC_SFRPHFIFOEMPTY, DSIM_INTSRC); + return 0; + } + + udelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + + +static int sec_mipi_dsim_wait_for_rx_done(struct sec_mipi_dsim *dsim, unsigned long timeout) +{ + uint32_t intsrc; + + do { + intsrc = dsim_read(dsim, DSIM_INTSRC); + if (intsrc & INTSRC_RXDATDONE) { + dsim_write(dsim, INTSRC_RXDATDONE, DSIM_INTSRC); + return 0; + } + + udelay(1); + } while (--timeout); + + return -ETIMEDOUT; +} + +static int sec_mipi_dsim_wait_pll_stable(struct sec_mipi_dsim *dsim) +{ + uint32_t status; + ulong start; + + start = get_timer(0); /* Get current timestamp */ + + do { + status = dsim_read(dsim, DSIM_STATUS); + if (status & STATUS_PLLSTABLE) + return 0; + } while (get_timer(0) < (start + 100)); /* Wait 100ms */ + + return -ETIMEDOUT; +} + +static int sec_mipi_dsim_config_pll(struct sec_mipi_dsim *dsim) +{ + int ret; + uint32_t pllctrl = 0, status, data_lanes_en, stop; + + dsim_write(dsim, 0x8000, DSIM_PLLTMR); + + /* TODO: config dp/dn swap if requires */ + + pllctrl |= PLLCTRL_SET_PMS(dsim->pms) | PLLCTRL_PLLEN; + dsim_write(dsim, pllctrl, DSIM_PLLCTRL); + + ret = sec_mipi_dsim_wait_pll_stable(dsim); + if (ret) { + printf("wait for pll stable time out\n"); + return ret; + } + + /* wait for clk & data lanes to go to stop state */ + mdelay(1); + + data_lanes_en = (0x1 << dsim->lanes) - 1; + status = dsim_read(dsim, DSIM_STATUS); + if (!(status & STATUS_STOPSTATECLK)) { + printf("clock is not in stop state\n"); + return -EBUSY; + } + + stop = STATUS_GET_STOPSTATEDAT(status); + if ((stop & data_lanes_en) != data_lanes_en) { + printf("one or more data lanes is not in stop state\n"); + return -EBUSY; + } + + return 0; +} + +static void sec_mipi_dsim_set_main_mode(struct sec_mipi_dsim *dsim) +{ + uint32_t bpp, hfp_wc, hbp_wc, hsa_wc, wc; + uint32_t mdresol = 0, mvporch = 0, mhporch = 0, msync = 0; + struct display_timing *timings = &dsim->timings; + + mdresol |= MDRESOL_SET_MAINVRESOL(timings->vactive.typ) | + MDRESOL_SET_MAINHRESOL(timings->hactive.typ); + dsim_write(dsim, mdresol, DSIM_MDRESOL); + + mvporch |= MVPORCH_SET_MAINVBP(timings->vback_porch.typ) | + MVPORCH_SET_STABLEVFP(timings->vfront_porch.typ) | + MVPORCH_SET_CMDALLOW(0x0); + dsim_write(dsim, mvporch, DSIM_MVPORCH); + + bpp = mipi_dsi_pixel_format_to_bpp(dsim->format); + + + wc = DIV_ROUND_UP(timings->hfront_porch.typ* (bpp >> 3), + dsim->lanes); + hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ? + wc - MIPI_HFP_PKT_OVERHEAD : timings->hfront_porch.typ; + wc = DIV_ROUND_UP(timings->hback_porch.typ * (bpp >> 3), + dsim->lanes); + hbp_wc = wc > MIPI_HBP_PKT_OVERHEAD ? + wc - MIPI_HBP_PKT_OVERHEAD : timings->hback_porch.typ; + + mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) | + MHPORCH_SET_MAINHBP(hbp_wc); + + dsim_write(dsim, mhporch, DSIM_MHPORCH); + + wc = DIV_ROUND_UP(timings->hsync_len.typ * (bpp >> 3), + dsim->lanes); + hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ? + wc - MIPI_HSA_PKT_OVERHEAD : timings->hsync_len.typ; + + msync |= MSYNC_SET_MAINVSA(timings->vsync_len.typ) | + MSYNC_SET_MAINHSA(hsa_wc); + + debug("hfp_wc %u hbp_wc %u hsa_wc %u\n", hfp_wc, hbp_wc, hsa_wc); + + dsim_write(dsim, msync, DSIM_MSYNC); +} + +static void sec_mipi_dsim_config_dpi(struct sec_mipi_dsim *dsim) +{ + uint32_t config = 0, rgb_status = 0, data_lanes_en; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) + rgb_status &= ~RGB_STATUS_CMDMODE_INSEL; + else + rgb_status |= RGB_STATUS_CMDMODE_INSEL; + + dsim_write(dsim, rgb_status, DSIM_RGB_STATUS); + + if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) + config |= CONFIG_CLKLANE_STOP_START; + + if (dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH) + config |= CONFIG_MFLUSH_VS; + + /* disable EoT packets in HS mode */ + if (dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET) + config |= CONFIG_EOT_R03; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { + config |= CONFIG_VIDEOMODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) + config |= CONFIG_BURSTMODE; + + else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) + config |= CONFIG_SYNCINFORM; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) + config |= CONFIG_AUTOMODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) + config |= CONFIG_HSEDISABLEMODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP) + config |= CONFIG_HFPDISABLEMODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP) + config |= CONFIG_HBPDISABLEMODE; + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA) + config |= CONFIG_HSADISABLEMODE; + } + + config |= CONFIG_SET_MAINVC(dsim->channel); + + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { + switch (dsim->format) { + case MIPI_DSI_FMT_RGB565: + config |= CONFIG_SET_MAINPIXFORMAT(0x4); + break; + case MIPI_DSI_FMT_RGB666_PACKED: + config |= CONFIG_SET_MAINPIXFORMAT(0x5); + break; + case MIPI_DSI_FMT_RGB666: + config |= CONFIG_SET_MAINPIXFORMAT(0x6); + break; + case MIPI_DSI_FMT_RGB888: + config |= CONFIG_SET_MAINPIXFORMAT(0x7); + break; + default: + config |= CONFIG_SET_MAINPIXFORMAT(0x7); + break; + } + } + + /* config data lanes number and enable lanes */ + data_lanes_en = (0x1 << dsim->lanes) - 1; + config |= CONFIG_SET_NUMOFDATLANE(dsim->lanes - 1); + config |= CONFIG_SET_LANEEN(0x1 | data_lanes_en << 1); + + debug("DSIM config 0x%x\n", config); + + dsim_write(dsim, config, DSIM_CONFIG); +} + +static void sec_mipi_dsim_config_cmd_lpm(struct sec_mipi_dsim *dsim, + bool enable) +{ + uint32_t escmode; + + escmode = dsim_read(dsim, DSIM_ESCMODE); + + if (enable) + escmode |= ESCMODE_CMDLPDT; + else + escmode &= ~ESCMODE_CMDLPDT; + + dsim_write(dsim, escmode, DSIM_ESCMODE); +} + +static void sec_mipi_dsim_config_dphy(struct sec_mipi_dsim *dsim) +{ + uint32_t phytiming = 0, phytiming1 = 0, phytiming2 = 0, timeout = 0; + + /* TODO: add a PHY timing table arranged by the pll Fout */ + + phytiming |= PHYTIMING_SET_M_TLPXCTL(6) | + PHYTIMING_SET_M_THSEXITCTL(11); + dsim_write(dsim, phytiming, DSIM_PHYTIMING); + + phytiming1 |= PHYTIMING1_SET_M_TCLKPRPRCTL(7) | + PHYTIMING1_SET_M_TCLKZEROCTL(38) | + PHYTIMING1_SET_M_TCLKPOSTCTL(13) | + PHYTIMING1_SET_M_TCLKTRAILCTL(8); + dsim_write(dsim, phytiming1, DSIM_PHYTIMING1); + + phytiming2 |= PHYTIMING2_SET_M_THSPRPRCTL(8) | + PHYTIMING2_SET_M_THSZEROCTL(13) | + PHYTIMING2_SET_M_THSTRAILCTL(11); + dsim_write(dsim, phytiming2, DSIM_PHYTIMING2); + + timeout |= TIMEOUT_SET_BTAOUT(0xf) | + TIMEOUT_SET_LPDRTOUT(0xf); + dsim_write(dsim, 0xf000f, DSIM_TIMEOUT); +} + +static void sec_mipi_dsim_write_pl_to_sfr_fifo(struct sec_mipi_dsim *dsim, + const void *payload, + size_t length) +{ + uint32_t pl_data; + + if (!length) + return; + + while (length >= 4) { + pl_data = get_unaligned_le32(payload); + dsim_write(dsim, pl_data, DSIM_PAYLOAD); + payload += 4; + length -= 4; + } + + pl_data = 0; + switch (length) { + case 3: + pl_data |= ((u8 *)payload)[2] << 16; + case 2: + pl_data |= ((u8 *)payload)[1] << 8; + case 1: + pl_data |= ((u8 *)payload)[0]; + dsim_write(dsim, pl_data, DSIM_PAYLOAD); + break; + } +} + +static void sec_mipi_dsim_write_ph_to_sfr_fifo(struct sec_mipi_dsim *dsim, + void *header, + bool use_lpm) +{ + uint32_t pkthdr; + + pkthdr = PKTHDR_SET_DATA1(((u8 *)header)[2]) | /* WC MSB */ + PKTHDR_SET_DATA0(((u8 *)header)[1]) | /* WC LSB */ + PKTHDR_SET_DI(((u8 *)header)[0]); /* Data ID */ + + dsim_write(dsim, pkthdr, DSIM_PKTHDR); +} + +static int sec_mipi_dsim_read_pl_from_sfr_fifo(struct sec_mipi_dsim *dsim, + void *payload, + size_t length) +{ + uint8_t data_type; + uint16_t word_count = 0; + uint32_t fifoctrl, ph, pl; + + fifoctrl = dsim_read(dsim, DSIM_FIFOCTRL); + + if (WARN_ON(fifoctrl & FIFOCTRL_EMPTYRX)) + return -EINVAL; + + ph = dsim_read(dsim, DSIM_RXFIFO); + data_type = PKTHDR_GET_DT(ph); + switch (data_type) { + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: + dev_err(dsim->device->dev, "peripheral report error: (0-7)%x, (8-15)%x\n", + PKTHDR_GET_DATA0(ph), PKTHDR_GET_DATA1(ph)); + return -EPROTO; + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: + if (!WARN_ON(length < 2)) { + ((u8 *)payload)[1] = PKTHDR_GET_DATA1(ph); + word_count++; + } + /* fall through */ + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: + ((u8 *)payload)[0] = PKTHDR_GET_DATA0(ph); + word_count++; + length = word_count; + break; + case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: + case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: + word_count = PKTHDR_GET_WC(ph); + if (word_count > length) { + dev_err(dsim->device->dev, "invalid receive buffer length\n"); + return -EINVAL; + } + + length = word_count; + + while (word_count >= 4) { + pl = dsim_read(dsim, DSIM_RXFIFO); + ((u8 *)payload)[0] = pl & 0xff; + ((u8 *)payload)[1] = (pl >> 8) & 0xff; + ((u8 *)payload)[2] = (pl >> 16) & 0xff; + ((u8 *)payload)[3] = (pl >> 24) & 0xff; + payload += 4; + word_count -= 4; + } + + if (word_count > 0) { + pl = dsim_read(dsim, DSIM_RXFIFO); + + switch (word_count) { + case 3: + ((u8 *)payload)[2] = (pl >> 16) & 0xff; + case 2: + ((u8 *)payload)[1] = (pl >> 8) & 0xff; + case 1: + ((u8 *)payload)[0] = pl & 0xff; + break; + } + } + + break; + default: + return -EINVAL; + } + + return length; +} + +static void sec_mipi_dsim_init_fifo_pointers(struct sec_mipi_dsim *dsim) +{ + uint32_t fifoctrl, fifo_ptrs; + + fifoctrl = dsim_read(dsim, DSIM_FIFOCTRL); + + fifo_ptrs = FIFOCTRL_NINITRX | + FIFOCTRL_NINITSFR | + FIFOCTRL_NINITI80 | + FIFOCTRL_NINITSUB | + FIFOCTRL_NINITMAIN; + + fifoctrl &= ~fifo_ptrs; + dsim_write(dsim, fifoctrl, DSIM_FIFOCTRL); + udelay(500); + + fifoctrl |= fifo_ptrs; + dsim_write(dsim, fifoctrl, DSIM_FIFOCTRL); + udelay(500); +} + + +static void sec_mipi_dsim_config_clkctrl(struct sec_mipi_dsim *dsim) +{ + uint32_t clkctrl = 0, data_lanes_en; + uint64_t byte_clk, esc_prescaler; + + clkctrl |= CLKCTRL_TXREQUESTHSCLK; + + /* using 1.5Gbps PHY */ + clkctrl |= CLKCTRL_DPHY_SEL_1P5G; + + clkctrl |= CLKCTRL_ESCCLKEN; + + clkctrl &= ~CLKCTRL_PLLBYPASS; + + clkctrl |= CLKCTRL_BYTECLKSRC_DPHY_PLL; + + clkctrl |= CLKCTRL_BYTECLKEN; + + data_lanes_en = (0x1 << dsim->lanes) - 1; + clkctrl |= CLKCTRL_SET_LANEESCCLKEN(0x1 | data_lanes_en << 1); + + /* calculate esc prescaler from byte clock: + * EscClk = ByteClk / EscPrescaler; + */ + byte_clk = dsim->bit_clk >> 3; + esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ); + + clkctrl |= CLKCTRL_SET_ESCPRESCALER(esc_prescaler); + + debug("DSIM clkctrl 0x%x\n", clkctrl); + + dsim_write(dsim, clkctrl, DSIM_CLKCTRL); +} + +static void sec_mipi_dsim_set_standby(struct sec_mipi_dsim *dsim, + bool standby) +{ + uint32_t mdresol = 0; + + mdresol = dsim_read(dsim, DSIM_MDRESOL); + + if (standby) + mdresol |= MDRESOL_MAINSTANDBY; + else + mdresol &= ~MDRESOL_MAINSTANDBY; + + dsim_write(dsim, mdresol, DSIM_MDRESOL); +} + +static void sec_mipi_dsim_disable_clkctrl(struct sec_mipi_dsim *dsim) +{ + uint32_t clkctrl; + + clkctrl = dsim_read(dsim, DSIM_CLKCTRL); + + clkctrl &= ~CLKCTRL_TXREQUESTHSCLK; + + clkctrl &= ~CLKCTRL_ESCCLKEN; + + clkctrl &= ~CLKCTRL_BYTECLKEN; + + dsim_write(dsim, clkctrl, DSIM_CLKCTRL); +} + +static void sec_mipi_dsim_disable_pll(struct sec_mipi_dsim *dsim) +{ + uint32_t pllctrl; + + pllctrl = dsim_read(dsim, DSIM_PLLCTRL); + + pllctrl &= ~PLLCTRL_PLLEN; + + dsim_write(dsim, pllctrl, DSIM_PLLCTRL); +} + +static inline struct sec_mipi_dsim *host_to_dsi(struct mipi_dsi_host *host) +{ + return container_of(host, struct sec_mipi_dsim, dsi_host); +} + +static int sec_mipi_dsim_bridge_clk_set(struct sec_mipi_dsim *dsim_host) +{ + int bpp; + uint64_t pix_clk, bit_clk; + + bpp = mipi_dsi_pixel_format_to_bpp(dsim_host->format); + if (bpp < 0) + return -EINVAL; + + pix_clk = dsim_host->timings.pixelclock.typ; + bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim_host->lanes); + +#if 0 + if (bit_clk > dsim_host->max_data_rate) { + printf("request bit clk freq exceeds lane's maximum value\n"); + return -EINVAL; + } +#endif + + dsim_host->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000); + dsim_host->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000); + + if (dsim_host->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { + /* TODO: add PMS calculate and check + * Only support '1080p@60Hz' for now, + * add other modes support later + */ + dsim_host->pms = 0x4210; + } + + debug("%s: bitclk %llu pixclk %llu\n", __func__, dsim_host->bit_clk, dsim_host->pix_clk); + + return 0; +} + +static int sec_mipi_dsim_bridge_prepare(struct sec_mipi_dsim *dsim_host) +{ + int ret; + + /* At this moment, the dsim bridge's preceding encoder has + * already been enabled. So the dsim can be configed here + */ + + /* config main display mode */ + sec_mipi_dsim_set_main_mode(dsim_host); + + /* config dsim dpi */ + sec_mipi_dsim_config_dpi(dsim_host); + + /* config dsim pll */ + ret = sec_mipi_dsim_config_pll(dsim_host); + if (ret) { + printf("dsim pll config failed: %d\n", ret); + return ret; + } + + /* config dphy timings */ + sec_mipi_dsim_config_dphy(dsim_host); + + sec_mipi_dsim_init_fifo_pointers(dsim_host); + + /* config esc clock, byte clock and etc */ + sec_mipi_dsim_config_clkctrl(dsim_host); + + /* enable data transfer of dsim */ + sec_mipi_dsim_set_standby(dsim_host, true); + + return 0; +} + +static int sec_mipi_dsim_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct sec_mipi_dsim *dsi = host_to_dsi(host); + + if (!device->lanes || device->lanes > dsi->max_data_lanes) { + printf("invalid data lanes number\n"); + return -EINVAL; + } + + if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO) || + !((device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) || + (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) { + printf("unsupported dsi mode\n"); + return -EINVAL; + } + + if (device->format != MIPI_DSI_FMT_RGB888 && + device->format != MIPI_DSI_FMT_RGB565 && + device->format != MIPI_DSI_FMT_RGB666 && + device->format != MIPI_DSI_FMT_RGB666_PACKED) { + printf("unsupported pixel format: %#x\n", device->format); + return -EINVAL; + } + + dsi->lanes = device->lanes; + dsi->channel = device->channel; + dsi->format = device->format; + dsi->mode_flags = device->mode_flags; + + debug("lanes %u, channel %u, format 0x%x, mode_flags 0x%lx\n", dsi->lanes, + dsi->channel, dsi->format, dsi->mode_flags); + + sec_mipi_dsim_bridge_clk_set(dsi); + sec_mipi_dsim_bridge_prepare(dsi); + + return 0; +} + +static ssize_t sec_mipi_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct sec_mipi_dsim *dsim = host_to_dsi(host); + int ret, nb_bytes; + bool use_lpm; + struct mipi_dsi_packet packet; + +#ifdef DEBUG + int i = 0; + u8 *p = msg->tx_buf; + + printf("sec_mipi_dsi_host_transfer\n"); + for (i; i < msg->tx_len; i++) { + printf("0x%.2x ", *(u8 *)p); + p++; + } + printf("\n"); +#endif + + ret = mipi_dsi_create_packet(&packet, msg); + if (ret) { + dev_err(dsim->device->dev, "failed to create dsi packet: %d\n", ret); + return ret; + } + + /* config LPM for CMD TX */ + use_lpm = msg->flags & MIPI_DSI_MSG_USE_LPM ? true : false; + sec_mipi_dsim_config_cmd_lpm(dsim, use_lpm); + + if (packet.payload_length) { /* Long Packet case */ + /* write packet payload */ + sec_mipi_dsim_write_pl_to_sfr_fifo(dsim, + packet.payload, + packet.payload_length); + + /* write packet header */ + sec_mipi_dsim_write_ph_to_sfr_fifo(dsim, + packet.header, + use_lpm); + + ret = sec_mipi_dsim_wait_for_pkt_done(dsim, MIPI_FIFO_TIMEOUT); + if (ret) { + dev_err(dsim->device->dev, "wait tx done timeout!\n"); + return -EBUSY; + } + } else { + /* write packet header */ + sec_mipi_dsim_write_ph_to_sfr_fifo(dsim, + packet.header, + use_lpm); + + ret = sec_mipi_dsim_wait_for_hdr_done(dsim, MIPI_FIFO_TIMEOUT); + if (ret) { + dev_err(dsim->device->dev, "wait pkthdr tx done time out\n"); + return -EBUSY; + } + } + + /* read packet payload */ + if (unlikely(msg->rx_buf)) { + ret = sec_mipi_dsim_wait_for_rx_done(dsim, + MIPI_FIFO_TIMEOUT); + if (ret) { + dev_err(dsim->device->dev, "wait rx done time out\n"); + return -EBUSY; + } + + ret = sec_mipi_dsim_read_pl_from_sfr_fifo(dsim, + msg->rx_buf, + msg->rx_len); + if (ret < 0) + return ret; + nb_bytes = msg->rx_len; + } else { + nb_bytes = packet.size; + } + + return nb_bytes; + +} + + +static const struct mipi_dsi_host_ops sec_mipi_dsim_host_ops = { + .attach = sec_mipi_dsim_host_attach, + .transfer = sec_mipi_dsi_host_transfer, +}; + +static int sec_mipi_dsim_init(struct udevice *dev, + struct mipi_dsi_device *device, + struct display_timing *timings, + unsigned int max_data_lanes, + const struct mipi_dsi_phy_ops *phy_ops) +{ + struct sec_mipi_dsim *dsi = dev_get_priv(dev); + + dsi->max_data_lanes = max_data_lanes; + dsi->device = device; + dsi->dsi_host.ops = &sec_mipi_dsim_host_ops; + device->host = &dsi->dsi_host; + + dsi->base = (void *)dev_read_addr(device->dev); + if ((fdt_addr_t)dsi->base == FDT_ADDR_T_NONE) { + dev_err(device->dev, "dsi dt register address error\n"); + return -EINVAL; + } + + dsi->timings = *timings; + + return 0; +} + +static int sec_mipi_dsim_enable(struct udevice *dev) +{ + return 0; +} + +static int sec_mipi_dsim_disable(struct udevice *dev) +{ + uint32_t intsrc; + struct sec_mipi_dsim *dsim_host = dev_get_priv(dev); + + /* disable data transfer of dsim */ + sec_mipi_dsim_set_standby(dsim_host, false); + + /* disable esc clock & byte clock */ + sec_mipi_dsim_disable_clkctrl(dsim_host); + + /* disable dsim pll */ + sec_mipi_dsim_disable_pll(dsim_host); + + /* Clear all intsrc */ + intsrc = dsim_read(dsim_host, DSIM_INTSRC); + dsim_write(dsim_host, intsrc, DSIM_INTSRC); + + return 0; +} + +struct dsi_host_ops sec_mipi_dsim_ops = { + .init = sec_mipi_dsim_init, + .enable = sec_mipi_dsim_enable, + .disable = sec_mipi_dsim_disable, +}; + +static int sec_mipi_dsim_probe(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id sec_mipi_dsim_ids[] = { + { .compatible = "samsung,sec-mipi-dsi" }, + { } +}; + +U_BOOT_DRIVER(sec_mipi_dsim) = { + .name = "sec_mipi_dsim", + .id = UCLASS_DSI_HOST, + .of_match = sec_mipi_dsim_ids, + .probe = sec_mipi_dsim_probe, + .remove = sec_mipi_dsim_disable, + .ops = &sec_mipi_dsim_ops, + .priv_auto = sizeof(struct sec_mipi_dsim), +}; |